ngxsmk-datepicker
Version:
<!-- SEO Keywords: Angular DatePicker, Angular Date Range Picker, Lightweight Calendar Component, Angular Signals DatePicker, SSR Ready DatePicker, Zoneless Angular, A11y DatePicker, Mobile-Friendly DatePicker, Ionic DatePicker Meta Description: The m
1,138 lines (1,123 loc) • 106 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 } from '@angular/forms';
import { Subject } 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;
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;
}[];
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;
rangeSelected: string;
monthChanged: string;
yearChanged: string;
calendarLoading: string;
calendarReady: string;
keyboardShortcuts: string;
invalidDateFormat: string;
dateBeforeMin: string;
dateAfterMax: string;
invalidDate: string;
}
/**
* Partial translations - allows overriding only specific keys
*/
type PartialDatepickerTranslations = Partial<DatepickerTranslations>;
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[];
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;
boundIsDateDisabled: (date: Date | null) => boolean;
boundIsSameDay: (date1: Date | null, date2: Date | null) => boolean;
boundIsHoliday: (date: Date | null) => boolean;
boundIsMultipleSelected: (date: Date | null) => boolean;
boundIsInRange: (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>;
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;
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; }; "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; }; "boundIsDateDisabled": { "alias": "boundIsDateDisabled"; "required": false; }; "boundIsSameDay": { "alias": "boundIsSameDay"; "required": false; }; "boundIsHoliday": { "alias": "boundIsHoliday"; "required": false; }; "boundIsMultipleSelected": { "alias": "boundIsMultipleSelected"; "required": false; }; "boundIsInRange": { "alias": "boundIsInRange"; "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; }; }, { "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"; }, 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'): 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';
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'): 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';
}
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";
};
};
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;
}
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: any, targetCmp?: any): void;
_uniqueId: string;
mode: 'single' | 'range' | 'multiple' | 'week' | 'month' | 'quarter' | 'year' | 'timeRange';
calendarViewMode: 'month' | 'year' | 'decade' | 'timeline' | 'time-slider';
isInvalidDate: (date: Date) => boolean;
showRanges: boolean;
showTime: boolean;
timeOnly: boolean;
timeRangeMode: boolean;
showCalendarButton: boolean;
minuteInterval: number;
use24Hour: boolean;
secondInterval: number;
showSeconds: boolean;
holidayProvider: HolidayProvider | null;
disableHolidays: boolean;
disabledDates: (string | Date)[];
disabledRanges: Array<{
start: Date | string;
end: Date | string;
}>;
recurringPattern?: RecurringPatternInput;
dateTemplate: TemplateRef<unknown> | null;
private _placeholder;
set placeholder(value: string | null);
get placeholder(): string;
inline: boolean | 'always' | 'auto';
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;
hooks: DatepickerHooks | null;
enableKeyboardShortcuts: boolean;
customShortcuts: {
[key: string]: (context: KeyboardShortcutContext) => boolean;
} | null;
autoApplyClose: 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).
*/
allowSameDay: boolean;
displayFormat?: string;
allowTyping: boolean;
private _calendarCount;
set calendarCount(value: number);
get calendarCount(): number;
calendarLayout: 'horizontal' | 'vertical' | 'auto';
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>
* ```
*/
syncScroll: {
enabled?: boolean;
monthGap?: number;
};
align: 'left' | 'right' | 'center';
useNativePicker: boolean;
enableHapticFeedback: boolean;
mobileModalStyle: 'bottom-sheet' | 'center' | 'fullscreen';
mobileTimePickerStyle: 'wheel' | 'slider' | 'native';
enablePullToRefresh: boolean;
mobileTheme: 'compact' | 'comfortable' | 'spacious';
enableVoiceInput: boolean;
autoDetectMobile: boolean;
disableFocusTrap: boolean;
appendToBody: 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;
private readonly _isCalendarOpen;
get isCalendarOpen(): boolean;
set isCalendarOpen(value: boolean);
private isOpeningCalendar;
/** 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;
classes?: 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;
valueChange: EventEmitter<DatepickerValue>;
action: EventEmitter<{
type: string;
payload?: unknown;
}>;
/** Emitted when validation fails (e.g. invalid typed date, date before min, after max). Message is translated. */
validationError: EventEmitter<{
code: string;
message: string;
}>;
private _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 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<{
label: string;
value: number;
}[]>;
yearOptions: i0.Signal<{
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;
readonly ngControl: NgControl | null;
private readonly isBrowser;
private readonly dateComparator;
constructor();
typedInputValue: 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
* @r