ngxsmk-datepicker
Version:
Lightweight, accessible date and range picker for Angular 17+. Standalone component, Signals, SSR and zoneless-ready, Luxon-based i18n and timezones.
2,830 lines • 128 kB
TypeScript
import * as i0 from '@angular/core';
import { EventEmitter, ElementRef, AfterViewInit, OnDestroy, TemplateRef, InjectionToken, EffectRef, Signal, OnInit, OnChanges, SimpleChanges } from '@angular/core';
import { ControlValueAccessor, NgControl, ValidatorFn } from '@angular/forms';
import { Subject, Observable } from 'rxjs';
declare function getStartOfDay(d: Date): Date;
declare function getEndOfDay(d: Date): Date;
declare function addMonths(d: Date, months: number): Date;
declare function subtractDays(d: Date, days: number): Date;
declare function getStartOfMonth(d: Date): Date;
declare function getEndOfMonth(d: Date): Date;
/**
* Returns the ISO 8601 week number (1-53) for the given date.
*
* ISO weeks start on Monday; week 1 is the week containing the year's first
* Thursday, so early January can belong to week 52/53 of the previous year
* and late December to week 1 of the next.
*/
declare function getISOWeekNumber(d: Date): number;
declare function isSameDay(d1: Date | null, d2: Date | null): boolean;
declare function normalizeDate(date: DateInput | null): Date | null;
type DateInput = Date | string | {
toDate: () => Date;
_isAMomentObject?: boolean;
$d?: Date;
};
interface HolidayProvider {
isHoliday(date: Date): boolean;
getHolidayLabel?(date: Date): string | null;
}
interface DateRange {
[key: string]: [DateInput, DateInput];
}
type DatepickerValue = Date | {
start: Date | null;
end: Date | null;
} | Date[] | null;
declare function generateMonthOptions(locale: string, year: number): {
label: string;
value: number;
}[];
/**
* Format a number using locale-aware number formatting.
* Uses Intl.NumberFormat for proper localization of numeric separators and decimals.
*
* @param value - The number to format
* @param locale - The locale to use for formatting (e.g., 'en-US', 'de-DE', 'fr-FR')
* @param options - Optional Intl.NumberFormatOptions for customization
* @returns Formatted number string
*/
declare function formatLocaleNumber(value: number, locale: string, options?: Intl.NumberFormatOptions): string;
declare function generateYearOptions(currentYear: number, range?: number): {
label: string;
value: number;
}[];
declare function generateTimeOptions(minuteInterval?: number, secondInterval?: number, includeSeconds?: boolean, use24Hour?: boolean): {
hourOptions: {
label: string;
value: number;
}[];
minuteOptions: {
label: string;
value: number;
}[];
secondOptions?: {
label: string;
value: number;
}[];
};
declare function generateWeekDays(locale: string, firstDayOfWeek?: number): string[];
declare function getFirstDayOfWeek(locale: string): number;
declare function get24Hour(displayHour: number, isPm: boolean): number;
declare function update12HourState(fullHour: number): {
isPm: boolean;
displayHour: number;
};
declare function processDateRanges(ranges: DateRange | null): {
[key: string]: [Date, Date];
} | null;
declare function generateYearGrid(currentYear: number): number[];
declare function generateDecadeGrid(currentDecade: number): number[];
interface DatepickerClasses {
wrapper?: string;
inputGroup?: string;
input?: string;
clearBtn?: string;
calendarBtn?: string;
popover?: string;
container?: string;
calendar?: string;
header?: string;
navPrev?: string;
navNext?: string;
dayCell?: string;
footer?: string;
closeBtn?: string;
}
declare class NgxsmkDatepickerInputComponent {
isNative: boolean;
disabled: boolean;
classes: DatepickerClasses | undefined;
nativeInputType: string;
formattedValue: string;
placeholder: string;
id: string;
name: string;
autocomplete: string;
required: boolean;
minDateNative: string | null;
maxDateNative: string | null;
ariaLabel: string;
ariaDescribedBy: string;
errorState: boolean;
clearAriaLabel: string;
clearLabel: string;
isCalendarOpen: boolean;
allowTyping: boolean;
typedInputValue: string;
displayValue: string;
showCalendarButton: boolean;
calendarAriaLabel: string;
validationErrorMessage: string | null;
nativeInputChange: EventEmitter<Event>;
inputBlur: EventEmitter<FocusEvent>;
clearValue: EventEmitter<MouseEvent>;
toggleCalendar: EventEmitter<Event>;
pointerDown: EventEmitter<PointerEvent>;
pointerUp: EventEmitter<PointerEvent>;
inputGroupFocus: EventEmitter<void>;
inputKeyDown: EventEmitter<Event>;
inputChange: EventEmitter<Event>;
inputFocus: EventEmitter<FocusEvent>;
nativeInput?: ElementRef<HTMLInputElement>;
customInput?: ElementRef<HTMLInputElement>;
focus(): void;
onNativeInputChange(event: Event): void;
onInputBlur(event: FocusEvent): void;
onClearValue(event: MouseEvent): void;
onToggleCalendar(event: Event): void;
onPointerDown(event: PointerEvent): void;
onPointerUp(event: PointerEvent): void;
onInputGroupFocus(): void;
onInputKeyDown(event: Event): void;
onInputChange(event: Event): void;
onInputFocus(event: FocusEvent): void;
static ɵfac: i0.ɵɵFactoryDeclaration<NgxsmkDatepickerInputComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<NgxsmkDatepickerInputComponent, "ngxsmk-datepicker-input", never, { "isNative": { "alias": "isNative"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "classes": { "alias": "classes"; "required": false; }; "nativeInputType": { "alias": "nativeInputType"; "required": false; }; "formattedValue": { "alias": "formattedValue"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "id": { "alias": "id"; "required": false; }; "name": { "alias": "name"; "required": false; }; "autocomplete": { "alias": "autocomplete"; "required": false; }; "required": { "alias": "required"; "required": false; }; "minDateNative": { "alias": "minDateNative"; "required": false; }; "maxDateNative": { "alias": "maxDateNative"; "required": false; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; }; "ariaDescribedBy": { "alias": "ariaDescribedBy"; "required": false; }; "errorState": { "alias": "errorState"; "required": false; }; "clearAriaLabel": { "alias": "clearAriaLabel"; "required": false; }; "clearLabel": { "alias": "clearLabel"; "required": false; }; "isCalendarOpen": { "alias": "isCalendarOpen"; "required": false; }; "allowTyping": { "alias": "allowTyping"; "required": false; }; "typedInputValue": { "alias": "typedInputValue"; "required": false; }; "displayValue": { "alias": "displayValue"; "required": false; }; "showCalendarButton": { "alias": "showCalendarButton"; "required": false; }; "calendarAriaLabel": { "alias": "calendarAriaLabel"; "required": false; }; "validationErrorMessage": { "alias": "validationErrorMessage"; "required": false; }; }, { "nativeInputChange": "nativeInputChange"; "inputBlur": "inputBlur"; "clearValue": "clearValue"; "toggleCalendar": "toggleCalendar"; "pointerDown": "pointerDown"; "pointerUp": "pointerUp"; "inputGroupFocus": "inputGroupFocus"; "inputKeyDown": "inputKeyDown"; "inputChange": "inputChange"; "inputFocus": "inputFocus"; }, never, never, true, never>;
}
declare class CustomSelectComponent implements AfterViewInit, OnDestroy {
options: {
label: string;
value: unknown;
disabled?: boolean;
}[];
value: unknown;
disabled: boolean;
valueChange: EventEmitter<unknown>;
container: ElementRef<HTMLDivElement>;
button: ElementRef<HTMLButtonElement>;
panel: ElementRef<HTMLDivElement>;
isOpen: boolean;
private readonly elementRef;
private readonly platformId;
private readonly document;
private readonly isBrowser;
private resizeObserver;
private scrollListener;
ngAfterViewInit(): void;
ngOnDestroy(): void;
private setupResizeObserver;
private setupScrollListener;
private updatePanelPosition;
onDocumentClick(event: MouseEvent | TouchEvent): void;
onDocumentTouchStart(event: TouchEvent): void;
get displayValue(): string;
toggleDropdown(): void;
private scrollToSelected;
selectOption(option: {
label: string;
value: unknown;
}): void;
static ɵfac: i0.ɵɵFactoryDeclaration<CustomSelectComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<CustomSelectComponent, "ngxsmk-custom-select", never, { "options": { "alias": "options"; "required": false; }; "value": { "alias": "value"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
}
declare class CalendarHeaderComponent {
monthSelect?: CustomSelectComponent;
yearSelect?: CustomSelectComponent;
monthOptions: {
label: string;
value: number;
}[];
yearOptions: {
label: string;
value: number;
}[];
currentMonth: number;
currentYear: number;
disabled: boolean;
isBackArrowDisabled: boolean;
prevMonthAriaLabel: string;
nextMonthAriaLabel: string;
headerClass?: string;
navPrevClass?: string;
navNextClass?: string;
currentYearChange: EventEmitter<number>;
currentMonthChange: EventEmitter<number>;
previousMonth: EventEmitter<void>;
nextMonth: EventEmitter<void>;
onMonthSelect(value: unknown): void;
onYearSelect(value: unknown): void;
static ɵfac: i0.ɵɵFactoryDeclaration<CalendarHeaderComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<CalendarHeaderComponent, "ngxsmk-calendar-header", never, { "monthOptions": { "alias": "monthOptions"; "required": false; }; "yearOptions": { "alias": "yearOptions"; "required": false; }; "currentMonth": { "alias": "currentMonth"; "required": false; }; "currentYear": { "alias": "currentYear"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "isBackArrowDisabled": { "alias": "isBackArrowDisabled"; "required": false; }; "prevMonthAriaLabel": { "alias": "prevMonthAriaLabel"; "required": false; }; "nextMonthAriaLabel": { "alias": "nextMonthAriaLabel"; "required": false; }; "headerClass": { "alias": "headerClass"; "required": false; }; "navPrevClass": { "alias": "navPrevClass"; "required": false; }; "navNextClass": { "alias": "navNextClass"; "required": false; }; }, { "currentYearChange": "currentYearChange"; "currentMonthChange": "currentMonthChange"; "previousMonth": "previousMonth"; "nextMonth": "nextMonth"; }, never, never, true, never>;
}
/**
* Complete translations interface for the datepicker component
*/
interface DatepickerTranslations {
selectDate: string;
selectTime: string;
clear: string;
close: string;
today: string;
selectEndDate: string;
day: string;
days: string;
previousMonth: string;
nextMonth: string;
previousYear: string;
nextYear: string;
previousYears: string;
nextYears: string;
previousDecade: string;
nextDecade: string;
clearSelection: string;
closeCalendar: string;
closeCalendarOverlay: string;
calendarFor: string;
selectYear: string;
selectDecade: string;
datesSelected: string;
timesSelected: string;
time: string;
startTime: string;
endTime: string;
from: string;
to: string;
holiday: string;
month: string;
year: string;
decade: string;
timeline: string;
timeSlider: string;
calendarOpened: string;
calendarClosed: string;
dateSelected: string;
startDateSelected?: string;
rangeSelected: string;
monthChanged: string;
yearChanged: string;
calendarLoading: string;
calendarReady: string;
keyboardShortcuts: string;
invalidDateFormat: string;
dateBeforeMin: string;
dateAfterMax: string;
invalidDate: string;
timezone?: string;
}
/**
* Partial translations - allows overriding only specific keys
*/
type PartialDatepickerTranslations = Partial<DatepickerTranslations>;
/**
* Calendar system types supported by the datepicker
*/
type CalendarSystem = 'gregorian' | 'islamic' | 'buddhist' | 'japanese' | 'hebrew' | 'persian';
/**
* Locale data structure for datepicker localization
*/
interface LocaleData {
/** Calendar system used by this locale */
calendar: CalendarSystem;
/** First day of the week (0 = Sunday, 1 = Monday, etc.) */
firstDayOfWeek: number;
/** Default date format string (e.g., 'MM/DD/YYYY', 'DD/MM/YYYY') */
dateFormat: string;
/** Full month names (12 elements) */
monthNames: string[];
/** Short month names (12 elements) */
monthNamesShort: string[];
/** Full weekday names (7 elements, starting with Sunday) */
weekdayNames: string[];
/** Short weekday names (7 elements, starting with Sunday) */
weekdayNamesShort: string[];
/** Whether this locale uses RTL (right-to-left) text direction */
isRtl: boolean;
/** Fallback locale if this one is not fully supported */
fallbackLocale?: string;
/** Locale-specific date format options */
dateFormatOptions?: {
year?: 'numeric' | '2-digit';
month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow';
day?: 'numeric' | '2-digit';
hour?: 'numeric' | '2-digit';
minute?: 'numeric' | '2-digit';
hour12?: boolean;
};
}
/**
* Service for managing locale data and providing fallback mechanisms
* Supports multiple calendar systems and provides locale-specific formatting
*/
declare class LocaleRegistryService {
private localeData;
private readonly defaultLocale;
constructor();
/**
* Register locale data for a specific locale
*/
register(locale: string, data: LocaleData): void;
/**
* Get locale data for a specific locale, with fallback support
*/
getLocaleData(locale: string): LocaleData;
/**
* Get fallback locale for an unsupported locale
*/
getFallbackLocale(unsupportedLocale: string): string | null;
/**
* Check if a locale is RTL
*/
isRtlLocale(locale: string): boolean;
/**
* Get calendar system for a locale
*/
getCalendarSystem(locale: string): CalendarSystem;
/**
* Register default locale data for common locales
*/
private registerDefaultLocales;
/**
* Get default locale data (English US)
*/
private getDefaultLocaleData;
static ɵfac: i0.ɵɵFactoryDeclaration<LocaleRegistryService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<LocaleRegistryService>;
}
/**
* Per-day decoration metadata for calendar cells — the building block for
* availability/pricing calendars (booking-site style) without custom templates.
*/
interface DayMetadata {
/** Small text rendered under the day number (e.g. a price like "$120"). */
label?: string;
/** Color of a small indicator dot rendered in the cell (any CSS color). */
indicatorColor?: string;
/** Extra CSS class(es) applied to the day cell element. */
cssClass?: string | string[];
/** Tooltip (title attribute) for the cell. A `getDayCellTooltip` hook takes precedence. */
tooltip?: string;
}
/**
* Resolves metadata for a calendar day. Called during rendering, so it should
* be fast — precompute or memoize lookups rather than fetching per call.
* Return null/undefined for days without decorations.
*/
type DayMetadataProvider = (date: Date) => DayMetadata | null | undefined;
declare class NgxsmkDatepickerContentComponent {
isCalendarVisible: boolean;
isCalendarOpen: boolean;
isInlineMode: boolean;
shouldAppendToBody: boolean;
theme: string;
popoverId: string;
classes: DatepickerClasses | undefined;
timeOnly: boolean;
showTime: boolean;
isMobile: boolean;
mobileModalStyle: string;
align: string;
ariaLabel: string;
isCalendarOpening: boolean;
loadingMessage: string;
showRanges: boolean;
rangesArray: {
key: string;
value: [Date, Date];
}[];
mode: 'single' | 'range' | 'multiple' | 'week' | 'month' | 'quarter' | 'year' | 'timeRange';
disabled: boolean;
calendarCount: number;
calendarLayout: string;
syncScrollEnabled: boolean;
calendarMonths: {
month: number;
year: number;
days: (Date | null)[];
}[];
weekDays: string[];
weekDaysFull: string[];
showOtherMonths: boolean;
showWeekNumbers: boolean;
weekNumberLabel: string;
secondaryCalendar: CalendarSystem | null;
secondaryCalendarLocale: string;
selectedDate: Date | null;
startDate: Date | null;
endDate: Date | null;
focusedDate: Date | null;
today: Date;
dateTemplate: TemplateRef<unknown> | null;
calendarViewMode: string;
monthOptions: {
label: string;
value: number;
}[];
currentMonth: number;
yearOptions: {
label: string;
value: number;
}[];
currentYear: number;
isBackArrowDisabled: boolean;
prevMonthAriaLabel: string;
nextMonthAriaLabel: string;
yearGrid: number[];
currentDecade: number;
decadeGrid: number[];
timelineStartDate: Date | null;
timelineEndDate: Date | null;
timelineMonths: Date[];
minuteInterval: number;
startTimeSlider: number;
endTimeSlider: number;
timeRangeMode: boolean;
hourOptions: {
label: string;
value: number;
}[];
minuteOptions: {
label: string;
value: number;
}[];
secondOptions: {
label: string;
value: number;
}[];
ampmOptions: {
label: string;
value: boolean;
}[];
currentDisplayHour: number;
currentMinute: number;
currentSecond: number;
isPm: boolean;
showSeconds: boolean;
use24Hour: boolean;
startDisplayHour: number;
startMinute: number;
startSecond: number;
startIsPm: boolean;
endDisplayHour: number;
endMinute: number;
endSecond: number;
endIsPm: boolean;
clearAriaLabel: string;
clearLabel: string;
closeAriaLabel: string;
closeLabel: string;
translations: DatepickerTranslations | null;
selectedRange: [Date | null, Date | null] | null;
showTimezoneSelector: boolean;
timezoneOptions: {
label: string;
value: string;
}[];
currentTimezone: string;
timezoneChange: EventEmitter<string>;
boundIsDateDisabled: (date: Date | null) => boolean;
boundIsYearDisabled?: ((year: number) => boolean) | undefined;
boundIsDecadeDisabled?: ((decade: number) => boolean) | undefined;
boundGetDayMetadata: (date: Date | null) => DayMetadata | null;
calendarHeaderTemplate: TemplateRef<unknown> | null;
calendarFooterTemplate: TemplateRef<unknown> | null;
/**
* Stable context handed to the header/footer slot templates.
* `$implicit` exposes the popover actions (`clear`, `close`).
*/
readonly slotContext: {
$implicit: {
clear: () => void;
close: () => void;
};
};
boundIsSameDay: (date1: Date | null, date2: Date | null) => boolean;
boundIsHoliday: (date: Date | null) => boolean;
boundIsMultipleSelected: (date: Date | null) => boolean;
boundIsInRange: (date: Date | null) => boolean;
boundIsInComparisonRange: (date: Date | null) => boolean;
boundIsPreviewInRange: (date: Date | null) => boolean;
boundGetAriaLabel: (date: Date | null) => string;
boundGetDayCellCustomClasses: (date: Date | null) => string | string[] | Set<string> | {
[klass: string]: unknown;
};
boundGetDayCellTooltip: (date: Date | null) => string | null;
boundFormatDayNumber: (date: Date | null) => string;
getMonthYearLabel: (month: number, year: number) => string;
getCalendarAriaLabelForMonth: (month: number, year: number) => string;
isTimelineMonthSelected: (date: Date) => boolean;
formatTimeSliderValue: (value: number) => string;
backdropClick: EventEmitter<Event>;
touchStartContainer: EventEmitter<TouchEvent>;
touchMoveContainer: EventEmitter<TouchEvent>;
touchEndContainer: EventEmitter<TouchEvent>;
rangeSelect: EventEmitter<[Date, Date]>;
previousMonth: EventEmitter<void>;
nextMonth: EventEmitter<void>;
currentMonthChange: EventEmitter<number>;
currentYearChange: EventEmitter<number>;
dateClick: EventEmitter<Date>;
dateHover: EventEmitter<Date | null>;
dateFocus: EventEmitter<Date>;
swipeStart: EventEmitter<TouchEvent>;
swipeMove: EventEmitter<TouchEvent>;
swipeEnd: EventEmitter<TouchEvent>;
touchStart: EventEmitter<{
event: TouchEvent;
day: Date | null;
}>;
touchMove: EventEmitter<TouchEvent>;
touchEnd: EventEmitter<{
event: TouchEvent;
day: Date | null;
}>;
viewModeChange: EventEmitter<"year" | "month" | "decade" | "timeline" | "time-slider">;
changeYear: EventEmitter<number>;
yearClick: EventEmitter<number>;
changeDecade: EventEmitter<number>;
decadeClick: EventEmitter<number>;
timelineZoomOut: EventEmitter<void>;
timelineZoomIn: EventEmitter<void>;
timelineMonthClick: EventEmitter<Date>;
startTimeSliderChange: EventEmitter<number>;
endTimeSliderChange: EventEmitter<number>;
currentDisplayHourChange: EventEmitter<number>;
currentMinuteChange: EventEmitter<number>;
currentSecondChange: EventEmitter<number>;
isPmChange: EventEmitter<boolean>;
timeChange: EventEmitter<void>;
startDisplayHourChange: EventEmitter<number>;
startMinuteChange: EventEmitter<number>;
startSecondChange: EventEmitter<number>;
startIsPmChange: EventEmitter<boolean>;
endDisplayHourChange: EventEmitter<number>;
endMinuteChange: EventEmitter<number>;
endSecondChange: EventEmitter<number>;
endIsPmChange: EventEmitter<boolean>;
timeRangeChange: EventEmitter<void>;
clearValue: EventEmitter<MouseEvent>;
closeCalendar: EventEmitter<void>;
escapeKey: EventEmitter<Event>;
containerKeyDown: EventEmitter<KeyboardEvent>;
header?: CalendarHeaderComponent;
popoverContainer?: ElementRef<HTMLElement>;
timelineContainer?: ElementRef<HTMLElement>;
closeAllSelects(): void;
onTimelineMonthClick(month: Date, event: MouseEvent): void;
onTimelineMonthSpace(month: Date, event: Event): void;
onPopoverEscape(event: Event): void;
aiInputElement?: ElementRef<HTMLInputElement>;
enableAi: boolean;
aiPlaceholder: string;
aiSuggestions: string[];
showAiSuggestions: boolean;
isAiResolving: boolean;
aiPromptSubmitted: EventEmitter<string>;
aiPromptText: string;
onAiSubmit(): void;
onAiChipClick(chip: string): void;
focusAiInput(): void;
onTimezoneChange(val: unknown): void;
static ɵfac: i0.ɵɵFactoryDeclaration<NgxsmkDatepickerContentComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<NgxsmkDatepickerContentComponent, "ngxsmk-datepicker-content", never, { "isCalendarVisible": { "alias": "isCalendarVisible"; "required": false; }; "isCalendarOpen": { "alias": "isCalendarOpen"; "required": false; }; "isInlineMode": { "alias": "isInlineMode"; "required": false; }; "shouldAppendToBody": { "alias": "shouldAppendToBody"; "required": false; }; "theme": { "alias": "theme"; "required": false; }; "popoverId": { "alias": "popoverId"; "required": false; }; "classes": { "alias": "classes"; "required": false; }; "timeOnly": { "alias": "timeOnly"; "required": false; }; "showTime": { "alias": "showTime"; "required": false; }; "isMobile": { "alias": "isMobile"; "required": false; }; "mobileModalStyle": { "alias": "mobileModalStyle"; "required": false; }; "align": { "alias": "align"; "required": false; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; }; "isCalendarOpening": { "alias": "isCalendarOpening"; "required": false; }; "loadingMessage": { "alias": "loadingMessage"; "required": false; }; "showRanges": { "alias": "showRanges"; "required": false; }; "rangesArray": { "alias": "rangesArray"; "required": false; }; "mode": { "alias": "mode"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "calendarCount": { "alias": "calendarCount"; "required": false; }; "calendarLayout": { "alias": "calendarLayout"; "required": false; }; "syncScrollEnabled": { "alias": "syncScrollEnabled"; "required": false; }; "calendarMonths": { "alias": "calendarMonths"; "required": false; }; "weekDays": { "alias": "weekDays"; "required": false; }; "weekDaysFull": { "alias": "weekDaysFull"; "required": false; }; "showOtherMonths": { "alias": "showOtherMonths"; "required": false; }; "showWeekNumbers": { "alias": "showWeekNumbers"; "required": false; }; "weekNumberLabel": { "alias": "weekNumberLabel"; "required": false; }; "secondaryCalendar": { "alias": "secondaryCalendar"; "required": false; }; "secondaryCalendarLocale": { "alias": "secondaryCalendarLocale"; "required": false; }; "selectedDate": { "alias": "selectedDate"; "required": false; }; "startDate": { "alias": "startDate"; "required": false; }; "endDate": { "alias": "endDate"; "required": false; }; "focusedDate": { "alias": "focusedDate"; "required": false; }; "today": { "alias": "today"; "required": false; }; "dateTemplate": { "alias": "dateTemplate"; "required": false; }; "calendarViewMode": { "alias": "calendarViewMode"; "required": false; }; "monthOptions": { "alias": "monthOptions"; "required": false; }; "currentMonth": { "alias": "currentMonth"; "required": false; }; "yearOptions": { "alias": "yearOptions"; "required": false; }; "currentYear": { "alias": "currentYear"; "required": false; }; "isBackArrowDisabled": { "alias": "isBackArrowDisabled"; "required": false; }; "prevMonthAriaLabel": { "alias": "prevMonthAriaLabel"; "required": false; }; "nextMonthAriaLabel": { "alias": "nextMonthAriaLabel"; "required": false; }; "yearGrid": { "alias": "yearGrid"; "required": false; }; "currentDecade": { "alias": "currentDecade"; "required": false; }; "decadeGrid": { "alias": "decadeGrid"; "required": false; }; "timelineStartDate": { "alias": "timelineStartDate"; "required": false; }; "timelineEndDate": { "alias": "timelineEndDate"; "required": false; }; "timelineMonths": { "alias": "timelineMonths"; "required": false; }; "minuteInterval": { "alias": "minuteInterval"; "required": false; }; "startTimeSlider": { "alias": "startTimeSlider"; "required": false; }; "endTimeSlider": { "alias": "endTimeSlider"; "required": false; }; "timeRangeMode": { "alias": "timeRangeMode"; "required": false; }; "hourOptions": { "alias": "hourOptions"; "required": false; }; "minuteOptions": { "alias": "minuteOptions"; "required": false; }; "secondOptions": { "alias": "secondOptions"; "required": false; }; "ampmOptions": { "alias": "ampmOptions"; "required": false; }; "currentDisplayHour": { "alias": "currentDisplayHour"; "required": false; }; "currentMinute": { "alias": "currentMinute"; "required": false; }; "currentSecond": { "alias": "currentSecond"; "required": false; }; "isPm": { "alias": "isPm"; "required": false; }; "showSeconds": { "alias": "showSeconds"; "required": false; }; "use24Hour": { "alias": "use24Hour"; "required": false; }; "startDisplayHour": { "alias": "startDisplayHour"; "required": false; }; "startMinute": { "alias": "startMinute"; "required": false; }; "startSecond": { "alias": "startSecond"; "required": false; }; "startIsPm": { "alias": "startIsPm"; "required": false; }; "endDisplayHour": { "alias": "endDisplayHour"; "required": false; }; "endMinute": { "alias": "endMinute"; "required": false; }; "endSecond": { "alias": "endSecond"; "required": false; }; "endIsPm": { "alias": "endIsPm"; "required": false; }; "clearAriaLabel": { "alias": "clearAriaLabel"; "required": false; }; "clearLabel": { "alias": "clearLabel"; "required": false; }; "closeAriaLabel": { "alias": "closeAriaLabel"; "required": false; }; "closeLabel": { "alias": "closeLabel"; "required": false; }; "translations": { "alias": "translations"; "required": false; }; "selectedRange": { "alias": "selectedRange"; "required": false; }; "showTimezoneSelector": { "alias": "showTimezoneSelector"; "required": false; }; "timezoneOptions": { "alias": "timezoneOptions"; "required": false; }; "currentTimezone": { "alias": "currentTimezone"; "required": false; }; "boundIsDateDisabled": { "alias": "boundIsDateDisabled"; "required": false; }; "boundIsYearDisabled": { "alias": "boundIsYearDisabled"; "required": false; }; "boundIsDecadeDisabled": { "alias": "boundIsDecadeDisabled"; "required": false; }; "boundGetDayMetadata": { "alias": "boundGetDayMetadata"; "required": false; }; "calendarHeaderTemplate": { "alias": "calendarHeaderTemplate"; "required": false; }; "calendarFooterTemplate": { "alias": "calendarFooterTemplate"; "required": false; }; "boundIsSameDay": { "alias": "boundIsSameDay"; "required": false; }; "boundIsHoliday": { "alias": "boundIsHoliday"; "required": false; }; "boundIsMultipleSelected": { "alias": "boundIsMultipleSelected"; "required": false; }; "boundIsInRange": { "alias": "boundIsInRange"; "required": false; }; "boundIsInComparisonRange": { "alias": "boundIsInComparisonRange"; "required": false; }; "boundIsPreviewInRange": { "alias": "boundIsPreviewInRange"; "required": false; }; "boundGetAriaLabel": { "alias": "boundGetAriaLabel"; "required": false; }; "boundGetDayCellCustomClasses": { "alias": "boundGetDayCellCustomClasses"; "required": false; }; "boundGetDayCellTooltip": { "alias": "boundGetDayCellTooltip"; "required": false; }; "boundFormatDayNumber": { "alias": "boundFormatDayNumber"; "required": false; }; "getMonthYearLabel": { "alias": "getMonthYearLabel"; "required": false; }; "getCalendarAriaLabelForMonth": { "alias": "getCalendarAriaLabelForMonth"; "required": false; }; "isTimelineMonthSelected": { "alias": "isTimelineMonthSelected"; "required": false; }; "formatTimeSliderValue": { "alias": "formatTimeSliderValue"; "required": false; }; "enableAi": { "alias": "enableAi"; "required": false; }; "aiPlaceholder": { "alias": "aiPlaceholder"; "required": false; }; "aiSuggestions": { "alias": "aiSuggestions"; "required": false; }; "showAiSuggestions": { "alias": "showAiSuggestions"; "required": false; }; "isAiResolving": { "alias": "isAiResolving"; "required": false; }; }, { "timezoneChange": "timezoneChange"; "backdropClick": "backdropClick"; "touchStartContainer": "touchStartContainer"; "touchMoveContainer": "touchMoveContainer"; "touchEndContainer": "touchEndContainer"; "rangeSelect": "rangeSelect"; "previousMonth": "previousMonth"; "nextMonth": "nextMonth"; "currentMonthChange": "currentMonthChange"; "currentYearChange": "currentYearChange"; "dateClick": "dateClick"; "dateHover": "dateHover"; "dateFocus": "dateFocus"; "swipeStart": "swipeStart"; "swipeMove": "swipeMove"; "swipeEnd": "swipeEnd"; "touchStart": "touchStart"; "touchMove": "touchMove"; "touchEnd": "touchEnd"; "viewModeChange": "viewModeChange"; "changeYear": "changeYear"; "yearClick": "yearClick"; "changeDecade": "changeDecade"; "decadeClick": "decadeClick"; "timelineZoomOut": "timelineZoomOut"; "timelineZoomIn": "timelineZoomIn"; "timelineMonthClick": "timelineMonthClick"; "startTimeSliderChange": "startTimeSliderChange"; "endTimeSliderChange": "endTimeSliderChange"; "currentDisplayHourChange": "currentDisplayHourChange"; "currentMinuteChange": "currentMinuteChange"; "currentSecondChange": "currentSecondChange"; "isPmChange": "isPmChange"; "timeChange": "timeChange"; "startDisplayHourChange": "startDisplayHourChange"; "startMinuteChange": "startMinuteChange"; "startSecondChange": "startSecondChange"; "startIsPmChange": "startIsPmChange"; "endDisplayHourChange": "endDisplayHourChange"; "endMinuteChange": "endMinuteChange"; "endSecondChange": "endSecondChange"; "endIsPmChange": "endIsPmChange"; "timeRangeChange": "timeRangeChange"; "clearValue": "clearValue"; "closeCalendar": "closeCalendar"; "escapeKey": "escapeKey"; "containerKeyDown": "containerKeyDown"; "aiPromptSubmitted": "aiPromptSubmitted"; }, never, never, true, never>;
}
interface DayCellRenderHook {
getDayCellClasses?(date: Date, isSelected: boolean, isDisabled: boolean, isToday: boolean, isHoliday: boolean): string[];
getDayCellTooltip?(date: Date, holidayLabel: string | null): string | null;
formatDayNumber?(date: Date): string;
}
interface ValidationHook {
validateDate?(date: Date, currentValue: DatepickerValue, mode: 'single' | 'range' | 'multiple' | 'week' | 'month' | 'quarter' | 'year' | 'timeRange' | 'schedule'): boolean;
validateRange?(startDate: Date, endDate: Date): boolean;
getValidationError?(date: Date): string | null;
}
interface KeyboardShortcutHook {
handleShortcut?(event: KeyboardEvent, context: KeyboardShortcutContext): boolean;
getShortcutHelp?(): KeyboardShortcutHelp[];
}
interface KeyboardShortcutContext {
currentDate: Date;
selectedDate: Date | null;
startDate: Date | null;
endDate: Date | null;
selectedDates: Date[];
mode: 'single' | 'range' | 'multiple' | 'week' | 'month' | 'quarter' | 'year' | 'timeRange' | 'schedule';
focusedDate: Date | null;
isCalendarOpen: boolean;
}
interface KeyboardShortcutHelp {
key: string;
description: string;
modifiers?: string[];
}
interface DateFormatHook {
formatDisplayValue?(value: DatepickerValue, mode: 'single' | 'range' | 'multiple' | 'week' | 'month' | 'quarter' | 'year' | 'timeRange' | 'schedule'): string;
formatAriaLabel?(date: Date): string;
}
interface EventHook {
beforeDateSelect?(date: Date, currentValue: DatepickerValue): boolean;
afterDateSelect?(date: Date, newValue: DatepickerValue): void;
onCalendarOpen?(): void;
onCalendarClose?(): void;
}
interface DatepickerHooks extends DayCellRenderHook, ValidationHook, KeyboardShortcutHook, DateFormatHook, EventHook {
}
/**
* Date Adapter Interface
*
* Allows consumers to swap formatting/parsing logic with external date libraries
* like date-fns, dayjs, or Luxon.
*/
interface DateAdapter {
/**
* Parse a date from a string or value
* @param value - The value to parse (string, Date, or library-specific type)
* @param onError - Optional callback for error handling. Called when parsing fails.
* @returns A Date object or null if parsing fails
*/
parse(value: string | Date | number | unknown, onError?: (error: Error) => void): Date | null;
/**
* Format a date to a string
* @param date - The date to format
* @param format - Format string (library-specific)
* @param locale - Locale string (e.g., 'en-US')
* @returns Formatted date string
*/
format(date: Date, format?: string, locale?: string): string;
/**
* Check if a value is a valid date
* @param value - The value to check
* @returns True if the value is a valid date
*/
isValid(value: string | Date | number | unknown): boolean;
/**
* Get the start of day for a date
* @param date - The date
* @returns Date at start of day
*/
startOfDay(date: Date): Date;
/**
* Get the end of day for a date
* @param date - The date
* @returns Date at end of day
*/
endOfDay(date: Date): Date;
/**
* Add months to a date
* @param date - The date
* @param months - Number of months to add
* @returns New date with months added
*/
addMonths(date: Date, months: number): Date;
/**
* Add days to a date
* @param date - The date
* @param days - Number of days to add
* @returns New date with days added
*/
addDays(date: Date, days: number): Date;
/**
* Check if two dates are the same day
* @param date1 - First date
* @param date2 - Second date
* @returns True if dates are the same day
*/
isSameDay(date1: Date | null, date2: Date | null): boolean;
}
/**
* Default Date Adapter using native JavaScript Date
*/
declare class NativeDateAdapter implements DateAdapter {
parse(value: string | Date | number | unknown, onError?: (error: Error) => void): Date | null;
format(date: Date, _format?: string, locale?: string): string;
isValid(value: string | Date | number | unknown): boolean;
startOfDay(date: Date): Date;
endOfDay(date: Date): Date;
addMonths(date: Date, months: number): Date;
addDays(date: Date, days: number): Date;
isSameDay(date1: Date | null, date2: Date | null): boolean;
}
interface DatepickerConfig {
weekStart?: number | null;
minuteInterval?: number;
holidayProvider?: HolidayProvider | null;
yearRange?: number;
locale?: string;
timezone?: string;
minDate?: Date | string | null;
maxDate?: Date | string | null;
dateAdapter?: DateAdapter;
animations?: AnimationConfig;
autoDetectMobile?: boolean;
mobileModalStyle?: 'bottom-sheet' | 'center' | 'fullscreen';
/**
* When `false`, disables all viewport-based responsive/mobile layout overrides
* (the `@media (max-width: …)` rules that force the mobile center-dialog presentation).
* Useful when embedding the datepicker as a fixed-size inline widget where the browser
* window width should not control the layout.
*
* Defaults to `true` (responsive layout enabled).
*/
responsive?: boolean;
}
interface AnimationConfig {
enabled?: boolean;
duration?: number;
easing?: string;
property?: string;
respectReducedMotion?: boolean;
}
declare const DEFAULT_ANIMATION_CONFIG: Required<AnimationConfig>;
declare const DATEPICKER_CONFIG: InjectionToken<DatepickerConfig>;
declare const DEFAULT_DATEPICKER_CONFIG: DatepickerConfig;
declare function provideDatepickerConfig(config: DatepickerConfig): {
provide: InjectionToken<DatepickerConfig>;
useValue: {
weekStart?: number | null;
minuteInterval?: number;
holidayProvider?: HolidayProvider | null;
yearRange?: number;
locale?: string;
timezone?: string;
minDate?: Date | string | null;
maxDate?: Date | string | null;
dateAdapter?: DateAdapter;
animations?: AnimationConfig;
autoDetectMobile?: boolean;
mobileModalStyle?: "bottom-sheet" | "center" | "fullscreen";
responsive?: boolean;
};
};
interface ValidationError {
kind: string;
message?: string;
}
type SignalFormFieldConfig = {
value?: DatepickerValue | string | (() => DatepickerValue | string) | {
(): DatepickerValue | string;
} | Signal<DatepickerValue | string>;
disabled?: boolean | (() => boolean) | {
(): boolean;
} | Signal<boolean>;
required?: boolean | (() => boolean) | {
(): boolean;
} | Signal<boolean>;
errors?: ValidationError[] | (() => ValidationError[]) | {
(): ValidationError[];
} | Signal<ValidationError[]>;
valid?: boolean | (() => boolean) | {
(): boolean;
} | Signal<boolean>;
invalid?: boolean | (() => boolean) | {
(): boolean;
} | Signal<boolean>;
touched?: boolean | (() => boolean) | {
(): boolean;
} | Signal<boolean>;
setValue?: (value: DatepickerValue | string) => void;
updateValue?: (updater: () => DatepickerValue | string) => void;
markAsDirty?: () => void;
markAsTouched?: () => void;
};
type SignalFormField = unknown;
interface FieldSyncCallbacks {
onValueChanged: (value: DatepickerValue) => void;
onDisabledChanged: (disabled: boolean) => void;
onRequiredChanged?: (required: boolean) => void;
onErrorStateChanged?: (hasError: boolean) => void;
onSyncError: (error: unknown) => void;
normalizeValue: (value: unknown) => DatepickerValue;
isValueEqual: (val1: DatepickerValue, val2: DatepickerValue) => boolean;
onCalendarGenerated?: () => void;
onStateChanged?: () => void;
}
declare class FieldSyncService {
private _fieldEffectRef;
private _lastKnownFieldValue;
private _isUpdatingFromInternal;
private readonly injector;
private readFieldValue;
private readDisabledState;
private readFieldErrors;
private readRequiredState;
private hasValidationErrors;
private resolveField;
setupFieldSync(fieldInput: SignalFormField, callbacks: FieldSyncCallbacks): EffectRef | null;
syncFieldValue(fieldInput: SignalFormField | Signal<SignalFormField> | (() => unknown) | unknown, callbacks: FieldSyncCallbacks): boolean;
updateFieldFromInternal(value: DatepickerValue, fieldInput: SignalFormField | Signal<SignalFormField> | (() => unknown) | unknown): void;
getLastKnownValue(): DatepickerValue | undefined;
markAsTouched(fieldInput: SignalFormField | Signal<SignalFormField> | (() => unknown) | unknown): void;
cleanup(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<FieldSyncService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<FieldSyncService>;
}
interface TranslationService {
translate(key: string, params?: Record<string, string | number>): string;
getCurrentLocale(): string;
}
declare class DefaultTranslationService implements TranslationService {
private translations;
private locale;
private readonly translationRegistry;
constructor();
initialize(translations: DatepickerTranslations, locale?: string): void;
initializeFromLocale(locale: string): void;
translate(key: string, params?: Record<string, string | number>): string;
getCurrentLocale(): string;
static ɵfac: i0.ɵɵFactoryDeclaration<DefaultTranslationService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<DefaultTranslationService>;
}
interface TouchGestureState {
touchStartTime: number;
touchStartElement: EventTarget | null;
dateCellTouchStartTime: number;
dateCellTouchStartDate: Date | null;
dateCellTouchStartX: number;
dateCellTouchStartY: number;
isDateCellTouching: boolean;
lastDateCellTouchDate: Date | null;
dateCellTouchHandled: boolean;
calendarSwipeStartX: number;
calendarSwipeStartY: number;
calendarSwipeStartTime: number;
isCalendarSwiping: boolean;
hoveredDate: Date | null;
}
/** Recurring date pattern configuration for disabled dates. */
type RecurringPatternInput = {
pattern: 'daily' | 'weekly' | 'monthly' | 'yearly' | 'weekdays' | 'weekends';
startDate: Date;
endDate?: Date;
dayOfWeek?: number;
dayOfMonth?: number;
interval?: number;
} | null;
/**
* Interface for Angular Material Form Field Control compatibility.
* We define it here to avoid a direct dependency on @angular/material.
*/
interface MatFormFieldControlMock<T> {
value: T | null;
stateChanges: Subject<void>;
id: string;
placeholder: string;
ngControl: NgControl | null;
focused: boolean;
empty: boolean;
shouldLabelFloat: boolean;
required: boolean;
disabled: boolean;
errorState: boolean;
controlType?: string;
autofilled?: boolean;
userAriaDescribedBy?: string;
setDescribedByIds(ids: string[]): void;
onContainerClick(event: MouseEvent): void;
}
type AiDateResolver = (prompt: string) => Promise<Date | {
start: Date;
end: Date;
} | null | undefined> | Observable<Date | {
start: Date;
end: Date;
} | null | undefined>;
declare class NgxsmkDatepickerComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit, ControlValueAccessor, MatFormFieldControlMock<DatepickerValue> {
private static _idCounter;
private static readonly _allInstances;
private static _materialSupportRegistered;
private static _patchMetadataArrays;
static withMaterialSupport(matFormFieldControlToken: unknown, targetCmp?: unknown): void;
_uniqueId: string;
mode: 'single' | 'range' | 'multiple' | 'week' | 'month' | 'quarter' | 'year' | 'timeRange';
calendarViewMode: 'month' | 'year' | 'decade' | 'timeline' | 'time-slider';
isInvalidDate: (date: Date) => boolean;
/**
* Server-driven disabled dates. Called with the first and last visible day whenever
* the visible month range changes; resolves to the dates that must be disabled.
* Stale responses (superseded by a newer navigation) are discarded.
*
* @example
* ```html
* <ngxsmk-datepicker [asyncDateFilter]="loadBlockedDates" />
* ```
* ```typescript
* loadBlockedDates = (start: Date, end: Date) =>
* firstValueFrom(this.http.get<string[]>(`/api/blocked?from=${start.toISOString()}&to=${end.toISOString()}`));
* ```
*/
asyncDateFilter: ((visibleStart: Date, visibleEnd: Date) => Promise<DateInput[]> | DateInput[]) | null;
/** Emits while an asyncDateFilter request is in flight (true) and when it settles (false). */
readonly asyncDateFilterLoading: i0.OutputEmitterRef<boolean>;
/** Emits when asyncDateFilter rejects; the previous disabled set is kept. */
readonly asyncDateFilterError: i0.OutputEmitterRef<unknown>;
readonly showRanges: i0.InputSignal<boolean>;
readonly showPresets: i0.InputSignal<boolean>;
showTime: boolean;
timeOnly: boolean;
readonly timeRangeMode: i0.InputSignal<boolean>;
showCalendarButton: boolean;
minuteInterval: number;
use24Hour: boolean;
secondInterval: number;
showSeconds: boolean;
holidayProvider: HolidayProvider | null;
disableHolidays: boolean;
private _disabledDates;
set disabledDates(val: (string | Date)[] | null | undefined);
get disabledDates(): (string | Date)[];
private _disabledRanges;
set disabledRanges(val: Array<{
start: Date | string;
end: Date | string;
}> | null | undefined);
get disabledRanges(): Array<{
start: Date | string;
end: Date | string;
}>;
recurringPattern?: RecurringPatternInput;
readonly dateTemplate: i0.InputSignal<TemplateRef<unknown> | null>;
private _placeholder;
set placeholder(value: string | null);
get placeholder(): string;
inline: boolean | 'always' | 'auto';
/**
* When `false`, disables all viewport-based responsive/mobile layout overrides
* (the `@media (max-width: …)` rules that force the center-dialog mobile presentation).
* Useful when the datepicker is embedded as a fixed-size inline widget and the browser
* window width should not affect the calendar layout.
*
* Defaults to `true` (responsive layout enabled, existing behavior preserved).
*
* @example
* ```html
* <!-- Force desktop layout regardless of viewport width -->
* <ngxsmk-datepicker [responsive]="false" />
* ```
*/
responsive: boolean;
private _inputId;
set inputId(value: string);
get inputId(): string;
private _name;
set name(value: string);
get name(): string;
private _autocomplete;
set autocomplete(value: string);
get autocomplete(): string;
translations?: PartialDatepickerTranslations;
translationService?: TranslationService;
clearLabel: string;
closeLabel: string;
prevMonthAriaLabel: string;
nextMonthAriaLabel: string;
clearAriaLabel: string;
closeAriaLabel: string;
get _clearLabel(): string;
get _closeLabel(): string;
get _prevMonthAriaLabel(): string;
get _nextMonthAriaLabel(): string;
get _clearAriaLabel(): string;
get _closeAriaLabel(): string;
weekStart: number | null;
private readonly _yearRange;
set yearRange(value: number);
get yearRange(): number;
timezone?: string;
/**
* When true, trailing and leading days from adjacent months are displayed in the 6-row calendar grid.
* Adjacent month days are dimmed (`opacity: 0.45`) and styled with `.ngxsmk-other-month`.
*/
showOtherMonths: boolean;
weekDaysFull: string[];
hooks: DatepickerHooks | null;
enableKeyboardShortcuts: boolean;
customShortcuts: {
[key: string]: (context: KeyboardShortcutContext) => boolean;
} | null;
readonly autoApplyClose: i0.InputSignal<boolean>;
/**
* Range mode only: allow a one-day range by clicking the same date twice, or by closing the popover
* with only a start date selected (start and end will both be that day).
*/
readonly allowSameDay: i0.InputSignal<boolean>;
displayFormat?: string;
allowTyping: boolean;
/**
* Guided input masking while typing (requires `allowTyping`).
*
* - `true` (or bare attribute): masks using `displayFormat`, falling back to `MM/DD/YYYY`.
* - a pattern string (e.g. `'DD.MM.YYYY'`): masks using that pattern.
* - `false` (default): masking still applies when `displayFormat` is set (existing behavior).
*
* Digits the user types are slotted into `DD`/`MM`/`YY`/`YYYY`/`HH`/`hh`/`mm`/`ss` tokens and
* literal separators are inserted automatically.
*/
inputMask: boolean | string;
enableNaturalLanguage: boolean;
readonly naturalLanguagePreviewTemplate: i0.InputSignal<TemplateRef<unknown> | undefined>;
readonly naturalLanguageResolved: i0.OutputEmitterRef<Date | {
start: Date;
end: Date;
}>;
enableAi: boolean;
aiPlaceholder: string;
aiSuggestions: string[];
showAiSuggestions: boolean;
aiResolver?: AiDateResolver;
readonly aiPromptSubmitted: i0.OutputEmitterRef<string>;
isAiResolving: boolean;
set calendars(value: number);
get calendars(): number;
rangePresetFactory?: (today: Date) => {
id: string;
name: string;
calculate: (today: Date) => {
start: Date;
end: Date;
};
}[];
readonly invalidRange: i0.OutputEmitterRef<{
start: Date;
end: Date;
disabledDatesInside: Date[];
}>;
readonly showTimezoneSelector: i0.InputSignal<boolean>;
defaultTimezone: string;
readonly timezoneChange: i0.OutputEmitterRef<string>;
timezoneOptions: {
label: string;
value: string;
}[];
getTimezoneOptions(): {
label: string;
value: string;
}[];
private _calendarCount;
set calendarCount(value: number);
get calendarCount(): number;
calendarLayout: 'horizontal' | 'vertical' | 'auto';
/**
* Whether selecting a date shifts the calendar view to that date's month.
* Set to `false` in multi-calendar mode to keep the visible months fixed
* when the user clicks a date in a calendar other than the first one.
*/
changeActiveMonthOnSelection: boolean;
/** Shows an ISO 8601 week-number column on the left of the day grid. */
showWeekNumbers: boolean;
/** Header label for the week-number column (e.g. "Wk", "KW", "S"). */
weekNumberLabel: string;
/**
* Annotates each day cell with its date in a second calendar system
* (Hijri, Jalali, Hebrew, Buddhist, or Japanese), rendered via Intl.
* The calendar grid itself remains Gregorian.
*/
secondaryCalendar: CalendarSystem | null;
/**
* Per-day decorations without custom templates: a label under the day number
* (e.g. a price), an indicator dot, extra CSS classes, and a tooltip.
* Called during rendering — keep it fast (precompute/memoize lookups).
*/
dayMetadata: DayMetadataProvider | null;
/**
* Custom content rendered at the top of the popover/inline calendar.
* Template context: `let-actions` → `{ clear(): void; close(): void }`.
*/
calendarHeaderTemplate: TemplateRef<unknown> | null;
/**
* Replaces the default footer (Clear/Close buttons) with custom actions.
* Template context: `let-actions` → `{ clear(): void; close(): void }`.
* Unlike the default footer, a custom footer also renders in inline mode.
*/
calendarFooterTemplate: TemplateRef<unknown> | null;
defaultMonthOffset: number;
/**
* Configuration for synchronous scrolling in multi-calendar mode.
* Keeps calendars in sync by enforcing consistent month offsets across visible calendars.
*
* @example
* ```typescript
* // Keep calendars exactly 1 month apart
* <ngxsmk-datepicker
* [calendarCount]="2"
* [syncScroll]="{ enabled: true, monthGap: 1 }">
* </ngxsmk-datepicker>
*
* // Disable sync scroll (independent navigation)
* <ngxsmk-datepicker
* [calendarCount]="3"
* [syncScroll]="{ enabled: false }">
* </ngxsmk-datepicker>
* ```
*/
readonly syncScroll: i0.InputSignal<{
enabled?: boolean;
monthGap?: number;
}>;
align: 'left' | 'right' | 'center';
useNativePicker: boolean;
readonly enableHapticFeedback: i0.InputSignal<boolean>;
mobileModalStyle: 'bottom-sheet' | 'center' | 'fullscreen';
mobileTimePickerStyle: 'wheel' | 'slider' | 'native';
readonly enablePullToRefresh: i0.InputSignal<boolean>;
readonly mobileTheme: i0.InputSignal<"compact" | "comfortable" | "spacious">;
enableVoiceInput: boolean;
autoDetectMobile: boolean;
readonly disableFocusTrap: i0.InputSignal<boolean>;
readonly appendToBody: i0.InputSignal<boolean>;
private readonly appRef;
private readonly document;
portalTemplate: TemplateRef<unknown>;
private portalViewRef;
get _shouldAppendToBody(): boolean;
/**
* Detects if the datepicker is rendered inside a modal/dialog so the calendar
* can be appended to body and positioned above the modal.
*/
private isInsideModal;
protected readonly _isCalendarOpen: i0.WritableSignal<boolean>;
get isCalendarOpen(): boolean;
set isCalendarOpen(value: boolean);
private readonly _isCalendarOpening;
/** Public getter for template: true while calendar is opening/generating (loading state). */
get isCalendarOpening(): boolean;
/** Returns translated "Loading calendar..." for template and ARIA. */
getCalendarLoadingMessage(): string;
private openCalendarTimeoutId;
private lastToggleTime;
private touchStartTime;
private touchStartElement;
private pointerDownTime;
private isPointerEvent;
private previousFocusElement;
private _value;
set value(val: DatepickerValue);
get value(): DatepickerValue;
private _field;
private _fieldEffectRef;
set field(field: SignalFormField);
get field(): SignalFormField;
private syncFieldValue;
private _startAtDate;
set startAt(value: DateInput | null);
private _locale;
set locale(value: string);
get locale(): string;
theme: 'light' | 'dark';
get isDarkMode(): boolean;
private _dateFormatPattern;
private customDateFormatService;
set dateFormatPattern(value: string | null);
get dateFormatPattern(): string | null;
private _animationConfig;
/**
* Animation configuration allowing customization of animation duration, easing, and reduction.
* Supports prefers-reduced-motion accessibility preference automatically.
*
* @example
* ```typescript
* // Disable all animations
* <ngxsmk-datepicker [animationConfig]="{ enabled: false }"></ngxsmk-datepicker>
*
* // Custom animation duration and easing
* <ngxsmk-datepicker [animationConfig]="{ duration: 300, easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)' }"></ngxsmk-datepicker>
*
* // Disable specific animation properties
* <ngxsmk-datepicker [animationConfig]="{ property: 'opacity' }"></ngxsmk-datepicker>
* ```
*/
set animationConfig(value: AnimationConfig | null);
get animationConfig(): AnimationConfig | null;
private _rtl;
set rtl(value: boolean | null);
get rtl(): boolean | null;
get isRtl(): boolean;
get rtlClass(): boolean;
readonly classes: i0.InputSignal<DatepickerClasses | undefined>;
private onChange;
private onTouched;
disabled: boolean;
set disabledState(isDisabled: boolean);
/**
* Subject used for Material Form Field integration.
* Emits when the component's state changes (disabled, required, error state, etc.)
*
* @remarks
* This Subject is required for Angular Material's form field control interface.
* It allows Material form fields to track state changes and update their appearance
* accordingly (e.g., showing error states, floating labels, etc.).
*
* The Subject is properly cleaned up in ngOnDestroy() to prevent memory leaks.
* It's marked as readonly to prevent external code from reassigning it.
*/
readonly stateChanges: Subject<void>;
private _focused;
private _required;
private _errorState;
get focused(): boolean;
get empty(): boolean;
get shouldLabelFloat(): boolean;
get required(): boolean;
set required(value: boolean);
get errorState(): boolean;
set errorState(value: boolean);
get controlType(): string;
get autofilled(): boolean;
get id(): string;
get describedBy(): string;
/**
* Aria describedby ID provided by the user or the parent form field.
* Required for Angular Material form field control interface.
*/
userAriaDescribedBy: string;
setDescribedByIds(ids: string[]): void;
onContainerClick(_event: MouseEvent): void;
readonly valueChange: i0.OutputEmitterRef<DatepickerValue>;
readonly action: i0.OutputEmitterRef<{
type: string;
payload?: unknown;
}>;
/** Emitted when validation fails (e.g. invalid typed date, date before min, after max). Message is translated. */
readonly validationError: i0.OutputEmitterRef<{
code: string;
message: string;
}>;
private readonly _validationErrorMessage;
/** User-facing validation error message when set (e.g. from typed input or min/max). */
get validationErrorMessage(): string | null;
private setValidationError;
private clearValidationError;
private _minDate;
set minDate(value: DateInput | null);
get minDate(): DateInput | null;
private _maxDate;
set maxDate(value: DateInput | null);
get maxDate(): DateInput | null;
private _ranges;
set ranges(value: DateRange | null);
currentDate: Date;
daysInMonth: (Date | null)[];
multiCalendarMonths: Array<{
month: number;
year: number;
days: (Date | null)[];
}>;
/**
* LRU (Least Recently Used) cache for calendar month generation.
* Caches generated month arrays to avoid recalculating the same months.
*
* @remarks
* Performance characteristics:
* - Calendar generation: O(1) per month when cached
* - Cache lookup: O(1) average case
* - Cache eviction: O(n) where n = cache size (only when cache is full)
*
* The cache automatically evicts the least recently used entry when it reaches
* MAX_CACHE_SIZE to prevent unbounded memory growth. This is especially important
* for applications with many datepicker instances or long-running sessions.
*/
/**
* Maximum number of months to cache before evicting LRU entries.
* Now managed by CalendarGenerationService.
*/
weekDays: string[];
today: Date;
selectedDate: Date | null;
selectedDates: Date[];
startDate: Date | null;
endDate: Date | null;
hoveredDate: Date | null;
rangesArray: {
key: string;
value: [Date, Date];
}[];
protected touchState: TouchGestureState;
private dateCellTouchHandledTime;
private touchHandledTimeout;
private readonly activeTimeouts;
private readonly activeAnimationFrames;
private fieldSyncTimeoutId;
private lazyLoadingObserver;
private readonly touchListenersSetup;
private readonly touchListenersAttached;
private bottomSheetSwipeStartY;
private bottomSheetSwipeCurrentY;
private isBottomSheetSwiping;
private readonly bottomSheetSwipeThreshold;
private readonly SWIPE_THRESHOLD;
private readonly SWIPE_TIME_THRESHOLD;
private _currentMonth;
_currentYear: number;
_currentDecade: number;
monthOptions: i0.Signal<{
disabled: boolean;
label: string;
value: number;
}[]>;
yearOptions: i0.Signal<{
disabled: boolean;
label: string;
value: number;
}[]>;
decadeOptions: {
label: string;
value: number;
}[];
yearGrid: number[];
hourOptions: {
label: string;
value: number;
}[];
minuteOptions: {
label: string;
value: number;
}[];
secondOptions: {
label: string;
value: number;
}[];
decadeGrid: number[];
private firstDayOfWeek;
currentHour: number;
currentMinute: number;
currentSecond: number;
currentDisplayHour: number;
isPm: boolean;
startHour: number;
startMinute: number;
startSecond: number;
startDisplayHour: number;
startIsPm: boolean;
endHour: number;
endMinute: number;
endSecond: number;
endDisplayHour: number;
endIsPm: boolean;
ampmOptions: {
label: string;
value: boolean;
}[];
timelineMonths: Date[];
timelineStartDate: Date;
timelineEndDate: Date;
private timelineZoomLevel;
startTimeSlider: number;
endTimeSlider: number;
private readonly elementRef;
private readonly cdr;
private readonly platformId;
private readonly globalConfig;
private readonly fieldSyncService;
private readonly localeRegistry;
private readonly translationRegistry;
private readonly focusTrapService;
private readonly ariaLiveService;
private readonly hapticFeedbackService;
private readonly calendarGenerationService;
private readonly parsingService;
private readonly touchService;
private readonly popoverPositioningService;
private readonly naturalLanguageParserService;
readonly ngControl: NgControl | null;
private readonly isBrowser;
private readonly dateComparator;
private readonly _naturalLanguagePreview;
get naturalLanguagePreview(): string | null;
set naturalLanguagePreview(value: string | null);
private readonly _showNaturalLanguagePreview;
get showNaturalLanguagePreview(): boolean;
set showNaturalLanguagePreview(value: boolean);
constructor();
private readonly _typedInputValue;
get typedInputValue(): string;
set typedInputValue(value: string);
private isTyping;
popoverContainer?: ElementRef<HTMLElement>;
readonly popoverId: string;
datepickerInput?: NgxsmkDatepickerInputComponent;
datepickerContent?: NgxsmkDatepickerContentComponent;
private focusTrapCleanup;
_translations: DatepickerTranslations | null;
private _translationService;
private _changeDetectionScheduled;
/**
* Schedules change detection to run in the next microtask.
* Prevents multiple change detection cycles from being scheduled simultaneously.
*
* @remarks
* This method is essential for zoneless compatibility. When Zone.js is not present,
* Angular's automatic change detection doesn't run, so components using OnPush
* strategy must manually trigger change detection when state changes.
*
* The debouncing mechanism prevents excessive change detection cycles when multiple
* state changes occur in rapid succession (e.g., during user interactions or async
* operations). Only one change detection cycle is scheduled per microtask queue.
*
* This pattern is compatible with both Zone.js and zoneless Angular applications.
*/
private scheduleChangeDetection;
/**
* Creates a tracked setTimeout that is automatically cleaned up on component destroy.
* All timeouts created through this method are stored in activeTimeouts for proper cleanup.
*
* @param callback - Function to execute after delay
* @param delay - Delay in milliseconds
* @returns Timeout ID that can be used with clearTimeout
*/
private trackedSetTimeout;
/**
* Creates a tracked requestAnimationFrame that is automatically cancelled on component destroy.
* All animation frames created through this method are stored in activeAnimationFrames for proper cleanup.
*
* @param callback - Function to execute on next animation frame
* @returns Animation frame ID that can be used with cancelAnimationFrame
*/
private trackedRequestAnimationFrame;
/**
* Executes a callback after two animation frames, ensuring DOM updates are complete.
* Useful for operations that need to run after Angular's change detection and browser rendering.
*
* @param callback - Function to execute after double animation frame
*/
private trackedDoubleRequestAnimationFrame;
/**
* Clears all active timeouts. Used when locale or weekStart changes
* to cancel any pending operations that might be invalidated by the change.
*/
private clearActiveTimeouts;
/**
* Debounces field synchronization to prevent race conditions from rapid updates.
* Cancels any pending sync operation before scheduling a new one.
*
* @param delay - Debounce delay in milliseconds (default: 100ms)
*/
private debouncedFieldSync;
protected readonly _currentMonthSignal: i0.WritableSignal<number>;
protected readonly _currentYearSignal: i0.WritableSignal<number>;
private readonly _localeSignal;
private readonly _holidayProviderSignal;
private readonly _disabledStateSignal;
/**
* Effect that automatically triggers change detection when key signals change.
* This reduces the need for manual markForCheck() calls throughout the codebase.
*/
private readonly _changeDetectionEffect;
/** Day-start timestamps returned by `asyncDateFilter` for the visible range. */
private readonly _asyncDisabledTimestamps;
/** Monotonic id so late-arriving asyncDateFilter responses can be discarded. */
private _asyncFilterRequestId;
private readonly _asyncDateFilterEffect;
/**
* Invokes `asyncDateFilter` for the currently visible month range and stores the
* resulting disabled dates. No-op on the server and when no filter is set.
*/
private refreshAsyncDisabledDates;
/**
* Signal tracking which calendar month indices are currently visible in the viewport.
* Used for lazy rendering of multi-calendar layouts to improve performance.
*/
private readonly _visibleCalendarIndicesSignal;
/** Bumped when `multiCalendarMonths` is regenerated so `renderedCalendars` invalidates (plain array is not a signal). */
private readonly _multiCalendarDataRevision;
readonly calendarAriaLabel: i0.Signal<string>;
readonly calendarLoadingMessage: i0.Signal<string>;
/**
* Computed signal for rendered calendars - only includes visible calendars + buffer.
* This dramatically reduces DOM nodes for multi-calendar layouts.
*/
renderedCalendars: i0.Signal<{
month: number;
year: number;
days: (Date | null)[];
}[]>;
private _cachedIsCurrentMonthMemo;
private _cachedIsDateDisabledMemo;
private _cachedIsSameDayMemo;
private _cachedIsHolidayMemo;
private _cachedGetHolidayLabelMemo;
private _memoDependencies;
private _disabledDatesTimestamps;
private _parsedDisabledRanges;
private _syncDisabledDatesCache;
private _updateMemoSignals;
private passiveTouchListeners;
get isInlineMode(): boolean;
private clearTouchHandledFlag;
private closeMonthYearDropdowns;
private setTouchHandledFlag;
isMobileDevice(): boolean;
shouldUseNativePicker(): boolean;
getNativeInputType(): string;
formatValueForNativeInput(value: DatepickerValue): string;
formatDateForNativeInput(date: Date): string;
getMinDateForNativeInput(): string | null;
getMaxDateForNativeInput(): string | null;
parseNativeInputValue(value: string): DatepickerValue;
onNativeInputChange(event: Event): void;
onBottomSheetTouchStart(event: TouchEvent): void;
onBottomSheetTouchMove(event: TouchEvent): void;
onBottomSheetTouchEnd(event: TouchEvent): void;
readonly boundIsDateDisabled: (d: Date | null) => boolean;
readonly boundIsSameDay: (d1: Date | null, d2: Date | null) => boolean;
readonly boundIsHoliday: (d: Date | null) => boolean;
readonly boundIsMultipleSelected: (d: Date | null) => boolean;
readonly boundIsInRange: (d: Date | null) => boolean;
readonly boundIsPreviewInRange: (d: Date | null) => boolean;
/**
* Optional secondary "comparison" range, highlighted in the calendar alongside the
* primary selection. Useful for analytics/dashboard scenarios (e.g. "this period vs
* the previous period"). Provide `[start, end]`; pass `null` to disable. Purely
* presentational — it does not affect selection or emitted values.
*/
readonly comparisonRange: i0.InputSignal<[Date | null, Date | null] | null>;
readonly boundIsInComparisonRange: (d: Date | null) => boolean;
readonly boundGetAriaLabel: (d: Date | null) => string;
readonly boundGetDayCellCustomClasses: (d: Date | null) => string[];
readonly boundGetDayCellTooltip: (d: Date | null) => string | null;
readonly boundGetDayMetadata: (d: Date | null) => DayMetadata | null;
readonly boundFormatDayNumber: (d: Date | null) => string;
readonly boundGetMonthYearLabel: (m: number, y: number) => string;
readonly boundGetCalendarAriaLabelForMonth: (m: number, y: number) => string;
readonly boundIsTimelineMonthSelected: (d: Date) => boolean;
readonly boundFormatTimeSliderValue: (v: number) => string;
isYearDisabled: (year: number) => boolean;
isDecadeDisabled: (decade: number) => boolean;
readonly boundIsYearDisabled: (year: number) => boolean;
readonly boundIsDecadeDisabled: (decade: number) => boolean;
get isCalendarVisible(): boolean;
get displayValue(): string;
private getDisplayValueTimeOnly;
private getDisplayValueDateDefault;
private formatWithCustomFormat;
private formatWithAdapter;
private formatWithParsingServiceFallback;
/**
* Format dates using a custom date format pattern
* Supports YYYY, MM, DD, HH, mm, ss, etc.
*/
private formatWithCustomPattern;
get isBackArrowDisabled(): boolean;
private adjustDisplayedDateToRange;
private _invalidateMemoCache;
get isCurrentMonthMemo(): (day: Date | null) => boolean;
/**
* Memoized function for checking if a date is disabled.
* Returns a cached function that checks date constraints efficiently.
*
* @returns A function that checks if a date is disabled
*
* @remarks
* This getter implements memoization to avoid recreating the validation function
* on every calendar render. The function is regenerated only when:
* - Disabled state constraints change (minDate, maxDate, disabledDates, disabledRanges)
* - Current month/year changes
*
* Performance: O(1) to get the memoized function, O(n) to execute where n = constraints
* The memoization significantly improves performance when rendering calendar grids
* with many date cells (e.g., multiple calendar months).
*/
get isDateDisabledMemo(): (day: Date | null) => boolean;
/**
* Memoized function for comparing if two dates are the same day.
* Uses an optimized date comparator for efficient day-level comparisons.
*
* @returns A function that compares two dates for same-day equality
*
* @remarks
* The date comparator normalizes times to start of day before comparison,
* ensuring accurate day-level equality checks regardless of time components.
*
* Performance: O(1) - Simple date field comparisons after normalization
*/
get isSameDayMemo(): (d1: Date | null, d2: Date | null) => boolean;
/**
* Memoized function for checking if a date is a holiday.
* Returns a cached function that uses the current holiday provider.
*
* @returns A function that checks if a date is a holiday
*
* @remarks
* The function is regenerated when the holidayProvider changes.
* This ensures the memoized function always uses the current provider
* while avoiding recreation on every calendar render.
*
* Performance: O(1) to get memoized function, O(1) to execute (depends on provider implementation)
*/
get isHolidayMemo(): (day: Date | null) => boolean;
get getHolidayLabelMemo(): (day: Date | null) => string | null;
/**
* TrackBy function for calendar day cells in *ngFor loops.
* Provides stable identity for Angular's change detection optimization.
*
* @param index - Array index of the day
* @param day - The date object (or null for empty cells)
* @returns Unique identifier for the day cell
*
* @remarks
* Using timestamp ensures stable identity even when Date objects are recreated.
* This significantly improves *ngFor performance by allowing Angular to track
* which items have changed, moved, or been removed.
*/
trackByDay(index: number, day: Date | null): string;
/**
* TrackBy function for calendar month containers in multi-calendar views.
* Provides stable identity for efficient change detection.
*
* @param _index - Array index (unused, using year-month for identity)
* @param calendarMonth - The calendar month object
* @returns Unique identifier combining year and month
*/
private containsNodeViaComposedPath;
private containsNodeViaDOM;
/**
* Checks if a DOM node is contained within this datepicker instance,
* including its input group and any portaled popover content.
*
* @param target - The node to check
* @returns True if the node is inside this datepicker's DOM tree
*/
containsNode(target: Node | null, event?: UIEvent): boolean;
/** Shared logic for closing calendar when user interacts outside (click or touch). */
private tryCloseCalendarOnOutsideInteraction;
onDocumentClick(event: MouseEvent | TouchEvent): void;
onDocumentTouchStart(event: TouchEvent): void;
private handleDocumentOutsideInteraction;
onTouchStart(event: TouchEvent): void;
onInputGroupFocus(): void;
private focusInput;
onTouchEnd(event: TouchEvent): void;
private closeOtherCalendarInstances;
private applyCalendarOpenStateFromTouch;
onPointerDown(event: PointerEvent): void;
onPointerUp(event: PointerEvent): void;
private clearPointerTouchState;
private applyCalendarCloseState;
private applyCalendarOpenStateFromPointer;
onKeyDown(event: KeyboardEvent): void;
private isElementInsideCalendar;
private handleKeyboardNavigation;
private tryCustomShortcuts;
private tryHooksHandleShortcut;
private handleShortcutKey;
private handleShortcutNavigationKeys;
private handleShortcutArrowKeys;
private handleShortcutPageHomeEndKeys;
private handleShortcutLetterAndSpecialKeys;
isKeyboardHelpOpen: boolean;
toggleKeyboardHelp(): void;
private getShortcutKey;
focusedDate: Date | null;
focusDateCell(date: Date): void;
private findFirstValidDateInMonth;
private findLastValidDateInMonth;
private navigateMonth;
private navigateYear;
private navigateDate;
private navigateToFirstDay;
private navigateToLastDay;
private selectToday;
private selectYesterday;
private selectTomorrow;
private selectNextWeek;
onDateFocus(day: Date | null): void;
private isDateValid;
/** Resolves `dayMetadata` for a day, tolerating provider errors. */
getDayMetadata(day: Date | null): DayMetadata | null;
getDayCellCustomClasses(day: Date | null): string[];
getDayCellTooltip(day: Date | null): string | null;
formatDayNumber(day: Date | null): string;
/**
* Generates an accessible label for a date cell.
* Provides screen readers with a descriptive label for each selectable date.
*
* @param day - The date to generate a label for
* @returns Localized date label (e.g., "Monday, January 15, 2024")
*
* @remarks
* The label includes weekday, month, day, and year for full context.
* Custom formatting can be provided via the formatAriaLabel hook.
* This ensures screen reader users have complete information about each date.
*/
getAriaLabel(day: Date | null): string;
/**
* ControlValueAccessor implementation: Writes a new value to the form control.
* Called by Angular Forms when the form control value changes programmatically.
*
* @param val - The new value from the form control
*
* @remarks
* This method:
* - Normalizes the incoming value to ensure consistent format
* - Initializes component state from the value
* - Updates memoized signals for change detection
* - Regenerates calendar to reflect the new value
* - Notifies Material Form Field of state changes
* - Syncs with Signal Form field if field input is used
*
* This is part of the ControlValueAccessor interface, enabling two-way binding
* with both Reactive Forms and Template-driven Forms.
*/
writeValue(val: DatepickerValue): void;
/**
* ControlValueAccessor implementation: Registers a callback for value changes.
* Called by Angular Forms to receive notifications when the user changes the value.
*
* @param fn - Callback function to call when value changes
*/
registerOnChange(fn: (value: DatepickerValue) => void): void;
/**
* ControlValueAccessor implementation: Registers a callback for touched state.
* Called by Angular Forms to receive notifications when the user interacts with the control.
*
* @param fn - Callback function to call when control is touched
*/
registerOnTouched(fn: () => void): void;
setDisabledState(isDisabled: boolean): void;
/**
* Emits a value change event and updates the internal state.
* Handles normalization, form field synchronization, and calendar auto-close behavior.
*
* @param val - The new datepicker value (Date, Date range, or array of dates)
*
* @remarks
* This method is the central point for value updates and ensures:
* - Value normalization for consistent internal representation
* - Signal Form field synchronization (if field input is used)
* - Event emission for two-way binding
* - Touch state tracking for form validation
* - Automatic calendar closing for single date and complete range selections
*
* The calendar auto-closes when:
* - Single date mode: After any date selection
* - Range mode: After both start and end dates are selected
* - Not in inline mode
* - Not in time-only mode
*/
private emitValue;
/**
* Toggles the calendar popover open/closed state.
* Handles focus management, accessibility announcements, and prevents rapid toggling.
*
* @param event - Optional event that triggered the toggle (used to prevent toggle on clear button clicks)
*
* @remarks
* This method implements several important behaviors:
* - Debouncing: Prevents rapid toggling within 300ms
* - Focus management: Stores previous focus element for restoration
* - Accessibility: Announces calendar state changes to screen readers
* - Touch optimization: Sets up passive touch listeners for mobile devices
*
* When opening:
* - Stores the currently focused element for restoration
* - Sets up focus trap for keyboard navigation
* - Announces calendar opening with current month/year
*
* When closing:
* - Removes focus trap
* - Restores focus to previous element
* - Announces calendar closing
*/
toggleCalendar(event?: Event): void;
private applyToggleWithNoEvent;
private shouldSkipClickToggle;
private applyDefaultMonthForOpen;
private applySmartViewModeForOpen;
private announceAfterOpen;
private announceAfterClose;
private announceCalendarOpened;
private announceCalendarClosed;
onBackdropInteract(event: Event): void;
onPopoverEscape(event: Event): void;
private scrollDebounceTimer;
private readonly updatePositionOnScroll;
private _startOpeningState;
private _startClosingState;
private renderInBody;
private lockBodyScroll;
private unlockBodyScroll;
/** Hides the body-appended popover so loading/calendar are not visible at wrong position. */
private hideBodyPopoverUntilPositioned;
/** Shows the body-appended popover after positioning has been applied. */
private revealBodyPopover;
private destroyBodyView;
private closeCalendar;
private shouldAutoClose;
/**
* Clears the selected date value(s) and resets the component state.
* Emits null value and closes calendar if open.
*
* @param event - Optional event that triggered the clear action
*
* @remarks
* This method:
* - Clears all selected dates (single, range, multiple modes)
* - Emits null value to form controls
* - Closes calendar if open
* - Provides haptic feedback on mobile if enabled
* - Resets touch gesture state
* - Announces clearing to screen readers
*
* Used by the clear button and can be called programmatically.
*/
clearValue(event?: MouseEvent | TouchEvent): void;
get currentMonth(): number;
set currentMonth(month: number);
get currentYear(): number;
set currentYear(year: number);
private _updateToday;
ngOnInit(): void;
private initializeTimeFromNowIfNeeded;
private resolveInitialValue;
private resolveInitialValueFromField;
ngAfterViewInit(): void;
private setupInputGroupPassiveListeners;
private _touchListenersSetupTimeout;
/**
* Sets up passive touch event listeners on calendar day cells for improved mobile performance.
* Implements retry logic to handle cases where DOM elements aren't immediately available.
* All listeners are tracked for proper cleanup on component destroy.
*/
private setupPassiveTouchListeners;
private attachTouchListenersToCells;
ngOnChanges(changes: SimpleChanges): void;
private handleChangesShowTimeInlineAndLayout;
private handleChangesLocaleRtl;
private handleChangesWeekStartAndRelated;
private applyChangesMinuteAnd24Hour;
private handleChangesField;
private handleChangesTimeAndMode;
private handleChangesDisabledStates;
private handleChangesTranslations;
private handleChangesMaxDate;
private handleChangesValue;
private handleChangesStartAt;
private handleChangesMinDate;
private handleChangesCalendarViewMode;
/**
* Validates component inputs for conflicts and invalid combinations.
* Logs warnings in development mode when invalid configurations are detected.
*
* @param changes - The SimpleChanges object from ngOnChanges
*/
private validateInputs;
private validateInputsMinMaxDate;
private validateInputsTimeOnly;
private validateInputsIntervals;
private validateInputsYearRange;
private initializeTimeSliders;
private get24Hour;
private update12HourState;
private applyCurrentTime;
private applyTimeIfNeeded;
/**
* Initializes the component's internal state from a DatepickerValue.
* Sets up selected dates, calendar view position, and time values based on the provided value.
*
* @param value - The datepicker value to initialize from (Date, range, array, or null)
*
* @remarks
* This method handles initialization for all selection modes:
* - Single mode: Sets selectedDate
* - Range mode: Sets startDate and endDate
* - Multiple mode: Sets selectedDates array
*
* The method also:
* - Determines the calendar view center date (uses value, startAt, or minDate as fallback)
* - Extracts and sets time values if the date includes time information
* - Normalizes all dates to ensure consistent internal representation
*
* Performance: O(1) for single/range, O(n) for multiple mode where n = array length
*/
private initializeValue;
private applyValueToSelection;
private resolveViewCenterDate;
private _normalizeDate;
/**
* Normalizes various date input formats into a consistent DatepickerValue type.
* Handles Date objects, Moment.js objects, date ranges, arrays, and strings.
*
* @param val - The value to normalize (can be Date, Moment, range object, array, or string)
* @returns Normalized DatepickerValue (Date, range object, array, or null)
*
* @remarks
* This method provides flexible input handling to support:
* - Native JavaScript Date objects
* - Moment.js objects (with timezone preservation)
* - Date range objects: { start: Date, end: Date }
* - Arrays of dates for multiple selection mode
* - String dates with custom format parsing
*
* Invalid or unparseable values are normalized to null.
* This ensures type safety and consistent internal state representation.
*/
private _normalizeValue;
private _normalizeRangeValue;
private _normalizeArrayValue;
/**
* Check if the provided value is a Moment.js object
*/
private isMomentObject;
/**
* Convert a Moment.js object to a Date, preserving timezone offset
*/
private momentToDate;
/**
* Compares two DatepickerValue objects for equality.
* Handles Date objects, range objects, and arrays with proper date comparison.
*
* @param val1 - First value to compare
* @param val2 - Second value to compare
* @returns true if values represent the same date(s), false otherwise
*
* @remarks
* This method performs deep equality checks:
* - For Date objects: Compares using date comparator (handles time normalization)
* - For range objects: Compares both start and end dates
* - For arrays: Compares lengths and all elements
* - Handles null/undefined values correctly
*
* Uses the dateComparator utility for efficient date comparisons that
* normalize times to start of day for accurate day-level equality.
*/
private isValueEqual;
/**
* Parses a date string, optionally using the configured date adapter with error callback.
* Falls back to native Date parsing if no adapter is configured.
*
* @param dateString - The date string to parse
* @returns Parsed Date object or null if parsing fails
*
* @remarks
* If a date adapter is configured via globalConfig, it will be used for parsing
* with error callbacks. Otherwise, native Date parsing is used.
* Error callbacks allow consumers to handle parsing failures gracefully.
*/
onInputFocus(event: FocusEvent): void;
/**
* Sanitizes user input to prevent XSS attacks.
* Removes potentially dangerous characters while preserving valid date/time input.
*
* @param input - Raw user input string
* @returns Sanitized string safe for template interpolation
*
* @remarks
* This method provides basic XSS protection by removing:
* - HTML tag delimiters (< and >)
* - Script event handlers (onerror, onclick, etc.)
* - JavaScript protocol (javascript:)
* - Data URIs that could contain scripts
*
* Note: Angular's template interpolation provides additional protection,
* but this sanitization adds an extra layer of defense for user-provided strings.
* For comprehensive sanitization, Angular's DomSanitizer should be used for
* any HTML content, but for date/time strings, this level of sanitization is sufficient.
*/
private sanitizeInput;
onInputChange(event: Event): void;
onInputBlur(event: FocusEvent): void;
private applyValidationErrorForBlur;
onInputKeyDown(event: Event): void;
/**
* Resolves the active mask pattern, or null when masking is off.
* `inputMask` (true / bare attribute / pattern string) takes precedence;
* otherwise a set `displayFormat` keeps masking on for backward compatibility.
*/
private effectiveInputMaskPattern;
private applyInputMask;
private isValidDate;
private applyTypedDate;
private generateTimeOptions;
private generateLocaleData;
private _getDefaultPresets;
private updateRangesArray;
setTimezone(tz: string): void;
checkAndEmitInvalidRange(start: Date, end: Date): boolean;
updateNaturalLanguagePreview(value: string): void;
onAiPromptSubmitted(prompt: string): Promise<void>;
selectRange(range: [Date, Date]): void;
isHoliday(date: Date | null): boolean;
getHolidayLabel(date: Date | null): string | null;
/**
* Checks if a date is disabled based on all configured constraints.
*
* @param date - The date to check
* @returns true if the date is disabled, false if it can be selected
*
* @remarks
* A date is considered disabled if it matches any of these conditions:
* - Falls before minDate
* - Falls after maxDate
* - Is in the disabledDates array
* - Falls within a disabledRanges entry
* - Fails the isInvalidDate custom validation function
* - Is a holiday and disableHolidays is true
*
* Performance: O(n) where n = disabledDates.length + disabledRanges.length
* For large constraint lists (>1000), consider optimizing with Set or DateRange tree.
*/
isDateDisabled(date: Date | null): boolean;
private _isInDisabledDates;
private _isInDisabledRanges;
private _isOutOfMinMaxBounds;
/**
* Checks if a date is selected in multiple selection mode.
*
* @param d - The date to check
* @returns true if the date is in the selectedDates array
*
* @remarks
* Performance: O(n) where n = selectedDates.length
* Uses day-level comparison (ignores time) for accurate matching.
*/
isMultipleSelected(d: Date | null): boolean;
/**
* Handles time value changes from time selection controls.
* Updates the selected date(s) with the new time values.
*
* @remarks
* This method:
* - Applies time changes to selected dates based on current mode
* - Emits value changes for form integration
* - Handles time-only mode by creating a date with current time
* - Updates all selected dates in multiple mode
* - Ensures startDate <= endDate in range mode
*/
timeChange(): void;
/**
* Handles time range changes in timeRangeMode.
* Updates the internal time range state and emits the time range to listeners.
* Only creates/updates time-only dates (no calendar dates).
*/
timeRangeChange(): void;
/**
* Handles date cell click/tap events.
* Processes date selection based on the current mode (single, range, multiple, etc.)
* and handles touch gesture debouncing to prevent accidental double selections.
*
* @param day - The date that was clicked (null for empty cells)
*
* @remarks
* This method implements several important behaviors:
* - Touch gesture handling: Debounces rapid touch events to prevent double-clicks
* - Date validation: Checks if the date is disabled before processing
* - Hook integration: Calls beforeDateSelect hook if provided
* - Mode-specific logic: Handles single, range, multiple, week, month, quarter, and year modes
* - Calendar navigation: Automatically navigates to different month if date is outside current view
* - Accessibility: Announces date selection to screen readers
* - Auto-close: Closes calendar after selection in single mode or complete range
*
* Performance considerations:
* - Touch debouncing prevents excessive event processing
* - Date normalization happens once per selection
* - Calendar regeneration is optimized with caching
*/
onDateClick(day: Date | null): void;
/**
* Returns true (and cleans up touch state) when the click event should be
* ignored because it was already handled by the touch handler within the
* deduplication window (250 ms).
*/
private _shouldSkipDueToTouchGuard;
private _navigateToMonthOfDay;
private _handleSingleModeClick;
private _handleRangeModeClick;
private _handleRangeEndSelection;
/** Completes range with end equal to start when `allowSameDay` is enabled. */
private _tryCompleteSameDayRange;
private _announceRangeSelected;
private _finalizeSameDayRangeOnClose;
private _handlePeriodModeClick;
private _handleMultipleModeClick;
private _applyRecurringPattern;
private _syncTimeAfterDateClick;
onDateHover(day: Date | null): void;
onDateCellTouchStart(event: TouchEvent, day: Date | null): void;
onDateCellTouchMove(event: TouchEvent): void;
onDateCellTouchEnd(event: TouchEvent, day: Date | null): void;
isPreviewInRange(day: Date | null): boolean;
private buildCalendarMonths;
/**
* Generates the calendar view for the current month(s).
* Uses LRU caching to optimize performance for frequently accessed months.
*
* @remarks
* Performance characteristics:
* - First generation: O(n) where n = number of days in month(s)
* - Cached generation: O(1) lookup + O(1) cache access update
* - Cache eviction: O(m) where m = cache size (only when cache is full)
*
* This method:
* 1. Generates dropdown options for month/year selection
* 2. Generates calendar days for each month in calendarCount
* 3. Uses LRU cache to avoid regenerating recently accessed months
* 4. Handles month/year rollover when displaying multiple calendars
* 5. Updates memoized dependencies for change detection optimization
* 6. Supports synchronous scrolling to keep calendars in sync (when enabled)
*
* The cache key format is `${year}-${month}` to ensure unique identification
* of calendar months across different years.
*
* When syncScroll is enabled, calendars are kept synchronized:
* - Calendar 0: currentDate month + (0 * monthGap)
* - Calendar 1: currentDate month + (1 * monthGap)
* - Calendar 2: currentDate month + (2 * monthGap)
*/
generateCalendar(): void;
/**
* Preloads adjacent months (previous and next) into the cache for smoother navigation.
* Implements lazy loading optimization to improve performance when users navigate between months.
*
* @param currentYear - Current calendar year
* @param currentMonth - Current calendar month (0-11)
*/
private preloadAdjacentMonths;
private generateDropdownOptions;
private generateYearGrid;
private generateDecadeGrid;
onYearClick(year: number): void;
onDecadeClick(decade: number): void;
/**
* Changes the displayed decade by the specified delta.
* Used in decade view mode for navigating between decades.
*
* @param delta - Number of decades to change (positive for future, negative for past)
*
* @remarks
* Each delta unit represents 10 years. The method updates the decade grid
* to show the new range of decades available for selection.
*/
changeDecade(delta: number): void;
/**
* Changes the displayed calendar year by the specified delta.
* Updates year grid and calendar view, and announces the change to screen readers.
*
* @param delta - Number of years to change (positive for future, negative for past)
*
* @remarks
* This method:
* - Updates currentYear and currentDate
* - Regenerates year grid and calendar view
* - Announces year change to screen readers for accessibility
* - Handles touch listener setup for mobile devices
*
* Performance: O(1) for year change, O(n) for grid/calendar generation
*/
changeYear(delta: number): void;
onViewModeChange(mode: 'month' | 'year' | 'decade' | 'timeline' | 'time-slider'): void;
onYearSelectChange(year: unknown): void;
private generateTimeline;
timelineZoomIn(): void;
timelineZoomOut(): void;
isTimelineMonthSelected(month: Date): boolean;
onTimelineMonthClick(month: Date): void;
formatTimeSliderValue(minutes: number): string;
onStartTimeSliderChange(minutes: number): void;
onEndTimeSliderChange(minutes: number): void;
onCalendarSwipeStart(event: TouchEvent): void;
onCalendarSwipeMove(event: TouchEvent): void;
onCalendarSwipeEnd(event: TouchEvent): void;
changeMonth(delta: number): void;
isSameDay(d1: Date | null, d2: Date | null): boolean;
isCurrentMonth(day: Date | null): boolean;
isInRange(d: Date | null): boolean;
/**
* Whether `d` falls within the optional {@link comparisonRange} (inclusive of both
* endpoints, since the comparison range has no distinct start/end cells).
*/
isInComparisonRange(d: Date | null): boolean;
private applyGlobalConfig;
private applyGlobalConfigDefaults;
private applyGlobalConfigLocaleAndDates;
private applyGlobalConfigMobile;
/**
* Apply animation configuration from global config
*/
private applyAnimationConfig;
/**
* Initialize translations from service or registry
*/
private initializeTranslations;
/**
* Generates an accessible label for the calendar dialog.
* Provides screen readers with context about which month/year is being displayed.
*
* @returns Localized calendar label (e.g., "Calendar for January 2024")
*/
getCalendarAriaLabel(): string;
/**
* Generates an accessible label for a specific calendar month in multi-calendar views.
*
* @param month - Month index (0-11)
* @param year - Year number
* @returns Localized calendar label for the specified month/year
*/
getCalendarAriaLabelForMonth(month: number, year: number): string;
/**
* Sets up IntersectionObserver for lazy loading multi-calendar months.
* Only initializes if multi-calendar is enabled (calendarCount > 1).
*
* @remarks
* Uses IntersectionObserver to track which calendar month elements are visible
* in the viewport. Updates the visible indices signal to enable/disable rendering.
*/
private setupLazyLoadingObserver;
/**
* Formats a month and year into a display label.
*
* @param month - Month index (0-11)
* @param year - Year number
* @returns Formatted string like "January 2024"
*/
getMonthYearLabel(month: number, year: number): string;
isCurrentMonthForCalendar(day: Date | null, targetMonth: number, targetYear: number): boolean;
getTranslation(key: keyof DatepickerTranslations, fallbackKey?: keyof DatepickerTranslations, params?: Record<string, string | number>): string;
/**
* Closes the calendar and restores focus to the previously focused element.
* This improves accessibility by returning focus to the trigger element.
*/
closeCalendarWithFocusRestore(): void;
private updateRtlState;
/**
* Component lifecycle hook: Cleanup all resources, subscriptions, and event listeners.
* Ensures no memory leaks by:
* - Removing instance from static registry
* - Cleaning up field sync service
* - Completing stateChanges subject
* - Clearing all tracked timeouts and animation frames
* - Removing touch event listeners
* - Invalidating month cache
*/
ngOnDestroy(): void;
private getActualPopoverContainer;
private setupFocusTrap;
/**
* Positions the popover relative to the input element dynamically.
* - Prioritizes layout below the input.
* - Falls back to positioning above if required.
* - Defaults to CSS-centered positioning if space is insufficient.
*
* @remarks
* This logic primarily targets mobile/tablet viewports; desktop layout (≥1024px)
* is handled via CSS absolute positioning.
*/
private positionPopoverRelativeToInput;
/**
* Determines if the component is operating within an Ionic environment.
* This detection disables features that may conflict with Ionic's overlay system.
*/
private isIonicEnvironment;
private removeFocusTrap;
static ɵfac: i0.ɵɵFactoryDeclaration<NgxsmkDatepickerComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<NgxsmkDatepickerComponent, "ngxsmk-datepicker", never, { "mode": { "alias": "mode"; "required": false; }; "calendarViewMode": { "alias": "calendarViewMode"; "required": false; }; "isInvalidDate": { "alias": "isInvalidDate"; "required": false; }; "asyncDateFilter": { "alias": "asyncDateFilter"; "required": false; }; "showRanges": { "alias": "showRanges"; "required": false; "isSignal": true; }; "showPresets": { "alias": "showPresets"; "required": false; "isSignal": true; }; "showTime": { "alias": "showTime"; "required": false; }; "timeOnly": { "alias": "timeOnly"; "required": false; }; "timeRangeMode": { "alias": "timeRangeMode"; "required": false; "isSignal": true; }; "showCalendarButton": { "alias": "showCalendarButton"; "required": false; }; "minuteInterval": { "alias": "minuteInterval"; "required": false; }; "use24Hour": { "alias": "use24Hour"; "required": false; }; "secondInterval": { "alias": "secondInterval"; "required": false; }; "showSeconds": { "alias": "showSeconds"; "required": false; }; "holidayProvider": { "alias": "holidayProvider"; "required": false; }; "disableHolidays": { "alias": "disableHolidays"; "required": false; }; "disabledDates": { "alias": "disabledDates"; "required": false; }; "disabledRanges": { "alias": "disabledRanges"; "required": false; }; "recurringPattern": { "alias": "recurringPattern"; "required": false; }; "dateTemplate": { "alias": "dateTemplate"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; }; "inline": { "alias": "inline"; "required": false; }; "responsive": { "alias": "responsive"; "required": false; }; "inputId": { "alias": "inputId"; "required": false; }; "name": { "alias": "name"; "required": false; }; "autocomplete": { "alias": "autocomplete"; "required": false; }; "translations": { "alias": "translations"; "required": false; }; "translationService": { "alias": "translationService"; "required": false; }; "clearLabel": { "alias": "clearLabel"; "required": false; }; "closeLabel": { "alias": "closeLabel"; "required": false; }; "prevMonthAriaLabel": { "alias": "prevMonthAriaLabel"; "required": false; }; "nextMonthAriaLabel": { "alias": "nextMonthAriaLabel"; "required": false; }; "clearAriaLabel": { "alias": "clearAriaLabel"; "required": false; }; "closeAriaLabel": { "alias": "closeAriaLabel"; "required": false; }; "weekStart": { "alias": "weekStart"; "required": false; }; "yearRange": { "alias": "yearRange"; "required": false; }; "timezone": { "alias": "timezone"; "required": false; }; "showOtherMonths": { "alias": "showOtherMonths"; "required": false; }; "hooks": { "alias": "hooks"; "required": false; }; "enableKeyboardShortcuts": { "alias": "enableKeyboardShortcuts"; "required": false; }; "customShortcuts": { "alias": "customShortcuts"; "required": false; }; "autoApplyClose": { "alias": "autoApplyClose"; "required": false; "isSignal": true; }; "allowSameDay": { "alias": "allowSameDay"; "required": false; "isSignal": true; }; "displayFormat": { "alias": "displayFormat"; "required": false; }; "allowTyping": { "alias": "allowTyping"; "required": false; }; "inputMask": { "alias": "inputMask"; "required": false; }; "enableNaturalLanguage": { "alias": "enableNaturalLanguage"; "required": false; }; "naturalLanguagePreviewTemplate": { "alias": "naturalLanguagePreviewTemplate"; "required": false; "isSignal": true; }; "enableAi": { "alias": "enableAi"; "required": false; }; "aiPlaceholder": { "alias": "aiPlaceholder"; "required": false; }; "aiSuggestions": { "alias": "aiSuggestions"; "required": false; }; "showAiSuggestions": { "alias": "showAiSuggestions"; "required": false; }; "aiResolver": { "alias": "aiResolver"; "required": false; }; "calendars": { "alias": "calendars"; "required": false; }; "rangePresetFactory": { "alias": "rangePresetFactory"; "required": false; }; "showTimezoneSelector": { "alias": "showTimezoneSelector"; "required": false; "isSignal": true; }; "defaultTimezone": { "alias": "defaultTimezone"; "required": false; }; "calendarCount": { "alias": "calendarCount"; "required": false; }; "calendarLayout": { "alias": "calendarLayout"; "required": false; }; "changeActiveMonthOnSelection": { "alias": "changeActiveMonthOnSelection"; "required": false; }; "showWeekNumbers": { "alias": "showWeekNumbers"; "required": false; }; "weekNumberLabel": { "alias": "weekNumberLabel"; "required": false; }; "secondaryCalendar": { "alias": "secondaryCalendar"; "required": false; }; "dayMetadata": { "alias": "dayMetadata"; "required": false; }; "calendarHeaderTemplate": { "alias": "calendarHeaderTemplate"; "required": false; }; "calendarFooterTemplate": { "alias": "calendarFooterTemplate"; "required": false; }; "defaultMonthOffset": { "alias": "defaultMonthOffset"; "required": false; }; "syncScroll": { "alias": "syncScroll"; "required": false; "isSignal": true; }; "align": { "alias": "align"; "required": false; }; "useNativePicker": { "alias": "useNativePicker"; "required": false; }; "enableHapticFeedback": { "alias": "enableHapticFeedback"; "required": false; "isSignal": true; }; "mobileModalStyle": { "alias": "mobileModalStyle"; "required": false; }; "mobileTimePickerStyle": { "alias": "mobileTimePickerStyle"; "required": false; }; "enablePullToRefresh": { "alias": "enablePullToRefresh"; "required": false; "isSignal": true; }; "mobileTheme": { "alias": "mobileTheme"; "required": false; "isSignal": true; }; "enableVoiceInput": { "alias": "enableVoiceInput"; "required": false; }; "autoDetectMobile": { "alias": "autoDetectMobile"; "required": false; }; "disableFocusTrap": { "alias": "disableFocusTrap"; "required": false; "isSignal": true; }; "appendToBody": { "alias": "appendToBody"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; }; "field": { "alias": "field"; "required": false; }; "startAt": { "alias": "startAt"; "required": false; }; "locale": { "alias": "locale"; "required": false; }; "theme": { "alias": "theme"; "required": false; }; "dateFormatPattern": { "alias": "dateFormatPattern"; "required": false; }; "animationConfig": { "alias": "animationConfig"; "required": false; }; "rtl": { "alias": "rtl"; "required": false; }; "classes": { "alias": "classes"; "required": false; "isSignal": true; }; "disabledState": { "alias": "disabledState"; "required": false; }; "required": { "alias": "required"; "required": false; }; "errorState": { "alias": "errorState"; "required": false; }; "userAriaDescribedBy": { "alias": "userAriaDescribedBy"; "required": false; }; "minDate": { "alias": "minDate"; "required": false; }; "maxDate": { "alias": "maxDate"; "required": false; }; "ranges": { "alias": "ranges"; "required": false; }; "comparisonRange": { "alias": "comparisonRange"; "required": false; "isSignal": true; }; }, { "asyncDateFilterLoading": "asyncDateFilterLoading"; "asyncDateFilterError": "asyncDateFilterError"; "naturalLanguageResolved": "naturalLanguageResolved"; "aiPromptSubmitted": "aiPromptSubmitted"; "invalidRange": "invalidRange"; "timezoneChange": "timezoneChange"; "valueChange": "valueChange"; "action": "action"; "validationError": "validationError"; }, never, never, true, never>;
static ngAcceptInputType_changeActiveMonthOnSelection: unknown;
static ngAcceptInputType_showWeekNumbers: unknown;
}
/**
* Wrapper NgModule for the standalone datepicker component.
* Use this in your `imports` array if you see NG1010 ("imports must be an array...
* Value could not be determined statically") when using the Angular compiler plugin
* or in strict AOT builds.
*
* @example
* ```typescript
* import { NgxsmkDatepickerModule } from 'ngxsmk-datepicker';
*
* @Component({
* standalone: true,
* imports: [NgxsmkDatepickerModule], // single static reference
* template: '<ngxsmk-datepicker></ngxsmk-datepicker>'
* })
* export class MyComponent {}
* ```
*
* For NgModule-based apps:
* ```typescript
* @NgModule({
* imports: [NgxsmkDatepickerModule],
* exports: [NgxsmkDatepickerModule]
* })
* export class MyFeatureModule {}
* ```
*/
declare class NgxsmkDatepickerModule {
static ɵfac: i0.ɵɵFactoryDeclaration<NgxsmkDatepickerModule, never>;
static ɵmod: i0.ɵɵNgModuleDeclaration<NgxsmkDatepickerModule, never, [typeof NgxsmkDatepickerComponent], [typeof NgxsmkDatepickerComponent]>;
static ɵinj: i0.ɵɵInjectorDeclaration<NgxsmkDatepickerModule>;
}
interface ExportOptions {
includeTime?: boolean;
dateFormat?: string;
timezone?: string;
csvHeaders?: string[];
}
declare function exportToJson(value: DatepickerValue, options?: ExportOptions): string;
declare function importFromJson(jsonString: string): DatepickerValue;
declare function exportToCsv(value: DatepickerValue, options?: ExportOptions): string;
declare function importFromCsv(csvString: string): DatepickerValue;
declare function exportToIcs(value: DatepickerValue, options?: ExportOptions & {
summary?: string;
description?: string;
location?: string;
}): string;
declare function importFromIcs(icsString: string): DatepickerValue;
/**
* Day-of-month of `date` in the given calendar system, using the locale's
* numbering system (e.g. `'20'` for 2026-07-11 in the Persian calendar).
* Returns '' when the environment does not support the calendar.
*/
declare function getSecondaryDayLabel(date: Date, calendar: CalendarSystem, locale?: string): string;
/**
* Full date of `date` in the given calendar system (e.g. `'Tir 20, 1405 AP'`).
* Useful for tooltips and ARIA descriptions. Returns '' when unsupported.
*/
declare function formatDateInCalendarSystem(date: Date, calendar: CalendarSystem, locale?: string): string;
/**
* Format a date with timezone support
* @param date The date to format
* @param locale The locale for formatting
* @param options Intl.DateTimeFormatOptions
* @param timezone Optional timezone (IANA timezone name, e.g., 'America/New_York', 'UTC', 'Europe/London')
* @returns Formatted date string
*/
declare function formatDateWithTimezone(date: Date, locale: string, options: Intl.DateTimeFormatOptions, timezone?: string): string;
/**
* Parse a date string with timezone awareness
* @param dateString The date string to parse
* @param timezone Optional timezone for parsing (IANA timezone name)
* @returns Date object (always in UTC internally)
*/
declare function parseDateWithTimezone(dateString: string, timezone?: string): Date | null;
/**
* Convert a date from one timezone to another
* @param date The date to convert
* @param fromTimezone Source timezone (IANA name)
* @param _toTimezone Target timezone (IANA name) - currently unused in simplified implementation
* @returns New Date object (still UTC internally, but represents the time in target timezone)
*/
declare function convertTimezone(date: Date, fromTimezone: string, _toTimezone: string): Date;
/**
* Get the current timezone offset in minutes for a given timezone
* @param timezone IANA timezone name
* @param date Optional date to check offset for (defaults to now)
* @returns Offset in minutes from UTC
*/
declare function getTimezoneOffset(timezone: string, date?: Date): number;
/**
* Check if a timezone string is valid
* @param timezone IANA timezone name
* @returns true if valid, false otherwise
*/
declare function isValidTimezone(timezone: string): boolean;
/**
* Service for managing datepicker translations
* Provides default translations for major languages
*/
declare class TranslationRegistryService {
private translations;
constructor();
/**
* Register translations for a locale
*/
register(locale: string, translations: DatepickerTranslations): void;
/**
* Get translations for a locale with fallback support
*/
getTranslations(locale: string): DatepickerTranslations;
/**
* Register default translations for major languages
*/
private registerDefaultTranslations;
/**
* Get English translations (default)
*/
private getEnglishTranslations;
static ɵfac: i0.ɵɵFactoryDeclaration<TranslationRegistryService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<TranslationRegistryService>;
}
/**
* Service for custom date formatting patterns.
* Supports custom patterns beyond standard Angular DatePipe formats.
*
* @remarks
* Supports the following pattern tokens:
* - YYYY: 4-digit year (e.g., 2025)
* - YY: 2-digit year (e.g., 25)
* - MMMM: Full month name (e.g., January)
* - MMM: Abbreviated month name (e.g., Jan)
* - MM: 2-digit month (e.g., 01)
* - M: 1 or 2-digit month (e.g., 1)
* - DDDD: Full weekday name (e.g., Monday)
* - DDD: Abbreviated weekday name (e.g., Mon)
* - DD: 2-digit day (e.g., 05)
* - D: 1 or 2-digit day (e.g., 5)
* - HH: 2-digit hour (24-hour format, e.g., 14)
* - H: 1 or 2-digit hour (24-hour format, e.g., 14)
* - hh: 2-digit hour (12-hour format, e.g., 02)
* - h: 1 or 2-digit hour (12-hour format, e.g., 2)
* - mm: 2-digit minutes (e.g., 05)
* - m: 1 or 2-digit minutes (e.g., 5)
* - ss: 2-digit seconds (e.g., 09)
* - s: 1 or 2-digit seconds (e.g., 9)
* - A/a: AM/PM or am/pm
*/
declare class CustomDateFormatService {
private locale;
private readonly monthNames;
private readonly weekdayNames;
constructor(locale?: string);
/**
* Set the locale for formatting
*/
setLocale(locale: string): void;
/**
* Format a date using a custom pattern
*
* @param date - The date to format
* @param pattern - The custom format pattern
* @returns Formatted date string
*/
format(date: Date, pattern: string): string;
/**
* Parse a formatted date string back to a Date object
* Note: This is a best-effort implementation and may not work for all patterns
*
* @param dateString - The date string to parse
* @param pattern - The format pattern used
* @returns Parsed Date object, or null if parsing fails
*/
parse(dateString: string, _pattern: string): Date | null;
private getMonthNames;
private initializeLocaleData;
private getWeekdayNames;
}
/**
* Theme builder service for generating CSS-in-JS styles and managing themes
*/
declare class ThemeBuilderService implements OnDestroy {
private readonly platformId;
private styleElement;
private scopedStyleElements;
/**
* Map theme color keys to actual CSS variable names
*/
private mapColorKey;
/**
* Map typography keys to actual CSS variable names
*/
private mapTypographyKey;
/**
* Generate CSS variables from a theme object
*/
generateTheme(theme: DatepickerTheme): string;
/**
* Apply theme to a specific element or globally
* @param theme The theme to apply
* @param targetElement Optional specific element to apply theme to. If not provided, applies globally.
*/
applyTheme(theme: DatepickerTheme, targetElement?: HTMLElement): void;
/**
* Apply theme variables directly to all datepicker elements (for global theme)
* Optimized version with batch DOM operations
*/
private applyToElements;
/**
* Generate CSS-in-JS style object (for styled-components, emotion, etc.)
*/
generateStyleObject(theme: DatepickerTheme): Record<string, string>;
/**
* Remove applied theme
* @param targetElement Optional specific element to remove theme from. If not provided, removes from all.
*/
removeTheme(targetElement?: HTMLElement): void;
/**
* Get current theme from CSS variables
*/
getCurrentTheme(selector?: string): Partial<DatepickerTheme>;
/**
* Clean up all themes and resources when service is destroyed
*/
cleanupAllThemes(): void;
ngOnDestroy(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<ThemeBuilderService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<ThemeBuilderService>;
}
/**
* Datepicker theme interface
*/
interface DatepickerTheme {
colors?: {
primary?: string;
secondary?: string;
background?: string;
surface?: string;
text?: string;
textSecondary?: string;
border?: string;
hover?: string;
active?: string;
disabled?: string;
error?: string;
[key: string]: string | undefined;
};
spacing?: {
xs?: string;
sm?: string;
md?: string;
lg?: string;
xl?: string;
[key: string]: string | undefined;
};
typography?: {
fontFamily?: string;
fontSize?: string;
fontWeight?: string;
lineHeight?: string;
[key: string]: string | undefined;
};
borderRadius?: {
sm?: string;
md?: string;
lg?: string;
full?: string;
[key: string]: string | undefined;
};
shadows?: {
sm?: string;
md?: string;
lg?: string;
/** Focus ring (e.g. input focus). Use a color-mix or rgba for 15% opacity. Example: 0 0 0 3px color-mix(in srgb, var(--datepicker-primary-color) 15%, transparent) */
focus?: string;
[key: string]: string | undefined;
};
}
/**
* Date preset interface
*/
interface DatePreset {
id: string;
name: string;
value: DatepickerValue;
createdAt: Date;
updatedAt: Date;
category?: string;
description?: string;
}
/**
* Service for managing date presets with localStorage persistence
*/
declare class DatePresetsService {
private readonly platformId;
private readonly storageKey;
private presets;
constructor();
/**
* Save a date preset
*/
savePreset(preset: Omit<DatePreset, 'id' | 'createdAt' | 'updatedAt'>): DatePreset;
/**
* Update an existing preset
*/
updatePreset(id: string, updates: Partial<Omit<DatePreset, 'id' | 'createdAt'>>): DatePreset | null;
/**
* Get a preset by ID
*/
getPreset(id: string): DatePreset | null;
/**
* Get all presets
*/
getAllPresets(): DatePreset[];
/**
* Get presets by category
*/
getPresetsByCategory(category: string): DatePreset[];
/**
* Get all categories
*/
getCategories(): string[];
/**
* Delete a preset
*/
deletePreset(id: string): boolean;
/**
* Clear all presets
*/
clearPresets(): void;
/**
* Apply a preset value (returns the value, not the preset object)
*/
applyPreset(id: string): DatepickerValue | null;
/**
* Check if a preset exists
*/
hasPreset(id: string): boolean;
/**
* Get preset count
*/
getPresetCount(): number;
/**
* Export presets to JSON
*/
exportPresets(): string;
/**
* Import presets from JSON
*/
importPresets(jsonString: string, merge?: boolean): {
imported: number;
errors: number;
};
private loadPresets;
private persistPresets;
private generateId;
private cloneValue;
private deserializeValue;
static ɵfac: i0.ɵɵFactoryDeclaration<DatePresetsService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<DatePresetsService>;
}
declare class AriaLiveService implements OnDestroy {
private readonly platformId;
private readonly isBrowser;
private politeRegion;
private assertiveRegion;
private politeClearTimeoutId;
private assertiveClearTimeoutId;
private debounceTimeoutId;
private announcementQueue;
private readonly DEBOUNCE_DELAY;
private readonly CLEAR_DELAY;
constructor();
/**
* Announce a message to screen readers with improved timing and queue management
*/
announce(message: string, priority?: 'polite' | 'assertive'): void;
/**
* Process queued announcements, keeping only the most recent for each priority
*/
private processAnnouncementQueue;
/**
* Announce to a specific live region
*/
private announceToRegion;
/**
* Set announcement text and schedule cleanup
*/
private setAnnouncement;
/**
* Create a live region for announcements
*/
private createLiveRegion;
ngOnDestroy(): void;
destroy(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<AriaLiveService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<AriaLiveService>;
}
declare class FocusTrapService implements OnDestroy {
private activeTraps;
private focusableSelectors;
/**
* Trap focus within an element and restore focus on cleanup
*/
trapFocus(elementRef: ElementRef<HTMLElement>): () => void;
/**
* Remove focus trap and restore previous focus
*/
private removeFocusTrap;
private getFirstFocusable;
private getLastFocusable;
ngOnDestroy(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<FocusTrapService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<FocusTrapService>;
}
declare class HapticFeedbackService {
private readonly platformId;
private readonly isBrowser;
private readonly isSupported;
/**
* Trigger light haptic feedback (short vibration)
*/
light(): void;
selection(): void;
medium(): void;
heavy(): void;
/**
* Trigger custom vibration pattern
* @param pattern Vibration pattern in milliseconds
*/
custom(pattern: number | number[]): void;
static ɵfac: i0.ɵɵFactoryDeclaration<HapticFeedbackService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<HapticFeedbackService>;
}
interface OverlayPositionOptions {
alignment?: 'left' | 'right' | 'center';
offset?: number;
flipOnCollision?: boolean;
minWidth?: number;
viewportWidth?: number;
viewportHeight?: number;
}
interface CalculatedOverlayPosition {
top: number;
left: number;
placement: 'bottom' | 'top';
width?: number;
}
declare class DatepickerOverlayService {
private readonly platformId;
private readonly isBrowser;
/**
* Calculates the best position for an overlay popover relative to a trigger anchor.
* Handles viewport collision detection and automatic top/bottom flip.
*/
calculatePosition(anchor: HTMLElement | DOMRect, popover: HTMLElement | {
width: number;
height: number;
}, options?: OverlayPositionOptions): CalculatedOverlayPosition;
/**
* Applies calculated position styles to a target popover element.
*/
applyPosition(popover: HTMLElement, position: CalculatedOverlayPosition, appendToBody?: boolean): void;
static ɵfac: i0.ɵɵFactoryDeclaration<DatepickerOverlayService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<DatepickerOverlayService>;
}
type DynamicDateInput = Date | string | null | undefined | (() => Date | string | null | undefined);
/**
* Validates that the selected date or date range is on or after the specified minimum date.
*
* @param minDate - The minimum date bound, as a Date, string, or function returning a Date/string.
* @returns An Angular ValidatorFn.
*
* @example
* ```typescript
* const control = new FormControl(new Date(), ngxsmkMinDateValidator(new Date(2025, 0, 1)));
* ```
*/
declare function ngxsmkMinDateValidator(minDate: DynamicDateInput): ValidatorFn;
/**
* Validates that the selected date or date range is on or before the specified maximum date.
*
* @param maxDate - The maximum date bound, as a Date, string, or function returning a Date/string.
* @returns An Angular ValidatorFn.
*
* @example
* ```typescript
* const control = new FormControl(new Date(), ngxsmkMaxDateValidator(new Date(2026, 11, 31)));
* ```
*/
declare function ngxsmkMaxDateValidator(maxDate: DynamicDateInput): ValidatorFn;
interface DateRangeValidatorOptions {
/** Minimum number of days in the range (inclusive). */
minDays?: number;
/** Maximum number of days in the range (inclusive). */
maxDays?: number;
/** When true, validates that both start and end dates are selected. */
requireBoth?: boolean;
}
/**
* Validates range constraints (minimum duration, maximum duration, and complete range requirements).
*
* @param options - Configuration options for range validation.
* @returns An Angular ValidatorFn.
*
* @example
* ```typescript
* const control = new FormControl(null, ngxsmkDateRangeValidator({ minDays: 2, maxDays: 14, requireBoth: true }));
* ```
*/
declare function ngxsmkDateRangeValidator(options?: DateRangeValidatorOptions): ValidatorFn;
/**
* Validates that the selected date (or dates within a range/multiple selection) does not fall on a blocked or disabled date.
*
* @param blocked - Array of blocked dates/strings, or a predicate function returning true if date is blocked.
* @returns An Angular ValidatorFn.
*
* @example
* ```typescript
* const control = new FormControl(null, ngxsmkBlockedDatesValidator([new Date(2026, 11, 25)]));
* ```
*/
declare function ngxsmkBlockedDatesValidator(blocked: (Date | string)[] | ((date: Date) => boolean)): ValidatorFn;
export { AriaLiveService, CustomDateFormatService, CustomSelectComponent, DATEPICKER_CONFIG, DEFAULT_ANIMATION_CONFIG, DEFAULT_DATEPICKER_CONFIG, DatePresetsService, DatepickerOverlayService, DefaultTranslationService, FieldSyncService, FocusTrapService, HapticFeedbackService, LocaleRegistryService, NativeDateAdapter, NgxsmkDatepickerComponent, NgxsmkDatepickerModule, ThemeBuilderService, TranslationRegistryService, addMonths, convertTimezone, exportToCsv, exportToIcs, exportToJson, formatDateInCalendarSystem, formatDateWithTimezone, formatLocaleNumber, generateDecadeGrid, generateMonthOptions, generateTimeOptions, generateWeekDays, generateYearGrid, generateYearOptions, get24Hour, getEndOfDay, getEndOfMonth, getFirstDayOfWeek, getISOWeekNumber, getSecondaryDayLabel, getStartOfDay, getStartOfMonth, getTimezoneOffset, importFromCsv, importFromIcs, importFromJson, isSameDay, isValidTimezone, ngxsmkBlockedDatesValidator, ngxsmkDateRangeValidator, ngxsmkMaxDateValidator, ngxsmkMinDateValidator, normalizeDate, parseDateWithTimezone, processDateRanges, provideDatepickerConfig, subtractDays, update12HourState };
export type { AiDateResolver, AnimationConfig, CalculatedOverlayPosition, CalendarSystem, DateAdapter, DateFormatHook, DateInput, DatePreset, DateRange, DateRangeValidatorOptions, DatepickerConfig, DatepickerHooks, DatepickerTheme, DatepickerTranslations, DatepickerValue, DayCellRenderHook, DayMetadata, DayMetadataProvider, DynamicDateInput, EventHook, ExportOptions, HolidayProvider, KeyboardShortcutContext, KeyboardShortcutHelp, KeyboardShortcutHook, LocaleData, OverlayPositionOptions, PartialDatepickerTranslations, SignalFormField, SignalFormFieldConfig, TranslationService, ValidationHook };