ngxsmk-datepicker
Version:
Lightweight, accessible date and range picker for Angular 17+. Standalone component, Signals, SSR and zoneless-ready, Luxon-based i18n and timezones.
16,215 lines • 1.08 MB
JavaScript
import * as i0 from '@angular/core';
import { EventEmitter, ViewChild, Output, Input, ChangeDetectionStrategy, Component, inject, ElementRef, PLATFORM_ID, HostListener, ViewEncapsulation, signal, computed, input, output, InjectionToken, Injector, runInInjectionContext, effect, isDevMode, Injectable, forwardRef, ApplicationRef, ChangeDetectorRef, booleanAttribute, HostBinding, NgModule } from '@angular/core';
import { NgClass, DOCUMENT, isPlatformBrowser, NgTemplateOutlet, DatePipe } from '@angular/common';
import * as i1 from '@angular/forms';
import { FormsModule, NgControl } from '@angular/forms';
import { Subject, isObservable, firstValueFrom } from 'rxjs';
function getStartOfDay(d) {
const start = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 12, 0, 0, 0);
start.setHours(0, 0, 0, 0);
return start;
}
function getEndOfDay(d) {
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59, 999);
}
function addMonths(d, months) {
const newDate = new Date(d);
const originalDay = d.getDate();
newDate.setMonth(d.getMonth() + months);
// Check for overflow (e.g., Jan 31 + 1 month -> March 3).
// If the date changed, it means the target month didn't have enough days.
if (newDate.getDate() !== originalDay) {
// Set to the last day of the previous month (which is the target month)
newDate.setDate(0);
}
return newDate;
}
function subtractDays(d, days) {
const newDate = new Date(d);
newDate.setDate(d.getDate() - days);
return newDate;
}
function getStartOfMonth(d) {
return new Date(d.getFullYear(), d.getMonth(), 1);
}
function getEndOfMonth(d) {
const lastDay = new Date(d.getFullYear(), d.getMonth() + 1, 0);
return getEndOfDay(lastDay);
}
/**
* 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.
*/
function getISOWeekNumber(d) {
const date = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
// Shift to the Thursday of this ISO week (ISO day: Mon=1..Sun=7)
const isoDay = date.getUTCDay() || 7;
date.setUTCDate(date.getUTCDate() + 4 - isoDay);
const yearStart = Date.UTC(date.getUTCFullYear(), 0, 1);
return Math.ceil(((date.getTime() - yearStart) / 86400000 + 1) / 7);
}
function getStartOfWeek(d, firstDayOfWeek = 0) {
const date = new Date(d);
const day = date.getDay();
const diff = (day < firstDayOfWeek ? 7 : 0) + day - firstDayOfWeek;
date.setDate(date.getDate() - diff);
return getStartOfDay(date);
}
function getEndOfWeek(d, firstDayOfWeek = 0) {
const startOfWeek = getStartOfWeek(d, firstDayOfWeek);
const endOfWeek = new Date(startOfWeek);
endOfWeek.setDate(endOfWeek.getDate() + 6);
return getEndOfDay(endOfWeek);
}
function getStartOfQuarter(d) {
const quarter = Math.floor(d.getMonth() / 3);
return new Date(d.getFullYear(), quarter * 3, 1);
}
function getEndOfQuarter(d) {
const quarter = Math.floor(d.getMonth() / 3);
return new Date(d.getFullYear(), (quarter + 1) * 3, 0);
}
function getStartOfYear(d) {
return new Date(d.getFullYear(), 0, 1);
}
function getEndOfYear(d) {
return new Date(d.getFullYear(), 11, 31);
}
function isSameDay(d1, d2) {
if (!d1 || !d2)
return false;
return d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth() && d1.getDate() === d2.getDate();
}
function normalizeDate(date) {
if (date === null || date === undefined || date === '')
return null;
const d = date instanceof Date
? new Date(date.getTime())
: new Date(date.toDate
? date.toDate()
: date);
if (isNaN(d.getTime()))
return null;
return d;
}
/**
* 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
*/
function formatDateWithTimezone(date, locale, options, timezone) {
if (timezone) {
const formatter = new Intl.DateTimeFormat(locale, {
...options,
timeZone: timezone,
});
return formatter.format(date);
}
return date.toLocaleString(locale, options);
}
/**
* 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)
*/
function parseDateWithTimezone(dateString, timezone) {
if (!dateString)
return null;
if (timezone) {
try {
const date = new Date(dateString);
if (isNaN(date.getTime())) {
return null;
}
return date;
}
catch {
return null;
}
}
const date = new Date(dateString);
return isNaN(date.getTime()) ? null : date;
}
/**
* 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)
*/
function convertTimezone(date, fromTimezone, _toTimezone) {
const fromFormatter = new Intl.DateTimeFormat('en-US', {
timeZone: fromTimezone,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
});
const parts = fromFormatter.formatToParts(date);
const year = parseInt(parts.find((p) => p.type === 'year')?.value || '0');
const month = parseInt(parts.find((p) => p.type === 'month')?.value || '0') - 1;
const day = parseInt(parts.find((p) => p.type === 'day')?.value || '0');
const hour = parseInt(parts.find((p) => p.type === 'hour')?.value || '0');
const minute = parseInt(parts.find((p) => p.type === 'minute')?.value || '0');
const second = parseInt(parts.find((p) => p.type === 'second')?.value || '0');
const dateString = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}T${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}:${String(second).padStart(2, '0')}`;
const result = new Date(dateString);
result.setMilliseconds(date.getMilliseconds());
return result;
}
/**
* 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
*/
function getTimezoneOffset(timezone, date = new Date()) {
try {
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: timezone,
timeZoneName: 'longOffset',
});
const parts = formatter.formatToParts(date);
const offsetPart = parts.find((p) => p.type === 'timeZoneName');
if (offsetPart) {
const offsetStr = offsetPart.value.replace('GMT', '').trim();
const sign = offsetStr[0] === '-' ? -1 : 1;
const [hours = 0, minutes = 0] = offsetStr.slice(1).split(':').map(Number);
return sign * (hours * 60 + minutes);
}
}
catch { }
return date.getTimezoneOffset();
}
/**
* Check if a timezone string is valid
* @param timezone IANA timezone name
* @returns true if valid, false otherwise
*/
function isValidTimezone(timezone) {
if (!timezone)
return false;
try {
Intl.DateTimeFormat(undefined, { timeZone: timezone });
return true;
}
catch {
return false;
}
}
function generateRecurringDates(config) {
const dates = [];
const interval = config.interval || 1;
const startDate = getStartOfDay(config.startDate);
const endDate = config.endDate ? getEndOfDay(config.endDate) : null;
const currentDate = new Date(startDate);
let count = 0;
const maxOccurrences = config.occurrences || (endDate ? 365 : 10);
switch (config.pattern) {
case 'daily': {
while (count < maxOccurrences) {
if (endDate && currentDate > endDate)
break;
dates.push(new Date(currentDate));
currentDate.setDate(currentDate.getDate() + interval);
count++;
}
break;
}
case 'weekly': {
if (config.dayOfWeek === undefined) {
const targetDay = startDate.getDay();
while (count < maxOccurrences) {
if (endDate && currentDate > endDate)
break;
const dayOfWeek = currentDate.getDay();
const daysUntilTarget = (targetDay - dayOfWeek + 7) % 7;
if (daysUntilTarget === 0 && count === 0) {
dates.push(new Date(currentDate));
count++;
currentDate.setDate(currentDate.getDate() + 7 * interval);
}
else {
currentDate.setDate(currentDate.getDate() + daysUntilTarget);
dates.push(new Date(currentDate));
count++;
currentDate.setDate(currentDate.getDate() + 7 * interval);
}
}
}
else {
while (count < maxOccurrences) {
if (endDate && currentDate > endDate)
break;
const dayOfWeek = currentDate.getDay();
const daysUntilTarget = (config.dayOfWeek - dayOfWeek + 7) % 7;
if (daysUntilTarget === 0 && count === 0) {
dates.push(new Date(currentDate));
count++;
currentDate.setDate(currentDate.getDate() + 7 * interval);
}
else {
currentDate.setDate(currentDate.getDate() + daysUntilTarget);
dates.push(new Date(currentDate));
count++;
currentDate.setDate(currentDate.getDate() + 7 * interval);
}
}
}
break;
}
case 'monthly': {
const targetDay = config.dayOfMonth || startDate.getDate();
while (count < maxOccurrences) {
if (endDate && currentDate > endDate)
break;
const lastDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0).getDate();
const dayToSet = Math.min(targetDay, lastDayOfMonth);
currentDate.setDate(dayToSet);
dates.push(new Date(currentDate));
count++;
currentDate.setMonth(currentDate.getMonth() + interval);
}
break;
}
case 'yearly': {
const monthDay = config.monthAndDay || { month: startDate.getMonth(), day: startDate.getDate() };
while (count < maxOccurrences) {
if (endDate && currentDate > endDate)
break;
currentDate.setMonth(monthDay.month);
currentDate.setDate(monthDay.day);
dates.push(new Date(currentDate));
count++;
currentDate.setFullYear(currentDate.getFullYear() + interval);
}
break;
}
case 'weekdays':
while (count < maxOccurrences) {
if (endDate && currentDate > endDate)
break;
const dayOfWeek = currentDate.getDay();
if (dayOfWeek >= 1 && dayOfWeek <= 5) {
dates.push(new Date(currentDate));
count++;
}
currentDate.setDate(currentDate.getDate() + 1);
}
break;
case 'weekends':
while (count < maxOccurrences) {
if (endDate && currentDate > endDate)
break;
const dayOfWeek = currentDate.getDay();
if (dayOfWeek === 0 || dayOfWeek === 6) {
dates.push(new Date(currentDate));
count++;
}
currentDate.setDate(currentDate.getDate() + 1);
}
break;
default:
break;
}
return dates;
}
function matchesRecurringPattern(date, config) {
const generatedDates = generateRecurringDates(config);
const dateTime = getStartOfDay(date).getTime();
return generatedDates.some((d) => getStartOfDay(d).getTime() === dateTime);
}
function generateMonthOptions(locale, year) {
return Array.from({ length: 12 }).map((_, i) => ({
label: new Date(year, i, 1).toLocaleDateString(locale, { month: 'long' }),
value: i,
}));
}
/**
* 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
*/
function formatLocaleNumber(value, locale, options) {
try {
const formatter = new Intl.NumberFormat(locale, {
useGrouping: true,
minimumIntegerDigits: 1,
...options,
});
return formatter.format(value);
}
catch {
// Fallback for invalid locale
return String(value);
}
}
function generateYearOptions(currentYear, range = 10) {
const startYear = currentYear - range;
const endYear = currentYear + range;
const options = [];
for (let i = startYear; i <= endYear; i++) {
// Years should not use thousand separators (e.g., "2026" not "2,026")
const label = `${i}`;
options.push({ label, value: i });
}
return options;
}
function generateTimeOptions(minuteInterval = 1, secondInterval = 1, includeSeconds = false, use24Hour = false) {
const hourOptions = use24Hour
? Array.from({ length: 24 }).map((_, i) => ({
label: i.toString().padStart(2, '0'),
value: i,
}))
: Array.from({ length: 12 }).map((_, i) => ({
label: (i + 1).toString().padStart(2, '0'),
value: i + 1,
}));
const minuteOptions = [];
for (let i = 0; i < 60; i += minuteInterval) {
minuteOptions.push({
label: i.toString().padStart(2, '0'),
value: i,
});
}
const result = { hourOptions, minuteOptions };
if (includeSeconds) {
const secondOptions = [];
for (let i = 0; i < 60; i += secondInterval) {
secondOptions.push({
label: i.toString().padStart(2, '0'),
value: i,
});
}
result.secondOptions = secondOptions;
}
return result;
}
function generateWeekDays(locale, firstDayOfWeek = 0) {
const day = new Date(2024, 0, 7 + firstDayOfWeek);
return Array.from({ length: 7 }).map(() => {
const weekDay = new Date(day).toLocaleDateString(locale, {
weekday: 'short',
});
day.setDate(day.getDate() + 1);
return weekDay;
});
}
function generateWeekDaysFull(locale, firstDayOfWeek = 0) {
const day = new Date(2024, 0, 7 + firstDayOfWeek);
return Array.from({ length: 7 }).map(() => {
const weekDay = new Date(day).toLocaleDateString(locale, {
weekday: 'long',
});
day.setDate(day.getDate() + 1);
return weekDay;
});
}
function getFirstDayOfWeek(locale) {
try {
const intlExt = Intl;
if (typeof intlExt !== 'undefined' && typeof intlExt.Locale !== 'undefined') {
const localeObj = new intlExt.Locale(locale);
if ('weekInfo' in localeObj && localeObj.weekInfo?.firstDay !== undefined) {
return localeObj.weekInfo.firstDay % 7;
}
}
const localeLower = locale.toLowerCase();
if (localeLower.startsWith('en-gb') ||
localeLower.startsWith('en-au') ||
localeLower.startsWith('en-nz') ||
localeLower.startsWith('de') ||
localeLower.startsWith('fr') ||
localeLower.startsWith('es') ||
localeLower.startsWith('it') ||
localeLower.startsWith('pt') ||
localeLower.startsWith('nl') ||
localeLower.startsWith('pl') ||
localeLower.startsWith('ru') ||
localeLower.startsWith('sv') ||
localeLower.startsWith('no') ||
localeLower.startsWith('da') ||
localeLower.startsWith('fi')) {
return 1; // Monday
}
// Default to Sunday for en-US and other locales
return 0;
}
catch {
// If locale parsing fails, default based on locale string
const localeLower = locale.toLowerCase();
if (localeLower.startsWith('en-gb') || localeLower.startsWith('en-au') || localeLower.startsWith('en-nz')) {
return 1; // Monday
}
return 0; // Sunday (default for en-US and others)
}
}
function get24Hour(displayHour, isPm) {
if (isPm) {
return displayHour === 12 ? 12 : displayHour + 12;
}
return displayHour === 12 ? 0 : displayHour;
}
function update12HourState(fullHour) {
return {
isPm: fullHour >= 12,
displayHour: fullHour % 12 || 12,
};
}
function processDateRanges(ranges) {
if (!ranges)
return null;
return Object.entries(ranges).reduce((acc, [key, dates]) => {
const start = normalizeDate(dates[0]);
const end = normalizeDate(dates[1]);
if (start && end)
acc[key] = [start, end];
return acc;
}, {});
}
function generateYearGrid(currentYear) {
const startYear = Math.floor(currentYear / 10) * 10 - 1;
const years = [];
for (let i = 0; i < 12; i++) {
years.push(startYear + i);
}
return years;
}
function generateDecadeGrid(currentDecade) {
const decades = [];
for (let i = 0; i < 12; i++) {
decades.push(currentDecade + i * 10 - 10);
}
return decades;
}
/**
* Generate a large year range for virtual scrolling (100 years centered on current)
*/
function generateLargeYearRange$1(centerYear, range = 100) {
const startYear = centerYear - Math.floor(range / 2);
const years = [];
for (let i = 0; i < range; i++) {
years.push(startYear + i);
}
return years;
}
/**
* Generate a large decade range for virtual scrolling (50 decades centered on current)
*/
function generateLargeDecadeRange$1(centerDecade, range = 50) {
const startDecade = centerDecade - Math.floor(range / 2) * 10;
const decades = [];
for (let i = 0; i < range; i++) {
decades.push(startDecade + i * 10);
}
return decades;
}
class NgxsmkDatepickerInputComponent {
constructor() {
this.isNative = false;
this.disabled = false;
this.classes = undefined;
this.nativeInputType = 'date';
this.formattedValue = '';
this.placeholder = '';
this.id = '';
this.name = '';
this.autocomplete = 'off';
this.required = false;
this.minDateNative = null;
this.maxDateNative = null;
this.ariaLabel = '';
this.ariaDescribedBy = '';
this.errorState = false;
this.clearAriaLabel = '';
this.clearLabel = '';
this.isCalendarOpen = false;
this.allowTyping = false;
this.typedInputValue = '';
this.displayValue = '';
this.showCalendarButton = false;
this.calendarAriaLabel = '';
this.validationErrorMessage = null;
this.nativeInputChange = new EventEmitter();
this.inputBlur = new EventEmitter();
this.clearValue = new EventEmitter();
this.toggleCalendar = new EventEmitter();
this.pointerDown = new EventEmitter();
this.pointerUp = new EventEmitter();
this.inputGroupFocus = new EventEmitter();
this.inputKeyDown = new EventEmitter();
this.inputChange = new EventEmitter();
this.inputFocus = new EventEmitter();
}
focus() {
if (this.isNative) {
this.nativeInput?.nativeElement.focus();
}
else {
this.customInput?.nativeElement.focus();
}
}
onNativeInputChange(event) {
this.nativeInputChange.emit(event);
}
onInputBlur(event) {
this.inputBlur.emit(event);
}
onClearValue(event) {
event.stopPropagation();
this.clearValue.emit(event);
}
onToggleCalendar(event) {
this.toggleCalendar.emit(event);
}
onPointerDown(event) {
this.pointerDown.emit(event);
}
onPointerUp(event) {
this.pointerUp.emit(event);
}
onInputGroupFocus() {
this.inputGroupFocus.emit();
}
onInputKeyDown(event) {
this.inputKeyDown.emit(event);
}
onInputChange(event) {
this.inputChange.emit(event);
}
onInputFocus(event) {
this.inputFocus.emit(event);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NgxsmkDatepickerInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: NgxsmkDatepickerInputComponent, isStandalone: true, selector: "ngxsmk-datepicker-input", inputs: { isNative: "isNative", disabled: "disabled", classes: "classes", nativeInputType: "nativeInputType", formattedValue: "formattedValue", placeholder: "placeholder", id: "id", name: "name", autocomplete: "autocomplete", required: "required", minDateNative: "minDateNative", maxDateNative: "maxDateNative", ariaLabel: "ariaLabel", ariaDescribedBy: "ariaDescribedBy", errorState: "errorState", clearAriaLabel: "clearAriaLabel", clearLabel: "clearLabel", isCalendarOpen: "isCalendarOpen", allowTyping: "allowTyping", typedInputValue: "typedInputValue", displayValue: "displayValue", showCalendarButton: "showCalendarButton", calendarAriaLabel: "calendarAriaLabel", validationErrorMessage: "validationErrorMessage" }, outputs: { nativeInputChange: "nativeInputChange", inputBlur: "inputBlur", clearValue: "clearValue", toggleCalendar: "toggleCalendar", pointerDown: "pointerDown", pointerUp: "pointerUp", inputGroupFocus: "inputGroupFocus", inputKeyDown: "inputKeyDown", inputChange: "inputChange", inputFocus: "inputFocus" }, viewQueries: [{ propertyName: "nativeInput", first: true, predicate: ["nativeInput"], descendants: true }, { propertyName: "customInput", first: true, predicate: ["customInput"], descendants: true }], ngImport: i0, template: `
@if (isNative) {
<div
class="ngxsmk-input-group ngxsmk-native-input-group"
[class.disabled]="disabled"
[ngClass]="classes?.inputGroup"
>
<input
[type]="nativeInputType"
#nativeInput
[value]="formattedValue"
[placeholder]="placeholder"
[id]="id"
[name]="name"
[autocomplete]="autocomplete"
[disabled]="disabled"
[required]="required"
[attr.min]="minDateNative"
[attr.max]="maxDateNative"
[attr.aria-label]="ariaLabel"
[attr.aria-required]="required"
[attr.aria-invalid]="errorState"
[attr.aria-describedby]="ariaDescribedBy"
class="ngxsmk-display-input ngxsmk-native-input"
[ngClass]="classes?.input"
(change)="onNativeInputChange($event)"
(blur)="onInputBlur($event)"
/>
@if (formattedValue) {
<button
type="button"
class="ngxsmk-clear-button"
(click)="onClearValue($event)"
[disabled]="disabled"
[attr.aria-label]="clearAriaLabel"
[title]="clearLabel"
[ngClass]="classes?.clearBtn"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="16" height="16">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
d="M368 368L144 144M368 144L144 368"
/>
</svg>
</button>
}
</div>
} @else {
<div class="ngxsmk-input-and-error">
<div
class="ngxsmk-input-group"
(click)="onToggleCalendar($event)"
(pointerdown)="onPointerDown($event)"
(pointerup)="onPointerUp($event)"
(focus)="onInputGroupFocus()"
(keydown.enter)="onToggleCalendar($event)"
(keydown.space)="onToggleCalendar($event); $event.preventDefault()"
[class.disabled]="disabled"
role="button"
[attr.aria-disabled]="disabled"
aria-haspopup="dialog"
[attr.aria-expanded]="isCalendarOpen"
tabindex="0"
[ngClass]="classes?.inputGroup"
>
<input
type="text"
#customInput
[value]="allowTyping ? typedInputValue || displayValue : displayValue"
[placeholder]="placeholder"
[id]="id"
[name]="name"
[autocomplete]="autocomplete"
[readonly]="!allowTyping"
[disabled]="disabled"
[required]="required"
[attr.aria-label]="ariaLabel"
[attr.aria-required]="required"
[attr.aria-invalid]="errorState"
[attr.aria-describedby]="ariaDescribedBy"
class="ngxsmk-display-input"
[ngClass]="classes?.input"
(keydown.enter)="onInputKeyDown($event)"
(keydown.space)="onInputKeyDown($event)"
(keydown.escape)="onInputKeyDown($event)"
(input)="onInputChange($event)"
(blur)="onInputBlur($event)"
(focus)="onInputFocus($event)"
/>
@if (displayValue) {
<button
type="button"
class="ngxsmk-clear-button"
(click)="onClearValue($event)"
(touchstart)="$event.stopPropagation()"
(touchend)="$event.stopPropagation()"
(pointerdown)="$event.stopPropagation()"
(pointerup)="$event.stopPropagation()"
[disabled]="disabled"
[attr.aria-label]="clearAriaLabel"
[title]="clearLabel"
[ngClass]="classes?.clearBtn"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="16" height="16">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
d="M368 368L144 144M368 144L144 368"
/>
</svg>
</button>
}
@if (showCalendarButton) {
<button
type="button"
class="ngxsmk-calendar-button"
(click)="onToggleCalendar($event); $event.stopPropagation()"
[disabled]="disabled"
[attr.aria-label]="calendarAriaLabel"
[title]="calendarAriaLabel"
[ngClass]="classes?.calendarBtn"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
d="M96 80H416c26.51 0 48 21.49 48 48V416c0 26.51-21.49 48-48 48H96c-26.51 0-48-21.49-48-48V128c0-26.51 21.49-48 48-48zM160 32v64M352 32v64M464 192H48M200 256h112M200 320h112M200 384h112M152 256h.01M152 320h.01M152 384h.01"
/>
</svg>
</button>
}
</div>
@if (validationErrorMessage) {
<div class="ngxsmk-validation-error" role="alert" [attr.aria-live]="'polite'">
{{ validationErrorMessage }}
</div>
}
</div>
}
`, isInline: true, dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NgxsmkDatepickerInputComponent, decorators: [{
type: Component,
args: [{
selector: 'ngxsmk-datepicker-input',
standalone: true,
imports: [NgClass],
template: `
@if (isNative) {
<div
class="ngxsmk-input-group ngxsmk-native-input-group"
[class.disabled]="disabled"
[ngClass]="classes?.inputGroup"
>
<input
[type]="nativeInputType"
#nativeInput
[value]="formattedValue"
[placeholder]="placeholder"
[id]="id"
[name]="name"
[autocomplete]="autocomplete"
[disabled]="disabled"
[required]="required"
[attr.min]="minDateNative"
[attr.max]="maxDateNative"
[attr.aria-label]="ariaLabel"
[attr.aria-required]="required"
[attr.aria-invalid]="errorState"
[attr.aria-describedby]="ariaDescribedBy"
class="ngxsmk-display-input ngxsmk-native-input"
[ngClass]="classes?.input"
(change)="onNativeInputChange($event)"
(blur)="onInputBlur($event)"
/>
@if (formattedValue) {
<button
type="button"
class="ngxsmk-clear-button"
(click)="onClearValue($event)"
[disabled]="disabled"
[attr.aria-label]="clearAriaLabel"
[title]="clearLabel"
[ngClass]="classes?.clearBtn"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="16" height="16">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
d="M368 368L144 144M368 144L144 368"
/>
</svg>
</button>
}
</div>
} @else {
<div class="ngxsmk-input-and-error">
<div
class="ngxsmk-input-group"
(click)="onToggleCalendar($event)"
(pointerdown)="onPointerDown($event)"
(pointerup)="onPointerUp($event)"
(focus)="onInputGroupFocus()"
(keydown.enter)="onToggleCalendar($event)"
(keydown.space)="onToggleCalendar($event); $event.preventDefault()"
[class.disabled]="disabled"
role="button"
[attr.aria-disabled]="disabled"
aria-haspopup="dialog"
[attr.aria-expanded]="isCalendarOpen"
tabindex="0"
[ngClass]="classes?.inputGroup"
>
<input
type="text"
#customInput
[value]="allowTyping ? typedInputValue || displayValue : displayValue"
[placeholder]="placeholder"
[id]="id"
[name]="name"
[autocomplete]="autocomplete"
[readonly]="!allowTyping"
[disabled]="disabled"
[required]="required"
[attr.aria-label]="ariaLabel"
[attr.aria-required]="required"
[attr.aria-invalid]="errorState"
[attr.aria-describedby]="ariaDescribedBy"
class="ngxsmk-display-input"
[ngClass]="classes?.input"
(keydown.enter)="onInputKeyDown($event)"
(keydown.space)="onInputKeyDown($event)"
(keydown.escape)="onInputKeyDown($event)"
(input)="onInputChange($event)"
(blur)="onInputBlur($event)"
(focus)="onInputFocus($event)"
/>
@if (displayValue) {
<button
type="button"
class="ngxsmk-clear-button"
(click)="onClearValue($event)"
(touchstart)="$event.stopPropagation()"
(touchend)="$event.stopPropagation()"
(pointerdown)="$event.stopPropagation()"
(pointerup)="$event.stopPropagation()"
[disabled]="disabled"
[attr.aria-label]="clearAriaLabel"
[title]="clearLabel"
[ngClass]="classes?.clearBtn"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="16" height="16">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
d="M368 368L144 144M368 144L144 368"
/>
</svg>
</button>
}
@if (showCalendarButton) {
<button
type="button"
class="ngxsmk-calendar-button"
(click)="onToggleCalendar($event); $event.stopPropagation()"
[disabled]="disabled"
[attr.aria-label]="calendarAriaLabel"
[title]="calendarAriaLabel"
[ngClass]="classes?.calendarBtn"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
d="M96 80H416c26.51 0 48 21.49 48 48V416c0 26.51-21.49 48-48 48H96c-26.51 0-48-21.49-48-48V128c0-26.51 21.49-48 48-48zM160 32v64M352 32v64M464 192H48M200 256h112M200 320h112M200 384h112M152 256h.01M152 320h.01M152 384h.01"
/>
</svg>
</button>
}
</div>
@if (validationErrorMessage) {
<div class="ngxsmk-validation-error" role="alert" [attr.aria-live]="'polite'">
{{ validationErrorMessage }}
</div>
}
</div>
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
}]
}], propDecorators: { isNative: [{
type: Input
}], disabled: [{
type: Input
}], classes: [{
type: Input
}], nativeInputType: [{
type: Input
}], formattedValue: [{
type: Input
}], placeholder: [{
type: Input
}], id: [{
type: Input
}], name: [{
type: Input
}], autocomplete: [{
type: Input
}], required: [{
type: Input
}], minDateNative: [{
type: Input
}], maxDateNative: [{
type: Input
}], ariaLabel: [{
type: Input
}], ariaDescribedBy: [{
type: Input
}], errorState: [{
type: Input
}], clearAriaLabel: [{
type: Input
}], clearLabel: [{
type: Input
}], isCalendarOpen: [{
type: Input
}], allowTyping: [{
type: Input
}], typedInputValue: [{
type: Input
}], displayValue: [{
type: Input
}], showCalendarButton: [{
type: Input
}], calendarAriaLabel: [{
type: Input
}], validationErrorMessage: [{
type: Input
}], nativeInputChange: [{
type: Output
}], inputBlur: [{
type: Output
}], clearValue: [{
type: Output
}], toggleCalendar: [{
type: Output
}], pointerDown: [{
type: Output
}], pointerUp: [{
type: Output
}], inputGroupFocus: [{
type: Output
}], inputKeyDown: [{
type: Output
}], inputChange: [{
type: Output
}], inputFocus: [{
type: Output
}], nativeInput: [{
type: ViewChild,
args: ['nativeInput']
}], customInput: [{
type: ViewChild,
args: ['customInput']
}] } });
class CustomSelectComponent {
constructor() {
this.options = [];
this.disabled = false;
this.valueChange = new EventEmitter();
this.isOpen = false;
this.elementRef = inject(ElementRef);
this.platformId = inject(PLATFORM_ID);
this.document = inject(DOCUMENT);
this.isBrowser = isPlatformBrowser(this.platformId);
this.resizeObserver = null;
this.scrollListener = null;
}
ngAfterViewInit() {
if (this.isBrowser) {
this.setupResizeObserver();
this.setupScrollListener();
}
}
ngOnDestroy() {
if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
if (this.scrollListener && this.isBrowser) {
window.removeEventListener('scroll', this.scrollListener, true);
}
}
setupResizeObserver() {
if (this.isBrowser && typeof ResizeObserver !== 'undefined') {
this.resizeObserver = new ResizeObserver(() => {
if (this.isOpen) {
this.updatePanelPosition();
}
});
if (this.container?.nativeElement) {
this.resizeObserver.observe(this.container.nativeElement);
}
}
}
setupScrollListener() {
if (this.isBrowser) {
this.scrollListener = () => {
if (this.isOpen) {
// Absolute positioning doesn't need updates on scroll
}
};
window.addEventListener('scroll', this.scrollListener, { passive: true, capture: true });
}
}
updatePanelPosition() {
// No special positioning needed for standard dropdowns
// CSS handles top: 100% + 4px
}
onDocumentClick(event) {
if (this.isBrowser) {
if (event.composedPath && typeof event.composedPath === 'function') {
const path = event.composedPath();
if (!path.includes(this.elementRef.nativeElement)) {
this.isOpen = false;
}
}
else {
const target = event.target;
if (target && !this.elementRef.nativeElement.contains(target)) {
this.isOpen = false;
}
}
}
}
onDocumentTouchStart(event) {
// On mobile, close dropdown when calendar opens
if (!this.isBrowser || !this.isOpen)
return;
const calendarBackdrop = this.document.querySelector('.ngxsmk-backdrop');
if (!calendarBackdrop)
return;
if (event.composedPath && typeof event.composedPath === 'function') {
const path = event.composedPath();
if (!path.includes(this.elementRef.nativeElement)) {
this.isOpen = false;
}
}
else {
const target = event.target;
// Only close if touch is outside the dropdown
if (target && !this.elementRef.nativeElement.contains(target)) {
this.isOpen = false;
}
}
}
get displayValue() {
const selectedOption = this.options.find((opt) => opt.value === this.value);
return selectedOption ? selectedOption.label : '';
}
toggleDropdown() {
if (this.disabled)
return;
this.isOpen = !this.isOpen;
if (this.isOpen) {
setTimeout(() => {
this.updatePanelPosition();
this.scrollToSelected();
}, 0);
}
}
scrollToSelected() {
if (!this.isBrowser || !this.panel?.nativeElement)
return;
const selectedEl = this.panel.nativeElement.querySelector('.selected');
if (selectedEl) {
selectedEl.scrollIntoView({ block: 'nearest', inline: 'nearest' });
}
}
selectOption(option) {
this.value = option.value;
this.valueChange.emit(this.value);
this.isOpen = false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CustomSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: CustomSelectComponent, isStandalone: true, selector: "ngxsmk-custom-select", inputs: { options: "options", value: "value", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:touchstart": "onDocumentTouchStart($event)" }, properties: { "attr.data-open": "isOpen" } }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }, { propertyName: "button", first: true, predicate: ["button"], descendants: true }, { propertyName: "panel", first: true, predicate: ["panel"], descendants: true }], ngImport: i0, template: `
<div
class="ngxsmk-select-container"
[class.is-open]="isOpen"
(click)="toggleDropdown()"
(keydown.enter)="toggleDropdown()"
(keydown.space)="toggleDropdown(); $event.preventDefault()"
tabindex="0"
role="button"
[attr.aria-expanded]="isOpen"
#container
>
<button type="button" class="ngxsmk-select-display" [disabled]="disabled" #button>
<span>{{ displayValue }}</span>
<svg class="ngxsmk-arrow-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="48"
d="M112 184l144 144 144-144"
/>
</svg>
</button>
@if (isOpen) {
<div class="ngxsmk-options-panel" #panel>
<ul>
@for (option of options; track option.value) {
<li
[class.selected]="option.value === value"
[class.disabled]="option.disabled"
(click)="option.disabled ? null : selectOption(option); $event.stopPropagation()"
(keydown.enter)="option.disabled ? null : selectOption(option); $event.stopPropagation()"
(keydown.space)="
option.disabled ? null : selectOption(option); $event.stopPropagation(); $event.preventDefault()
"
[attr.tabindex]="option.disabled ? -1 : 0"
role="option"
[attr.aria-selected]="option.value === value"
[attr.aria-disabled]="option.disabled ? true : null"
>
{{ option.label }}
</li>
}
</ul>
</div>
}
</div>
`, isInline: true, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CustomSelectComponent, decorators: [{
type: Component,
args: [{
selector: 'ngxsmk-custom-select',
standalone: true,
imports: [],
encapsulation: ViewEncapsulation.None,
host: {
'[attr.data-open]': 'isOpen',
},
template: `
<div
class="ngxsmk-select-container"
[class.is-open]="isOpen"
(click)="toggleDropdown()"
(keydown.enter)="toggleDropdown()"
(keydown.space)="toggleDropdown(); $event.preventDefault()"
tabindex="0"
role="button"
[attr.aria-expanded]="isOpen"
#container
>
<button type="button" class="ngxsmk-select-display" [disabled]="disabled" #button>
<span>{{ displayValue }}</span>
<svg class="ngxsmk-arrow-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="48"
d="M112 184l144 144 144-144"
/>
</svg>
</button>
@if (isOpen) {
<div class="ngxsmk-options-panel" #panel>
<ul>
@for (option of options; track option.value) {
<li
[class.selected]="option.value === value"
[class.disabled]="option.disabled"
(click)="option.disabled ? null : selectOption(option); $event.stopPropagation()"
(keydown.enter)="option.disabled ? null : selectOption(option); $event.stopPropagation()"
(keydown.space)="
option.disabled ? null : selectOption(option); $event.stopPropagation(); $event.preventDefault()
"
[attr.tabindex]="option.disabled ? -1 : 0"
role="option"
[attr.aria-selected]="option.value === value"
[attr.aria-disabled]="option.disabled ? true : null"
>
{{ option.label }}
</li>
}
</ul>
</div>
}
</div>
`,
}]
}], propDecorators: { options: [{
type: Input
}], value: [{
type: Input
}], disabled: [{
type: Input
}], valueChange: [{
type: Output
}], container: [{
type: ViewChild,
args: ['container', { static: false }]
}], button: [{
type: ViewChild,
args: ['button', { static: false }]
}], panel: [{
type: ViewChild,
args: ['panel', { static: false }]
}], onDocumentClick: [{
type: HostListener,
args: ['document:click', ['$event']]
}], onDocumentTouchStart: [{
type: HostListener,
args: ['document:touchstart', ['$event']]
}] } });
/**
* The header section of the calendar, containing navigation and selection controls.
*
* Includes:
* - Month selection dropdown
* - Year selection dropdown
* - Previous/Next month navigation buttons
*/
class CalendarHeaderComponent {
constructor() {
this.monthOptions = [];
this.yearOptions = [];
this.currentMonth = 0;
this.currentYear = new Date().getFullYear();
this.disabled = false;
this.isBackArrowDisabled = false;
this.prevMonthAriaLabel = '';
this.nextMonthAriaLabel = '';
this.currentYearChange = new EventEmitter();
this.currentMonthChange = new EventEmitter();
this.previousMonth = new EventEmitter();
this.nextMonth = new EventEmitter();
}
onMonthSelect(value) {
this.currentMonthChange.emit(value);
}
onYearSelect(value) {
this.currentYearChange.emit(value);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CalendarHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.11", type: CalendarHeaderComponent, isStandalone: true, selector: "ngxsmk-calendar-header", inputs: { monthOptions: "monthOptions", yearOptions: "yearOptions", currentMonth: "currentMonth", currentYear: "currentYear", disabled: "disabled", isBackArrowDisabled: "isBackArrowDisabled", prevMonthAriaLabel: "prevMonthAriaLabel", nextMonthAriaLabel: "nextMonthAriaLabel", headerClass: "headerClass", navPrevClass: "navPrevClass", navNextClass: "navNextClass" }, outputs: { currentYearChange: "currentYearChange", currentMonthChange: "currentMonthChange", previousMonth: "previousMonth", nextMonth: "nextMonth" }, viewQueries: [{ propertyName: "monthSelect", first: true, predicate: ["monthSelect"], descendants: true }, { propertyName: "yearSelect", first: true, predicate: ["yearSelect"], descendants: true }], ngImport: i0, template: `
<div class="ngxsmk-header" [ngClass]="headerClass">
<div class="ngxsmk-month-year-selects">
<ngxsmk-custom-select
#monthSelect
class="month-select"
[options]="monthOptions"
[(value)]="currentMonth"
[disabled]="disabled"
(valueChange)="onMonthSelect($event)"
>
</ngxsmk-custom-select>
<ngxsmk-custom-select
#yearSelect
class="year-select"
[options]="yearOptions"
[(value)]="currentYear"
[disabled]="disabled"
(valueChange)="onYearSelect($event)"
>
</ngxsmk-custom-select>
</div>
<div class="ngxsmk-nav-buttons">
<button
type="button"
class="ngxsmk-nav-button"
(click)="previousMonth.emit()"
[disabled]="disabled || isBackArrowDisabled"
[attr.aria-label]="prevMonthAriaLabel"
[title]="prevMonthAriaLabel"
[ngClass]="navPrevClass"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="48"
d="M328 112L184 256l144 144"
/>
</svg>
</button>
<button
type="button"
class="ngxsmk-nav-button"
(click)="nextMonth.emit()"
[disabled]="disabled"
[attr.aria-label]="nextMonthAriaLabel"
[title]="nextMonthAriaLabel"
[ngClass]="navNextClass"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="48"
d="M184 112l144 144-144 144"
/>
</svg>
</button>
</div>
</div>
`, isInline: true, dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: CustomSelectComponent, selector: "ngxsmk-custom-select", inputs: ["options", "value", "disabled"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CalendarHeaderComponent, decorators: [{
type: Component,
args: [{
selector: 'ngxsmk-calendar-header',
standalone: true,
imports: [NgClass, CustomSelectComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
template: `
<div class="ngxsmk-header" [ngClass]="headerClass">
<div class="ngxsmk-month-year-selects">
<ngxsmk-custom-select
#monthSelect
class="month-select"
[options]="monthOptions"
[(value)]="currentMonth"
[disabled]="disabled"
(valueChange)="onMonthSelect($event)"
>
</ngxsmk-custom-select>
<ngxsmk-custom-select
#yearSelect
class="year-select"
[options]="yearOptions"
[(value)]="currentYear"
[disabled]="disabled"
(valueChange)="onYearSelect($event)"
>
</ngxsmk-custom-select>
</div>
<div class="ngxsmk-nav-buttons">
<button
type="button"
class="ngxsmk-nav-button"
(click)="previousMonth.emit()"
[disabled]="disabled || isBackArrowDisabled"
[attr.aria-label]="prevMonthAriaLabel"
[title]="prevMonthAriaLabel"
[ngClass]="navPrevClass"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="48"
d="M328 112L184 256l144 144"
/>
</svg>
</button>
<button
type="button"
class="ngxsmk-nav-button"
(click)="nextMonth.emit()"
[disabled]="disabled"
[attr.aria-label]="nextMonthAriaLabel"
[title]="nextMonthAriaLabel"
[ngClass]="navNextClass"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="48"
d="M184 112l144 144-144 144"
/>
</svg>
</button>
</div>
</div>
`,
}]
}], propDecorators: { monthSelect: [{
type: ViewChild,
args: ['monthSelect']
}], yearSelect: [{
type: ViewChild,
args: ['yearSelect']
}], monthOptions: [{
type: Input
}], yearOptions: [{
type: Input
}], currentMonth: [{
type: Input
}], currentYear: [{
type: Input
}], disabled: [{
type: Input
}], isBackArrowDisabled: [{
type: Input
}], prevMonthAriaLabel: [{
type: Input
}], nextMonthAriaLabel: [{
type: Input
}], headerClass: [{
type: Input
}], navPrevClass: [{
type: Input
}], navNextClass: [{
type: Input
}], currentYearChange: [{
type: Output
}], currentMonthChange: [{
type: Output
}], previousMonth: [{
type: Output
}], nextMonth: [{
type: Output
}] } });
/**
* Utilities for rendering dates in non-Gregorian calendar systems via
* `Intl.DateTimeFormat`'s `-u-ca-` locale extension.
*
* The datepicker grid itself remains Gregorian; these helpers power the
* `secondaryCalendar` feature that annotates each day cell with its date in
* another calendar system (e.g. Hijri or Jalali), the same approach used by
* Windows and Google Calendar.
*/
const formatterCache = new Map();
function getFormatter(locale, calendar, options) {
const key = `${locale}|${calendar}|${JSON.stringify(options)}`;
const cached = formatterCache.get(key);
if (cached)
return cached;
try {
const formatter = new Intl.DateTimeFormat(locale, { ...options, calendar });
formatterCache.set(key, formatter);
return formatter;
}
catch {
return null;
}
}
/**
* 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.
*/
function getSecondaryDayLabel(date, calendar, locale = 'en-US') {
const formatter = getFormatter(locale, calendar, { day: 'numeric' });
if (!formatter)
return '';
try {
return formatter.format(date);
}
catch {
return '';
}
}
/**
* Full date of `date` in the given calendar system (e.g. `'Tir 20, 1405 AP'`).
* Useful for tooltips and ARIA descriptions. Returns '' when unsupported.
*/
function formatDateInCalendarSystem(date, calendar, locale = 'en-US') {
const formatter = getFormatter(locale, calendar, { year: 'numeric', month: 'long', day: 'numeric' });
if (!formatter)
return '';
try {
return formatter.format(date);
}
catch {
return '';
}
}
/**
* Presentational component that renders the grid of days for a single month.
*
* @remarks
* This component is "dumb" or stateless; it receives all necessary data (days, selection state,
* styling classes) from the parent `NgxsmkDatepickerComponent` and emits events for user interactions.
*
* Key responsibilities:
* - Rendering the 7x6 day grid
* - Applying appropriate CSS classes for selection, ranges, today, etc.
* - Handling touch and mouse events for dates
* - Delegating complex logic (isDateDisabled, etc.) back to the parent via bound functions
*/
class CalendarMonthViewComponent {
constructor() {
this.days = [];
this.weekDays = [];
this.weekDaysFull = [];
this.showOtherMonths = false;
this.dateTemplate = null;
this.dayTemplate = null;
this.mode = 'single';
this.selectedDate = null;
this.startDate = null;
this.endDate = null;
this.focusedDate = null;
this.today = new Date();
this.currentMonth = 0;
this.currentYear = 0;
this.ariaLabel = '';
this.showWeekNumbers = false;
this.weekNumberLabel = 'Wk';
this.secondaryCalendar = null;
this.secondaryCalendarLocale = 'en-US';
// Function inputs for logic checks
this.isDateDisabled = () => false;
this.isSameDay = () => false;
this.isHoliday = () => false;
this.isMultipleSelected = () => false;
this.isInRange = () => false;
this.isInComparisonRange = () => false;
this.isPreviewInRange = () => false;
this.getAriaLabel = () => '';
this.getDayCellCustomClasses = () => ({});
this.getDayCellTooltip = () => '';
this.getDayMetadata = () => null;
this.formatDayNumber = (d) => (d ? d.getDate().toString() : '');
this.dateClick = new EventEmitter();
this.dateMouseDown = new EventEmitter();
this.dateMouseUp = new EventEmitter();
this.dateHover = new EventEmitter();
this.dateFocus = new EventEmitter();
this.swipeStart = new EventEmitter();
this.swipeMove = new EventEmitter();
this.swipeEnd = new EventEmitter();
this.touchStart = new EventEmitter();
this.touchMove = new EventEmitter();
this.touchEnd = new EventEmitter();
}
isRangeType() {
return ['range', 'week', 'month', 'quarter', 'year', 'timeRange'].includes(this.mode);
}
trackByDay(index, day) {
return day ? day.getTime() : index;
}
getSecondaryDay(day) {
if (!day || !this.secondaryCalendar)
return '';
return getSecondaryDayLabel(day, this.secondaryCalendar, this.secondaryCalendarLocale);
}
/** ISO week number for the row starting at `startIndex`, from its first real day. */
getWeekNumberForRow(startIndex) {
for (let i = startIndex; i < startIndex + 7 && i < this.days.length; i++) {
const day = this.days[i];
if (day) {
return String(getISOWeekNumber(day));
}
}
return '';
}
isCurrentMonth(date) {
return !!date && date.getMonth() === this.currentMonth && date.getFullYear() === this.currentYear;
}
onDateClick(day) {
if (day && !this.isDateDisabled(day)) {
this.dateClick.emit(day);
}
}
onDateHover(day) {
if (day) {
this.dateHover.emit(day);
}
}
onDateFocus(day) {
if (day) {
this.dateFocus.emit(day);
}
}
onDateMouseDown(day) {
if (day && !this.isDateDisabled(day)) {
this.dateMouseDown.emit(day);
}
}
onDateMouseUp() {
this.dateMouseUp.emit();
}
onDateCellTouchStart(event, day) {
this.touchStart.emit({ event, day });
}
onDateCellTouchEnd(event, day) {
this.touchEnd.emit({ event, day });
}
onDateCellTouchMove(event) {
this.touchMove.emit(event);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CalendarMonthViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: CalendarMonthViewComponent, isStandalone: true, selector: "ngxsmk-calendar-month-view", inputs: { days: "days", weekDays: "weekDays", weekDaysFull: "weekDaysFull", showOtherMonths: "showOtherMonths", classes: "classes", dateTemplate: "dateTemplate", dayTemplate: "dayTemplate", mode: "mode", selectedDate: "selectedDate", startDate: "startDate", endDate: "endDate", focusedDate: "focusedDate", today: "today", currentMonth: "currentMonth", currentYear: "currentYear", ariaLabel: "ariaLabel", showWeekNumbers: "showWeekNumbers", weekNumberLabel: "weekNumberLabel", secondaryCalendar: "secondaryCalendar", secondaryCalendarLocale: "secondaryCalendarLocale", isDateDisabled: "isDateDisabled", isSameDay: "isSameDay", isHoliday: "isHoliday", isMultipleSelected: "isMultipleSelected", isInRange: "isInRange", isInComparisonRange: "isInComparisonRange", isPreviewInRange: "isPreviewInRange", getAriaLabel: "getAriaLabel", getDayCellCustomClasses: "getDayCellCustomClasses", getDayCellTooltip: "getDayCellTooltip", getDayMetadata: "getDayMetadata", formatDayNumber: "formatDayNumber" }, outputs: { dateClick: "dateClick", dateMouseDown: "dateMouseDown", dateMouseUp: "dateMouseUp", dateHover: "dateHover", dateFocus: "dateFocus", swipeStart: "swipeStart", swipeMove: "swipeMove", swipeEnd: "swipeEnd", touchStart: "touchStart", touchMove: "touchMove", touchEnd: "touchEnd" }, ngImport: i0, template: `
<div
class="ngxsmk-days-grid-wrapper"
(touchstart)="swipeStart.emit($event)"
(touchmove)="swipeMove.emit($event)"
(touchend)="swipeEnd.emit($event)"
>
<div
class="ngxsmk-days-grid"
role="grid"
[attr.aria-label]="ariaLabel"
[class.ngxsmk-with-week-numbers]="showWeekNumbers"
>
@if (showWeekNumbers) {
<div class="ngxsmk-day-name ngxsmk-week-number-header" aria-hidden="true">{{ weekNumberLabel }}</div>
}
@for (day of weekDays; track $index) {
<div class="ngxsmk-day-name" role="columnheader" [attr.aria-label]="weekDaysFull[$index] || day">
{{ day }}
</div>
}
@for (day of days; track day ? day.getTime() : $index) {
@if (showWeekNumbers && $index % 7 === 0) {
<div class="ngxsmk-week-number" aria-hidden="true">{{ getWeekNumberForRow($index) }}</div>
}
<div
class="ngxsmk-day-cell"
[ngClass]="classes?.dayCell"
[class.empty]="!isCurrentMonth(day) && !showOtherMonths"
[class.ngxsmk-other-month]="!isCurrentMonth(day)"
[class.disabled]="isDateDisabled(day)"
[class.today]="isSameDay(day, today)"
[class.holiday]="isHoliday(day)"
[class.selected]="mode === 'single' && isSameDay(day, selectedDate)"
[class.multiple-selected]="mode === 'multiple' && isMultipleSelected(day)"
[class.start-date]="isRangeType() && isSameDay(day, startDate)"
[class.end-date]="isRangeType() && isSameDay(day, endDate)"
[class.in-range]="isRangeType() && isInRange(day)"
[class.in-comparison-range]="isInComparisonRange(day)"
[class.preview-range]="isPreviewInRange(day)"
[class.focused]="day && focusedDate && isSameDay(day, focusedDate)"
[attr.tabindex]="day && !isDateDisabled(day) ? 0 : -1"
[attr.role]="day ? 'gridcell' : null"
[attr.aria-selected]="day && mode === 'single' && isSameDay(day, selectedDate) ? 'true' : null"
[attr.aria-label]="day ? getAriaLabel(day) : null"
[ngClass]="getDayCellCustomClasses(day)"
[attr.title]="day ? getDayCellTooltip(day) : null"
[attr.data-date]="day ? day.getTime() : null"
(click)="onDateClick(day)"
(mousedown)="onDateMouseDown(day)"
(mouseup)="onDateMouseUp()"
(touchstart)="onDateCellTouchStart($event, day)"
(touchend)="onDateCellTouchEnd($event, day)"
(touchmove)="onDateCellTouchMove($event)"
(keydown.enter)="onDateClick(day)"
(keydown.space)="onDateClick(day); $event.preventDefault()"
(mouseenter)="onDateHover(day)"
(focus)="onDateFocus(day)"
>
@if (day) {
@if (dayTemplate) {
<ng-container
*ngTemplateOutlet="
dayTemplate;
context: {
$implicit: day,
date: day,
selected:
(mode === 'single' && isSameDay(day, selectedDate)) ||
(mode === 'multiple' && isMultipleSelected(day)) ||
(isRangeType() && (isSameDay(day, startDate) || isSameDay(day, endDate))),
disabled: isDateDisabled(day),
today: isSameDay(day, today),
holiday: isHoliday(day),
inRange: isRangeType() && isInRange(day),
inComparisonRange: isInComparisonRange(day),
startDate: isRangeType() && isSameDay(day, startDate),
endDate: isRangeType() && isSameDay(day, endDate),
meta: getDayMetadata(day),
}
"
></ng-container>
} @else if (dateTemplate) {
<ng-container
*ngTemplateOutlet="
dateTemplate;
context: {
$implicit: day,
date: day,
selected:
(mode === 'single' && isSameDay(day, selectedDate)) ||
(mode === 'multiple' && isMultipleSelected(day)) ||
(isRangeType() && (isSameDay(day, startDate) || isSameDay(day, endDate))),
disabled: isDateDisabled(day),
today: isSameDay(day, today),
holiday: isHoliday(day),
inRange: isRangeType() && isInRange(day),
inComparisonRange: isInComparisonRange(day),
startDate: isRangeType() && isSameDay(day, startDate),
endDate: isRangeType() && isSameDay(day, endDate),
meta: getDayMetadata(day),
}
"
></ng-container>
} @else {
<div class="ngxsmk-day-number">{{ formatDayNumber(day) }}</div>
@if (secondaryCalendar) {
<div class="ngxsmk-day-secondary" aria-hidden="true">{{ getSecondaryDay(day) }}</div>
}
@if (getDayMetadata(day); as meta) {
@if (meta.indicatorColor) {
<span
class="ngxsmk-day-indicator"
aria-hidden="true"
[style.background-color]="meta.indicatorColor"
></span>
}
@if (meta.label) {
<div class="ngxsmk-day-meta-label">{{ meta.label }}</div>
}
}
}
}
</div>
}
</div>
</div>
`, isInline: true, dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CalendarMonthViewComponent, decorators: [{
type: Component,
args: [{
selector: 'ngxsmk-calendar-month-view',
standalone: true,
imports: [NgClass, NgTemplateOutlet],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div
class="ngxsmk-days-grid-wrapper"
(touchstart)="swipeStart.emit($event)"
(touchmove)="swipeMove.emit($event)"
(touchend)="swipeEnd.emit($event)"
>
<div
class="ngxsmk-days-grid"
role="grid"
[attr.aria-label]="ariaLabel"
[class.ngxsmk-with-week-numbers]="showWeekNumbers"
>
@if (showWeekNumbers) {
<div class="ngxsmk-day-name ngxsmk-week-number-header" aria-hidden="true">{{ weekNumberLabel }}</div>
}
@for (day of weekDays; track $index) {
<div class="ngxsmk-day-name" role="columnheader" [attr.aria-label]="weekDaysFull[$index] || day">
{{ day }}
</div>
}
@for (day of days; track day ? day.getTime() : $index) {
@if (showWeekNumbers && $index % 7 === 0) {
<div class="ngxsmk-week-number" aria-hidden="true">{{ getWeekNumberForRow($index) }}</div>
}
<div
class="ngxsmk-day-cell"
[ngClass]="classes?.dayCell"
[class.empty]="!isCurrentMonth(day) && !showOtherMonths"
[class.ngxsmk-other-month]="!isCurrentMonth(day)"
[class.disabled]="isDateDisabled(day)"
[class.today]="isSameDay(day, today)"
[class.holiday]="isHoliday(day)"
[class.selected]="mode === 'single' && isSameDay(day, selectedDate)"
[class.multiple-selected]="mode === 'multiple' && isMultipleSelected(day)"
[class.start-date]="isRangeType() && isSameDay(day, startDate)"
[class.end-date]="isRangeType() && isSameDay(day, endDate)"
[class.in-range]="isRangeType() && isInRange(day)"
[class.in-comparison-range]="isInComparisonRange(day)"
[class.preview-range]="isPreviewInRange(day)"
[class.focused]="day && focusedDate && isSameDay(day, focusedDate)"
[attr.tabindex]="day && !isDateDisabled(day) ? 0 : -1"
[attr.role]="day ? 'gridcell' : null"
[attr.aria-selected]="day && mode === 'single' && isSameDay(day, selectedDate) ? 'true' : null"
[attr.aria-label]="day ? getAriaLabel(day) : null"
[ngClass]="getDayCellCustomClasses(day)"
[attr.title]="day ? getDayCellTooltip(day) : null"
[attr.data-date]="day ? day.getTime() : null"
(click)="onDateClick(day)"
(mousedown)="onDateMouseDown(day)"
(mouseup)="onDateMouseUp()"
(touchstart)="onDateCellTouchStart($event, day)"
(touchend)="onDateCellTouchEnd($event, day)"
(touchmove)="onDateCellTouchMove($event)"
(keydown.enter)="onDateClick(day)"
(keydown.space)="onDateClick(day); $event.preventDefault()"
(mouseenter)="onDateHover(day)"
(focus)="onDateFocus(day)"
>
@if (day) {
@if (dayTemplate) {
<ng-container
*ngTemplateOutlet="
dayTemplate;
context: {
$implicit: day,
date: day,
selected:
(mode === 'single' && isSameDay(day, selectedDate)) ||
(mode === 'multiple' && isMultipleSelected(day)) ||
(isRangeType() && (isSameDay(day, startDate) || isSameDay(day, endDate))),
disabled: isDateDisabled(day),
today: isSameDay(day, today),
holiday: isHoliday(day),
inRange: isRangeType() && isInRange(day),
inComparisonRange: isInComparisonRange(day),
startDate: isRangeType() && isSameDay(day, startDate),
endDate: isRangeType() && isSameDay(day, endDate),
meta: getDayMetadata(day),
}
"
></ng-container>
} @else if (dateTemplate) {
<ng-container
*ngTemplateOutlet="
dateTemplate;
context: {
$implicit: day,
date: day,
selected:
(mode === 'single' && isSameDay(day, selectedDate)) ||
(mode === 'multiple' && isMultipleSelected(day)) ||
(isRangeType() && (isSameDay(day, startDate) || isSameDay(day, endDate))),
disabled: isDateDisabled(day),
today: isSameDay(day, today),
holiday: isHoliday(day),
inRange: isRangeType() && isInRange(day),
inComparisonRange: isInComparisonRange(day),
startDate: isRangeType() && isSameDay(day, startDate),
endDate: isRangeType() && isSameDay(day, endDate),
meta: getDayMetadata(day),
}
"
></ng-container>
} @else {
<div class="ngxsmk-day-number">{{ formatDayNumber(day) }}</div>
@if (secondaryCalendar) {
<div class="ngxsmk-day-secondary" aria-hidden="true">{{ getSecondaryDay(day) }}</div>
}
@if (getDayMetadata(day); as meta) {
@if (meta.indicatorColor) {
<span
class="ngxsmk-day-indicator"
aria-hidden="true"
[style.background-color]="meta.indicatorColor"
></span>
}
@if (meta.label) {
<div class="ngxsmk-day-meta-label">{{ meta.label }}</div>
}
}
}
}
</div>
}
</div>
</div>
`,
}]
}], propDecorators: { days: [{
type: Input
}], weekDays: [{
type: Input
}], weekDaysFull: [{
type: Input
}], showOtherMonths: [{
type: Input
}], classes: [{
type: Input
}], dateTemplate: [{
type: Input
}], dayTemplate: [{
type: Input
}], mode: [{
type: Input
}], selectedDate: [{
type: Input
}], startDate: [{
type: Input
}], endDate: [{
type: Input
}], focusedDate: [{
type: Input
}], today: [{
type: Input
}], currentMonth: [{
type: Input
}], currentYear: [{
type: Input
}], ariaLabel: [{
type: Input
}], showWeekNumbers: [{
type: Input
}], weekNumberLabel: [{
type: Input
}], secondaryCalendar: [{
type: Input
}], secondaryCalendarLocale: [{
type: Input
}], isDateDisabled: [{
type: Input
}], isSameDay: [{
type: Input
}], isHoliday: [{
type: Input
}], isMultipleSelected: [{
type: Input
}], isInRange: [{
type: Input
}], isInComparisonRange: [{
type: Input
}], isPreviewInRange: [{
type: Input
}], getAriaLabel: [{
type: Input
}], getDayCellCustomClasses: [{
type: Input
}], getDayCellTooltip: [{
type: Input
}], getDayMetadata: [{
type: Input
}], formatDayNumber: [{
type: Input
}], dateClick: [{
type: Output
}], dateMouseDown: [{
type: Output
}], dateMouseUp: [{
type: Output
}], dateHover: [{
type: Output
}], dateFocus: [{
type: Output
}], swipeStart: [{
type: Output
}], swipeMove: [{
type: Output
}], swipeEnd: [{
type: Output
}], touchStart: [{
type: Output
}], touchMove: [{
type: Output
}], touchEnd: [{
type: Output
}] } });
/**
* Calculate virtual scroll range for a list of items
*/
function calculateVirtualScroll(items, scrollTop, config) {
const { itemHeight, containerHeight, overscan = 3, itemsPerRow = 1 } = config;
const totalRows = Math.ceil(items.length / itemsPerRow);
const totalHeight = totalRows * itemHeight;
const startRow = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
const visibleRowCount = Math.ceil(containerHeight / itemHeight);
const endRow = Math.min(totalRows - 1, startRow + visibleRowCount + overscan * 2);
const startIndex = startRow * itemsPerRow;
const endIndex = Math.min(items.length - 1, (endRow + 1) * itemsPerRow - 1);
const visibleItems = [];
for (let i = startIndex; i <= endIndex; i++) {
const item = items[i];
if (item !== undefined && item !== null) {
visibleItems.push({
index: i,
data: item,
});
}
}
const offsetY = startRow * itemHeight;
return {
startIndex,
endIndex,
visibleItems,
totalHeight,
offsetY,
hasMoreBefore: startIndex > 0,
hasMoreAfter: endIndex < items.length - 1,
};
}
/**
* Get virtual scroll range (simplified version)
*/
function getVirtualScrollRange(totalItems, scrollTop, containerHeight, itemHeight, overscan = 3, itemsPerRow = 1) {
const totalRows = Math.ceil(totalItems / itemsPerRow);
const startRow = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
const visibleRowCount = Math.ceil(containerHeight / itemHeight);
const endRow = Math.min(totalRows - 1, startRow + visibleRowCount + overscan * 2);
const start = startRow * itemsPerRow;
const end = Math.min(totalItems - 1, (endRow + 1) * itemsPerRow - 1);
const offset = startRow * itemHeight;
return { start, end, offset };
}
/**
* Generate a large year range for virtual scrolling
*/
function generateLargeYearRange(centerYear, range = 100) {
const startYear = centerYear - Math.floor(range / 2);
const years = [];
for (let i = 0; i < range; i++) {
years.push(startYear + i);
}
return years;
}
/**
* Generate a large decade range for virtual scrolling
*/
function generateLargeDecadeRange(centerDecade, range = 50) {
const startDecade = centerDecade - Math.floor(range / 2) * 10;
const decades = [];
for (let i = 0; i < range; i++) {
decades.push(startDecade + i * 10);
}
return decades;
}
/**
* Find the index of a target value in a sorted array (for scrolling to specific year/decade)
*/
function findIndexInSortedArray(items, target, compareFn) {
let left = 0;
let right = items.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
const midItem = items[mid];
if (midItem === undefined) {
return Math.max(0, Math.min(left, items.length - 1));
}
const comparison = compareFn(midItem, target);
if (comparison === 0) {
return mid;
}
else if (comparison < 0) {
left = mid + 1;
}
else {
right = mid - 1;
}
}
return Math.max(0, Math.min(left, items.length - 1));
}
/**
* Presentational component for selecting years and decades with virtual scrolling support.
*
* @remarks
* Supports two distinct view modes:
* 1. **'year'**: Displays a virtual-scrolled grid of years for selection.
* 2. **'decade'**: Displays a virtual-scrolled grid of decades for navigating large time spans.
*
* Virtual scrolling ensures that only visible items are rendered, enabling smooth navigation
* through large year/decade ranges without performance degradation.
*
* This component is stateless and relies on the parent for logic and state management.
*/
class CalendarYearViewComponent {
constructor() {
this.viewMode = 'year';
this.yearGrid = [];
this.decadeGrid = [];
this.currentYear = new Date().getFullYear();
this.currentDecade = new Date().getFullYear();
this.today = new Date();
this.disabled = false;
this.selectedDate = null;
this.startDate = null;
this.mode = 'single';
// Virtual scrolling container heights
this.yearContainerHeight = 280; // Adjust based on CSS height
this.decadeContainerHeight = 280;
// Translation labels
this.previousYearsLabel = 'Previous Years';
this.nextYearsLabel = 'Next Years';
this.previousDecadeLabel = 'Previous Decade';
this.nextDecadeLabel = 'Next Decade';
this.viewModeChange = new EventEmitter();
this.yearClick = new EventEmitter();
this.decadeClick = new EventEmitter();
this.changeYear = new EventEmitter();
this.changeDecade = new EventEmitter();
// Virtual scrolling signals
this.yearScrollPositionSignal = signal(0, ...(ngDevMode ? [{ debugName: "yearScrollPositionSignal" }] : /* istanbul ignore next */ []));
this.decadeScrollPositionSignal = signal(0, ...(ngDevMode ? [{ debugName: "decadeScrollPositionSignal" }] : /* istanbul ignore next */ []));
// Large year/decade ranges for virtual scrolling
this.largeYearRange = [];
this.largeDecadeRange = [];
// Virtual scroll results (computed from signals and scroll position)
this.yearVirtualResult = computed(() => {
return calculateVirtualScroll(this.largeYearRange, this.yearScrollPositionSignal(), {
itemHeight: 40, // Adjust based on CSS button height
containerHeight: this.yearContainerHeight,
overscan: 5,
itemsPerRow: 4, // 4 columns for year grid
});
}, ...(ngDevMode ? [{ debugName: "yearVirtualResult" }] : /* istanbul ignore next */ []));
this.decadeVirtualResult = computed(() => {
return calculateVirtualScroll(this.largeDecadeRange, this.decadeScrollPositionSignal(), {
itemHeight: 40, // Adjust based on CSS button height
containerHeight: this.decadeContainerHeight,
overscan: 5,
itemsPerRow: 3, // 3 columns for decade grid
});
}, ...(ngDevMode ? [{ debugName: "decadeVirtualResult" }] : /* istanbul ignore next */ []));
}
onYearCellClick(year, event) {
event.stopPropagation();
if (this.disabled || (this.isYearDisabled && this.isYearDisabled(year)))
return;
this.yearClick.emit(year);
}
onDecadeCellClick(decade, event) {
event.stopPropagation();
if (this.disabled || (this.isDecadeDisabled && this.isDecadeDisabled(decade)))
return;
this.decadeClick.emit(decade);
}
isYearSelected(year) {
if (this.mode === 'year' ||
this.mode === 'month' ||
this.mode === 'quarter' ||
this.mode === 'range' ||
this.mode === 'week') {
return this.startDate !== null && this.startDate.getFullYear() === year;
}
if (this.selectedDate !== null) {
return this.selectedDate.getFullYear() === year;
}
return false;
}
getDecadeRangeLabel() {
const decadeStart = Math.floor(this.currentYear / 10) * 10;
return `${decadeStart} - ${decadeStart + 9}`;
}
ngOnInit() {
// Generate large ranges for virtual scrolling
// Centers on current year/decade with 100 years / 50 decades range
const decadeStart = Math.floor(this.currentYear / 10) * 10;
this.largeYearRange = generateLargeYearRange(this.currentYear, 100);
this.largeDecadeRange = generateLargeDecadeRange(decadeStart, 50);
}
ngOnChanges(changes) {
if (changes['currentYear'] && !changes['currentYear'].firstChange) {
const year = changes['currentYear'].currentValue;
if (year && !this.largeYearRange.includes(year)) {
this.largeYearRange = generateLargeYearRange(year, 100);
}
}
if (changes['currentDecade'] && !changes['currentDecade'].firstChange) {
const decade = changes['currentDecade'].currentValue;
if (decade && !this.largeDecadeRange.includes(decade)) {
this.largeDecadeRange = generateLargeDecadeRange(decade, 50);
}
}
}
onYearScroll(event) {
const scrollTop = event.target.scrollTop;
this.yearScrollPositionSignal.set(scrollTop);
}
onDecadeScroll(event) {
const scrollTop = event.target.scrollTop;
this.decadeScrollPositionSignal.set(scrollTop);
}
getYearAriaLabel(year) {
return `Select year ${year}`;
}
getDecadeAriaLabel(decade) {
return `Select decade ${decade} - ${decade + 9}`;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CalendarYearViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: CalendarYearViewComponent, isStandalone: true, selector: "ngxsmk-calendar-year-view", inputs: { viewMode: "viewMode", yearGrid: "yearGrid", decadeGrid: "decadeGrid", currentYear: "currentYear", currentDecade: "currentDecade", today: "today", disabled: "disabled", isYearDisabled: "isYearDisabled", isDecadeDisabled: "isDecadeDisabled", selectedDate: "selectedDate", startDate: "startDate", mode: "mode", yearContainerHeight: "yearContainerHeight", decadeContainerHeight: "decadeContainerHeight", previousYearsLabel: "previousYearsLabel", nextYearsLabel: "nextYearsLabel", previousDecadeLabel: "previousDecadeLabel", nextDecadeLabel: "nextDecadeLabel", headerClass: "headerClass", navPrevClass: "navPrevClass", navNextClass: "navNextClass" }, outputs: { viewModeChange: "viewModeChange", yearClick: "yearClick", decadeClick: "decadeClick", changeYear: "changeYear", changeDecade: "changeDecade" }, viewQueries: [{ propertyName: "yearScrollContainer", first: true, predicate: ["yearScrollContainer"], descendants: true }, { propertyName: "decadeScrollContainer", first: true, predicate: ["decadeScrollContainer"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
@if (viewMode === 'year') {
<div class="ngxsmk-header" [ngClass]="headerClass">
<div class="ngxsmk-year-display">
<button
type="button"
class="ngxsmk-view-toggle"
(click)="viewModeChange.emit('decade')"
[disabled]="disabled"
>
{{ getDecadeRangeLabel() }}
</button>
</div>
<div class="ngxsmk-nav-buttons">
<button
type="button"
class="ngxsmk-nav-button"
(click)="changeYear.emit(-12)"
[disabled]="disabled"
[attr.aria-label]="previousYearsLabel"
[ngClass]="navPrevClass"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="48"
d="M328 112L184 256l144 144"
/>
</svg>
</button>
<button
type="button"
class="ngxsmk-nav-button"
(click)="changeYear.emit(12)"
[disabled]="disabled"
[attr.aria-label]="nextYearsLabel"
[ngClass]="navNextClass"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="48"
d="M184 112l144 144-144 144"
/>
</svg>
</button>
</div>
</div>
<div
class="ngxsmk-year-grid-container"
#yearScrollContainer
(scroll)="onYearScroll($event)"
[style.height.px]="yearContainerHeight"
>
<div
class="ngxsmk-year-grid"
role="grid"
aria-label="Select Year"
[style.height.px]="yearVirtualResult().totalHeight"
[style.transform]="'translateY(' + (yearVirtualResult().offsetY || 0) + 'px)'"
>
@for (item of yearVirtualResult().visibleItems; track item.index) {
<button
type="button"
role="gridcell"
class="ngxsmk-year-cell"
[class.selected]="isYearSelected(item.data)"
[class.today]="item.data === today.getFullYear()"
[disabled]="disabled || (isYearDisabled ? isYearDisabled(item.data) : false)"
[attr.aria-selected]="isYearSelected(item.data)"
[attr.aria-current]="item.data === today.getFullYear() ? 'date' : null"
(click)="onYearCellClick(item.data, $event)"
(keydown.enter)="onYearCellClick(item.data, $event)"
[attr.aria-label]="getYearAriaLabel(item.data)"
>
{{ item.data }}
</button>
}
</div>
</div>
}
@if (viewMode === 'decade') {
<div class="ngxsmk-header" [ngClass]="headerClass">
<div class="ngxsmk-decade-display">{{ currentDecade }} - {{ currentDecade + 99 }}</div>
<div class="ngxsmk-nav-buttons">
<button
type="button"
class="ngxsmk-nav-button"
(click)="changeDecade.emit(-1)"
[disabled]="disabled"
[attr.aria-label]="previousDecadeLabel"
[ngClass]="navPrevClass"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="48"
d="M328 112L184 256l144 144"
/>
</svg>
</button>
<button
type="button"
class="ngxsmk-nav-button"
(click)="changeDecade.emit(1)"
[disabled]="disabled"
[attr.aria-label]="nextDecadeLabel"
[ngClass]="navNextClass"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="48"
d="M184 112l144 144-144 144"
/>
</svg>
</button>
</div>
</div>
<div
class="ngxsmk-decade-grid-container"
#decadeScrollContainer
(scroll)="onDecadeScroll($event)"
[style.height.px]="decadeContainerHeight"
>
<div
class="ngxsmk-decade-grid"
role="grid"
aria-label="Select Decade"
[style.height.px]="decadeVirtualResult().totalHeight"
[style.transform]="'translateY(' + (decadeVirtualResult().offsetY || 0) + 'px)'"
>
@for (item of decadeVirtualResult().visibleItems; track item.index) {
<button
type="button"
role="gridcell"
class="ngxsmk-decade-cell"
[class.selected]="item.data === currentDecade"
[disabled]="disabled || (isDecadeDisabled ? isDecadeDisabled(item.data) : false)"
[attr.aria-selected]="item.data === currentDecade"
(click)="onDecadeCellClick(item.data, $event)"
(keydown.enter)="onDecadeCellClick(item.data, $event)"
[attr.aria-label]="getDecadeAriaLabel(item.data)"
>
{{ item.data }} - {{ item.data + 9 }}
</button>
}
</div>
</div>
}
`, isInline: true, dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CalendarYearViewComponent, decorators: [{
type: Component,
args: [{
selector: 'ngxsmk-calendar-year-view',
standalone: true,
imports: [NgClass],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
@if (viewMode === 'year') {
<div class="ngxsmk-header" [ngClass]="headerClass">
<div class="ngxsmk-year-display">
<button
type="button"
class="ngxsmk-view-toggle"
(click)="viewModeChange.emit('decade')"
[disabled]="disabled"
>
{{ getDecadeRangeLabel() }}
</button>
</div>
<div class="ngxsmk-nav-buttons">
<button
type="button"
class="ngxsmk-nav-button"
(click)="changeYear.emit(-12)"
[disabled]="disabled"
[attr.aria-label]="previousYearsLabel"
[ngClass]="navPrevClass"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="48"
d="M328 112L184 256l144 144"
/>
</svg>
</button>
<button
type="button"
class="ngxsmk-nav-button"
(click)="changeYear.emit(12)"
[disabled]="disabled"
[attr.aria-label]="nextYearsLabel"
[ngClass]="navNextClass"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="48"
d="M184 112l144 144-144 144"
/>
</svg>
</button>
</div>
</div>
<div
class="ngxsmk-year-grid-container"
#yearScrollContainer
(scroll)="onYearScroll($event)"
[style.height.px]="yearContainerHeight"
>
<div
class="ngxsmk-year-grid"
role="grid"
aria-label="Select Year"
[style.height.px]="yearVirtualResult().totalHeight"
[style.transform]="'translateY(' + (yearVirtualResult().offsetY || 0) + 'px)'"
>
@for (item of yearVirtualResult().visibleItems; track item.index) {
<button
type="button"
role="gridcell"
class="ngxsmk-year-cell"
[class.selected]="isYearSelected(item.data)"
[class.today]="item.data === today.getFullYear()"
[disabled]="disabled || (isYearDisabled ? isYearDisabled(item.data) : false)"
[attr.aria-selected]="isYearSelected(item.data)"
[attr.aria-current]="item.data === today.getFullYear() ? 'date' : null"
(click)="onYearCellClick(item.data, $event)"
(keydown.enter)="onYearCellClick(item.data, $event)"
[attr.aria-label]="getYearAriaLabel(item.data)"
>
{{ item.data }}
</button>
}
</div>
</div>
}
@if (viewMode === 'decade') {
<div class="ngxsmk-header" [ngClass]="headerClass">
<div class="ngxsmk-decade-display">{{ currentDecade }} - {{ currentDecade + 99 }}</div>
<div class="ngxsmk-nav-buttons">
<button
type="button"
class="ngxsmk-nav-button"
(click)="changeDecade.emit(-1)"
[disabled]="disabled"
[attr.aria-label]="previousDecadeLabel"
[ngClass]="navPrevClass"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="48"
d="M328 112L184 256l144 144"
/>
</svg>
</button>
<button
type="button"
class="ngxsmk-nav-button"
(click)="changeDecade.emit(1)"
[disabled]="disabled"
[attr.aria-label]="nextDecadeLabel"
[ngClass]="navNextClass"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="48"
d="M184 112l144 144-144 144"
/>
</svg>
</button>
</div>
</div>
<div
class="ngxsmk-decade-grid-container"
#decadeScrollContainer
(scroll)="onDecadeScroll($event)"
[style.height.px]="decadeContainerHeight"
>
<div
class="ngxsmk-decade-grid"
role="grid"
aria-label="Select Decade"
[style.height.px]="decadeVirtualResult().totalHeight"
[style.transform]="'translateY(' + (decadeVirtualResult().offsetY || 0) + 'px)'"
>
@for (item of decadeVirtualResult().visibleItems; track item.index) {
<button
type="button"
role="gridcell"
class="ngxsmk-decade-cell"
[class.selected]="item.data === currentDecade"
[disabled]="disabled || (isDecadeDisabled ? isDecadeDisabled(item.data) : false)"
[attr.aria-selected]="item.data === currentDecade"
(click)="onDecadeCellClick(item.data, $event)"
(keydown.enter)="onDecadeCellClick(item.data, $event)"
[attr.aria-label]="getDecadeAriaLabel(item.data)"
>
{{ item.data }} - {{ item.data + 9 }}
</button>
}
</div>
</div>
}
`,
}]
}], propDecorators: { viewMode: [{
type: Input
}], yearGrid: [{
type: Input
}], decadeGrid: [{
type: Input
}], currentYear: [{
type: Input
}], currentDecade: [{
type: Input
}], today: [{
type: Input
}], disabled: [{
type: Input
}], isYearDisabled: [{
type: Input
}], isDecadeDisabled: [{
type: Input
}], selectedDate: [{
type: Input
}], startDate: [{
type: Input
}], mode: [{
type: Input
}], yearContainerHeight: [{
type: Input
}], decadeContainerHeight: [{
type: Input
}], previousYearsLabel: [{
type: Input
}], nextYearsLabel: [{
type: Input
}], previousDecadeLabel: [{
type: Input
}], nextDecadeLabel: [{
type: Input
}], headerClass: [{
type: Input
}], navPrevClass: [{
type: Input
}], navNextClass: [{
type: Input
}], yearScrollContainer: [{
type: ViewChild,
args: ['yearScrollContainer']
}], decadeScrollContainer: [{
type: ViewChild,
args: ['decadeScrollContainer']
}], viewModeChange: [{
type: Output
}], yearClick: [{
type: Output
}], decadeClick: [{
type: Output
}], changeYear: [{
type: Output
}], changeDecade: [{
type: Output
}] } });
/**
* Component for selecting time (Hours, Minutes, Seconds, AM/PM).
*
* @remarks
* Renders a row of custom select dropdowns for each time component.
* It handles the display logic and emits individual changes which are aggregated
* by the parent component into a full date-time update.
*/
class TimeSelectionComponent {
constructor() {
this.hourOptions = [];
this.minuteOptions = [];
this.secondOptions = [];
this.ampmOptions = [
{ label: 'AM', value: false },
{ label: 'PM', value: true },
];
this.currentDisplayHour = 12;
this.currentMinute = 0;
this.currentSecond = 0;
this.isPm = false;
this.disabled = false;
this.timeLabel = 'Time';
this.showSeconds = false;
this.showAmpm = true;
this.timeChange = new EventEmitter();
this.currentDisplayHourChange = new EventEmitter();
this.currentMinuteChange = new EventEmitter();
this.currentSecondChange = new EventEmitter();
this.isPmChange = new EventEmitter();
}
onHourChange(value) {
this.currentDisplayHourChange.emit(Number(value));
this.timeChange.emit();
}
onMinuteChange(value) {
this.currentMinuteChange.emit(Number(value));
this.timeChange.emit();
}
onSecondChange(value) {
this.currentSecondChange.emit(Number(value));
this.timeChange.emit();
}
onAmPmChange(value) {
this.isPmChange.emit(Boolean(value));
this.timeChange.emit();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: TimeSelectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: TimeSelectionComponent, isStandalone: true, selector: "ngxsmk-time-selection", inputs: { hourOptions: "hourOptions", minuteOptions: "minuteOptions", secondOptions: "secondOptions", ampmOptions: "ampmOptions", currentDisplayHour: "currentDisplayHour", currentMinute: "currentMinute", currentSecond: "currentSecond", isPm: "isPm", disabled: "disabled", timeLabel: "timeLabel", showSeconds: "showSeconds", showAmpm: "showAmpm" }, outputs: { timeChange: "timeChange", currentDisplayHourChange: "currentDisplayHourChange", currentMinuteChange: "currentMinuteChange", currentSecondChange: "currentSecondChange", isPmChange: "isPmChange" }, ngImport: i0, template: `
<div class="ngxsmk-time-selection">
<span class="ngxsmk-time-label">{{ timeLabel }}</span>
<ngxsmk-custom-select
class="hour-select"
[options]="hourOptions"
[(value)]="currentDisplayHour"
(valueChange)="onHourChange($event)"
[disabled]="disabled"
>
</ngxsmk-custom-select>
<span class="ngxsmk-time-separator">:</span>
<ngxsmk-custom-select
class="minute-select"
[options]="minuteOptions"
[(value)]="currentMinute"
(valueChange)="onMinuteChange($event)"
[disabled]="disabled"
>
</ngxsmk-custom-select>
@if (showSeconds) {
<ngxsmk-custom-select
class="second-select"
[options]="secondOptions"
[(value)]="currentSecond"
(valueChange)="onSecondChange($event)"
[disabled]="disabled"
>
</ngxsmk-custom-select>
}
@if (showAmpm) {
<ngxsmk-custom-select
class="ampm-select"
[options]="ampmOptions"
[(value)]="isPm"
(valueChange)="onAmPmChange($event)"
[disabled]="disabled"
>
</ngxsmk-custom-select>
}
</div>
`, isInline: true, dependencies: [{ kind: "component", type: CustomSelectComponent, selector: "ngxsmk-custom-select", inputs: ["options", "value", "disabled"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: TimeSelectionComponent, decorators: [{
type: Component,
args: [{
selector: 'ngxsmk-time-selection',
standalone: true,
imports: [CustomSelectComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="ngxsmk-time-selection">
<span class="ngxsmk-time-label">{{ timeLabel }}</span>
<ngxsmk-custom-select
class="hour-select"
[options]="hourOptions"
[(value)]="currentDisplayHour"
(valueChange)="onHourChange($event)"
[disabled]="disabled"
>
</ngxsmk-custom-select>
<span class="ngxsmk-time-separator">:</span>
<ngxsmk-custom-select
class="minute-select"
[options]="minuteOptions"
[(value)]="currentMinute"
(valueChange)="onMinuteChange($event)"
[disabled]="disabled"
>
</ngxsmk-custom-select>
@if (showSeconds) {
<ngxsmk-custom-select
class="second-select"
[options]="secondOptions"
[(value)]="currentSecond"
(valueChange)="onSecondChange($event)"
[disabled]="disabled"
>
</ngxsmk-custom-select>
}
@if (showAmpm) {
<ngxsmk-custom-select
class="ampm-select"
[options]="ampmOptions"
[(value)]="isPm"
(valueChange)="onAmPmChange($event)"
[disabled]="disabled"
>
</ngxsmk-custom-select>
}
</div>
`,
}]
}], propDecorators: { hourOptions: [{
type: Input
}], minuteOptions: [{
type: Input
}], secondOptions: [{
type: Input
}], ampmOptions: [{
type: Input
}], currentDisplayHour: [{
type: Input
}], currentMinute: [{
type: Input
}], currentSecond: [{
type: Input
}], isPm: [{
type: Input
}], disabled: [{
type: Input
}], timeLabel: [{
type: Input
}], showSeconds: [{
type: Input
}], showAmpm: [{
type: Input
}], timeChange: [{
type: Output
}], currentDisplayHourChange: [{
type: Output
}], currentMinuteChange: [{
type: Output
}], currentSecondChange: [{
type: Output
}], isPmChange: [{
type: Output
}] } });
class NgxsmkDatepickerPresetsComponent {
constructor() {
this.ranges = input([], ...(ngDevMode ? [{ debugName: "ranges" }] : /* istanbul ignore next */ []));
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
this.classes = input(undefined, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
this.selectedRange = input(null, ...(ngDevMode ? [{ debugName: "selectedRange" }] : /* istanbul ignore next */ []));
this.rangeSelected = output();
}
onRangeSelect(range) {
if (!this.disabled()) {
this.rangeSelected.emit(range);
}
}
isActive(value) {
const range = this.selectedRange();
if (!range || !range[0] || !range[1]) {
return false;
}
const start = range[0];
const end = range[1];
return this.isSameDay(value[0], start) && this.isSameDay(value[1], end);
}
isSameDay(d1, d2) {
return d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth() && d1.getDate() === d2.getDate();
}
trackByRange(_index, range) {
return range.key;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NgxsmkDatepickerPresetsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: NgxsmkDatepickerPresetsComponent, isStandalone: true, selector: "ngxsmk-datepicker-presets", inputs: { ranges: { classPropertyName: "ranges", publicName: "ranges", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null }, selectedRange: { classPropertyName: "selectedRange", publicName: "selectedRange", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rangeSelected: "rangeSelected" }, ngImport: i0, template: `
<div class="ngxsmk-ranges-container">
<ul>
@for (range of ranges(); track trackByRange($index, range)) {
<li
(click)="onRangeSelect(range.value)"
(keydown.enter)="onRangeSelect(range.value)"
(keydown.space)="onRangeSelect(range.value); $event.preventDefault()"
[class.disabled]="disabled()"
[class.ngxsmk-preset-active]="isActive(range.value)"
[attr.tabindex]="disabled() ? -1 : 0"
role="button"
[attr.aria-disabled]="disabled()"
>
{{ range.key }}
</li>
}
</ul>
</div>
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NgxsmkDatepickerPresetsComponent, decorators: [{
type: Component,
args: [{
selector: 'ngxsmk-datepicker-presets',
standalone: true,
imports: [],
template: `
<div class="ngxsmk-ranges-container">
<ul>
@for (range of ranges(); track trackByRange($index, range)) {
<li
(click)="onRangeSelect(range.value)"
(keydown.enter)="onRangeSelect(range.value)"
(keydown.space)="onRangeSelect(range.value); $event.preventDefault()"
[class.disabled]="disabled()"
[class.ngxsmk-preset-active]="isActive(range.value)"
[attr.tabindex]="disabled() ? -1 : 0"
role="button"
[attr.aria-disabled]="disabled()"
>
{{ range.key }}
</li>
}
</ul>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
}]
}], propDecorators: { ranges: [{ type: i0.Input, args: [{ isSignal: true, alias: "ranges", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }], selectedRange: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedRange", required: false }] }], rangeSelected: [{ type: i0.Output, args: ["rangeSelected"] }] } });
class NgxsmkDatepickerContentComponent {
constructor() {
this.isCalendarVisible = false;
this.isCalendarOpen = false;
this.isInlineMode = false;
this.shouldAppendToBody = false;
this.theme = 'light';
this.popoverId = '';
this.classes = undefined;
this.timeOnly = false;
this.showTime = false;
this.isMobile = false;
this.mobileModalStyle = 'bottom-sheet';
this.align = 'left';
this.ariaLabel = '';
this.isCalendarOpening = false;
this.loadingMessage = '';
this.showRanges = true;
this.rangesArray = [];
this.mode = 'single';
this.disabled = false;
this.calendarCount = 1;
this.calendarLayout = 'auto';
this.syncScrollEnabled = false;
this.calendarMonths = [];
this.weekDays = [];
this.weekDaysFull = [];
this.showOtherMonths = false;
this.showWeekNumbers = false;
this.weekNumberLabel = 'Wk';
this.secondaryCalendar = null;
this.secondaryCalendarLocale = 'en-US';
this.selectedDate = null;
this.startDate = null;
this.endDate = null;
this.focusedDate = null;
this.today = new Date();
this.dateTemplate = null;
this.calendarViewMode = 'month';
this.monthOptions = [];
this.currentMonth = 0;
this.yearOptions = [];
this.currentYear = new Date().getFullYear();
this.isBackArrowDisabled = false;
this.prevMonthAriaLabel = '';
this.nextMonthAriaLabel = '';
this.yearGrid = [];
this.currentDecade = 0;
this.decadeGrid = [];
this.timelineStartDate = null;
this.timelineEndDate = null;
this.timelineMonths = [];
this.minuteInterval = 1;
this.startTimeSlider = 0;
this.endTimeSlider = 0;
this.timeRangeMode = false;
this.hourOptions = [];
this.minuteOptions = [];
this.secondOptions = [];
this.ampmOptions = [];
this.currentDisplayHour = 12;
this.currentMinute = 0;
this.currentSecond = 0;
this.isPm = false;
this.showSeconds = false;
this.use24Hour = false;
this.startDisplayHour = 12;
this.startMinute = 0;
this.startSecond = 0;
this.startIsPm = false;
this.endDisplayHour = 12;
this.endMinute = 0;
this.endSecond = 0;
this.endIsPm = false;
this.clearAriaLabel = '';
this.clearLabel = '';
this.closeAriaLabel = '';
this.closeLabel = '';
this.translations = null;
this.selectedRange = null;
this.showTimezoneSelector = false;
this.timezoneOptions = [];
this.currentTimezone = 'UTC';
this.timezoneChange = new EventEmitter();
// Bound functions
this.boundIsDateDisabled = () => false;
this.boundGetDayMetadata = () => null;
this.calendarHeaderTemplate = null;
this.calendarFooterTemplate = null;
/**
* Stable context handed to the header/footer slot templates.
* `$implicit` exposes the popover actions (`clear`, `close`).
*/
this.slotContext = {
$implicit: {
clear: () => this.clearValue.emit(new MouseEvent('click')),
close: () => this.closeCalendar.emit(),
},
};
this.boundIsInComparisonRange = () => false;
this.backdropClick = new EventEmitter();
this.touchStartContainer = new EventEmitter();
this.touchMoveContainer = new EventEmitter();
this.touchEndContainer = new EventEmitter();
this.rangeSelect = new EventEmitter();
this.previousMonth = new EventEmitter();
this.nextMonth = new EventEmitter();
this.currentMonthChange = new EventEmitter();
this.currentYearChange = new EventEmitter();
this.dateClick = new EventEmitter();
this.dateHover = new EventEmitter();
this.dateFocus = new EventEmitter();
this.swipeStart = new EventEmitter();
this.swipeMove = new EventEmitter();
this.swipeEnd = new EventEmitter();
this.touchStart = new EventEmitter();
this.touchMove = new EventEmitter();
this.touchEnd = new EventEmitter();
this.viewModeChange = new EventEmitter();
this.changeYear = new EventEmitter();
this.yearClick = new EventEmitter();
this.changeDecade = new EventEmitter();
this.decadeClick = new EventEmitter();
this.timelineZoomOut = new EventEmitter();
this.timelineZoomIn = new EventEmitter();
this.timelineMonthClick = new EventEmitter();
this.startTimeSliderChange = new EventEmitter();
this.endTimeSliderChange = new EventEmitter();
this.currentDisplayHourChange = new EventEmitter();
this.currentMinuteChange = new EventEmitter();
this.currentSecondChange = new EventEmitter();
this.isPmChange = new EventEmitter();
this.timeChange = new EventEmitter();
this.startDisplayHourChange = new EventEmitter();
this.startMinuteChange = new EventEmitter();
this.startSecondChange = new EventEmitter();
this.startIsPmChange = new EventEmitter();
this.endDisplayHourChange = new EventEmitter();
this.endMinuteChange = new EventEmitter();
this.endSecondChange = new EventEmitter();
this.endIsPmChange = new EventEmitter();
this.timeRangeChange = new EventEmitter();
this.clearValue = new EventEmitter();
this.closeCalendar = new EventEmitter();
this.escapeKey = new EventEmitter();
this.containerKeyDown = new EventEmitter();
this.enableAi = false;
this.aiPlaceholder = 'Ask AI (e.g. "next Friday", "last 7 days")...';
this.aiSuggestions = [];
this.showAiSuggestions = true;
this.isAiResolving = false;
this.aiPromptSubmitted = new EventEmitter();
this.aiPromptText = '';
}
closeAllSelects() {
if (this.header) {
if (this.header.monthSelect) {
this.header.monthSelect.isOpen = false;
}
if (this.header.yearSelect) {
this.header.yearSelect.isOpen = false;
}
}
}
onTimelineMonthClick(month, event) {
event.stopPropagation();
this.timelineMonthClick.emit(month);
}
onTimelineMonthSpace(month, event) {
event.preventDefault();
this.timelineMonthClick.emit(month);
}
onPopoverEscape(event) {
event.preventDefault();
event.stopPropagation();
this.escapeKey.emit(event);
if (!this.isInlineMode) {
this.closeCalendar.emit();
}
}
onAiSubmit() {
const text = this.aiPromptText?.trim();
if (text && !this.isAiResolving) {
this.aiPromptSubmitted.emit(text);
this.aiPromptText = '';
}
}
onAiChipClick(chip) {
if (this.isAiResolving || !chip?.trim())
return;
this.aiPromptSubmitted.emit(chip.trim());
}
focusAiInput() {
this.aiInputElement?.nativeElement?.focus();
}
onTimezoneChange(val) {
if (typeof val === 'string') {
this.timezoneChange.emit(val);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NgxsmkDatepickerContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: NgxsmkDatepickerContentComponent, isStandalone: true, selector: "ngxsmk-datepicker-content", inputs: { isCalendarVisible: "isCalendarVisible", isCalendarOpen: "isCalendarOpen", isInlineMode: "isInlineMode", shouldAppendToBody: "shouldAppendToBody", theme: "theme", popoverId: "popoverId", classes: "classes", timeOnly: "timeOnly", showTime: "showTime", isMobile: "isMobile", mobileModalStyle: "mobileModalStyle", align: "align", ariaLabel: "ariaLabel", isCalendarOpening: "isCalendarOpening", loadingMessage: "loadingMessage", showRanges: "showRanges", rangesArray: "rangesArray", mode: "mode", disabled: "disabled", calendarCount: "calendarCount", calendarLayout: "calendarLayout", syncScrollEnabled: "syncScrollEnabled", calendarMonths: "calendarMonths", weekDays: "weekDays", weekDaysFull: "weekDaysFull", showOtherMonths: "showOtherMonths", showWeekNumbers: "showWeekNumbers", weekNumberLabel: "weekNumberLabel", secondaryCalendar: "secondaryCalendar", secondaryCalendarLocale: "secondaryCalendarLocale", selectedDate: "selectedDate", startDate: "startDate", endDate: "endDate", focusedDate: "focusedDate", today: "today", dateTemplate: "dateTemplate", calendarViewMode: "calendarViewMode", monthOptions: "monthOptions", currentMonth: "currentMonth", yearOptions: "yearOptions", currentYear: "currentYear", isBackArrowDisabled: "isBackArrowDisabled", prevMonthAriaLabel: "prevMonthAriaLabel", nextMonthAriaLabel: "nextMonthAriaLabel", yearGrid: "yearGrid", currentDecade: "currentDecade", decadeGrid: "decadeGrid", timelineStartDate: "timelineStartDate", timelineEndDate: "timelineEndDate", timelineMonths: "timelineMonths", minuteInterval: "minuteInterval", startTimeSlider: "startTimeSlider", endTimeSlider: "endTimeSlider", timeRangeMode: "timeRangeMode", hourOptions: "hourOptions", minuteOptions: "minuteOptions", secondOptions: "secondOptions", ampmOptions: "ampmOptions", currentDisplayHour: "currentDisplayHour", currentMinute: "currentMinute", currentSecond: "currentSecond", isPm: "isPm", showSeconds: "showSeconds", use24Hour: "use24Hour", startDisplayHour: "startDisplayHour", startMinute: "startMinute", startSecond: "startSecond", startIsPm: "startIsPm", endDisplayHour: "endDisplayHour", endMinute: "endMinute", endSecond: "endSecond", endIsPm: "endIsPm", clearAriaLabel: "clearAriaLabel", clearLabel: "clearLabel", closeAriaLabel: "closeAriaLabel", closeLabel: "closeLabel", translations: "translations", selectedRange: "selectedRange", showTimezoneSelector: "showTimezoneSelector", timezoneOptions: "timezoneOptions", currentTimezone: "currentTimezone", boundIsDateDisabled: "boundIsDateDisabled", boundIsYearDisabled: "boundIsYearDisabled", boundIsDecadeDisabled: "boundIsDecadeDisabled", boundGetDayMetadata: "boundGetDayMetadata", calendarHeaderTemplate: "calendarHeaderTemplate", calendarFooterTemplate: "calendarFooterTemplate", boundIsSameDay: "boundIsSameDay", boundIsHoliday: "boundIsHoliday", boundIsMultipleSelected: "boundIsMultipleSelected", boundIsInRange: "boundIsInRange", boundIsInComparisonRange: "boundIsInComparisonRange", boundIsPreviewInRange: "boundIsPreviewInRange", boundGetAriaLabel: "boundGetAriaLabel", boundGetDayCellCustomClasses: "boundGetDayCellCustomClasses", boundGetDayCellTooltip: "boundGetDayCellTooltip", boundFormatDayNumber: "boundFormatDayNumber", getMonthYearLabel: "getMonthYearLabel", getCalendarAriaLabelForMonth: "getCalendarAriaLabelForMonth", isTimelineMonthSelected: "isTimelineMonthSelected", formatTimeSliderValue: "formatTimeSliderValue", enableAi: "enableAi", aiPlaceholder: "aiPlaceholder", aiSuggestions: "aiSuggestions", showAiSuggestions: "showAiSuggestions", isAiResolving: "isAiResolving" }, outputs: { 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" }, viewQueries: [{ propertyName: "header", first: true, predicate: CalendarHeaderComponent, descendants: true }, { propertyName: "popoverContainer", first: true, predicate: ["popoverContainer"], descendants: true }, { propertyName: "timelineContainer", first: true, predicate: ["timelineContainer"], descendants: true }, { propertyName: "aiInputElement", first: true, predicate: ["aiInputElement"], descendants: true }], ngImport: i0, template: `
@if (isCalendarVisible) {
@if (!isInlineMode && isCalendarOpen) {
<div
class="ngxsmk-backdrop"
[class.ngxsmk-backdrop-allow-modal-scroll]="shouldAppendToBody"
[class.dark-theme]="theme === 'dark'"
role="button"
tabindex="0"
[attr.aria-label]="translations?.closeCalendarOverlay ?? ''"
(click)="backdropClick.emit($event)"
(keydown.enter)="backdropClick.emit($event)"
(keydown.space)="backdropClick.emit($event)"
></div>
}
<div
#popoverContainer
[id]="popoverId"
class="ngxsmk-popover-container"
[class.dark-theme]="theme === 'dark'"
[class.ngxsmk-inline-container]="isInlineMode"
[class.ngxsmk-popover-open]="isCalendarOpen && !isInlineMode"
[class.ngxsmk-time-only-popover]="timeOnly"
[class.ngxsmk-has-time-selection]="showTime || timeOnly"
[class.ngxsmk-bottom-sheet]="isMobile && mobileModalStyle === 'bottom-sheet' && !isInlineMode"
[class.ngxsmk-fullscreen]="isMobile && mobileModalStyle === 'fullscreen' && !isInlineMode"
[class.ngxsmk-align-left]="align === 'left'"
[class.ngxsmk-align-right]="align === 'right'"
[class.ngxsmk-align-center]="align === 'center'"
[ngClass]="classes?.popover"
tabindex="-1"
role="dialog"
[attr.aria-label]="ariaLabel"
[attr.aria-modal]="!isInlineMode"
(touchstart)="touchStartContainer.emit($event)"
(touchmove)="touchMoveContainer.emit($event)"
(touchend)="touchEndContainer.emit($event)"
(mousedown)="$event.stopPropagation()"
(keydown)="containerKeyDown.emit($event)"
(keydown.escape)="onPopoverEscape($event)"
>
<div class="ngxsmk-datepicker-container" [ngClass]="classes?.container">
@if (isCalendarOpening) {
<div class="ngxsmk-calendar-loading" role="status" aria-live="polite" [attr.aria-label]="loadingMessage">
<div class="ngxsmk-calendar-loading-spinner"></div>
<span class="ngxsmk-calendar-loading-text">{{ loadingMessage }}</span>
</div>
}
@if (enableAi) {
<div class="ngxsmk-ai-container">
<div class="ngxsmk-ai-bar" role="search">
<div class="ngxsmk-ai-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
<path
d="M19 9l1.25-2.75L23 5l-2.75-1.25L19 1l-1.25 2.75L15 5l2.75 1.25L19 9zm-7.5.5L9 4 6.5 9.5 1 12l5.5 2.5L9 20l2.5-5.5L17 12l-5.5-2.5zM19 15l-1.25 2.75L15 19l2.75 1.25L19 23l1.25-2.75L23 19l-2.75-1.25L19 15z"
/>
</svg>
</div>
<input
#aiInputElement
type="text"
class="ngxsmk-ai-input"
[placeholder]="aiPlaceholder"
[(ngModel)]="aiPromptText"
[disabled]="isAiResolving"
(keydown.enter)="onAiSubmit()"
(keydown)="$event.stopPropagation()"
[attr.aria-label]="aiPlaceholder"
/>
<button
type="button"
class="ngxsmk-ai-submit"
[class.is-loading]="isAiResolving"
(click)="onAiSubmit()"
[disabled]="isAiResolving || !aiPromptText || !aiPromptText.trim()"
aria-label="Submit AI prompt"
>
@if (isAiResolving) {
<span class="ngxsmk-ai-spinner" aria-hidden="true"></span>
} @else {
<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor">
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" />
</svg>
}
</button>
</div>
@if (showAiSuggestions && aiSuggestions.length > 0) {
<div class="ngxsmk-ai-chips">
@for (chip of aiSuggestions; track chip) {
<button
type="button"
class="ngxsmk-ai-chip"
(click)="onAiChipClick(chip)"
[disabled]="isAiResolving"
>
{{ chip }}
</button>
}
</div>
}
</div>
}
@if (calendarHeaderTemplate) {
<div class="ngxsmk-custom-header-slot">
<ng-container *ngTemplateOutlet="calendarHeaderTemplate; context: slotContext"></ng-container>
</div>
}
@if (showRanges && rangesArray.length > 0 && mode === 'range' && !timeOnly) {
<ngxsmk-datepicker-presets
[ranges]="rangesArray"
[selectedRange]="selectedRange"
[disabled]="disabled"
[classes]="classes"
(rangeSelected)="rangeSelect.emit($event)"
></ngxsmk-datepicker-presets>
}
<div
class="ngxsmk-calendar-container"
[class.ngxsmk-time-only-mode]="timeOnly"
[class.ngxsmk-has-multi-calendar]="calendarCount > 1"
[class.ngxsmk-calendar-layout-horizontal]="calendarCount > 1 && calendarLayout === 'horizontal'"
[class.ngxsmk-calendar-layout-vertical]="calendarCount > 1 && calendarLayout === 'vertical'"
[class.ngxsmk-calendar-layout-auto]="calendarCount > 1 && calendarLayout === 'auto'"
[ngClass]="classes?.calendar"
>
@if (!timeOnly) {
@if (calendarViewMode === 'month') {
<ngxsmk-calendar-header
[headerClass]="classes?.header ?? ''"
[navPrevClass]="classes?.navPrev ?? ''"
[navNextClass]="classes?.navNext ?? ''"
[monthOptions]="monthOptions"
[currentMonth]="currentMonth"
[yearOptions]="yearOptions"
[currentYear]="currentYear"
[disabled]="disabled"
[isBackArrowDisabled]="isBackArrowDisabled"
[prevMonthAriaLabel]="prevMonthAriaLabel"
[nextMonthAriaLabel]="nextMonthAriaLabel"
(previousMonth)="previousMonth.emit()"
(nextMonth)="nextMonth.emit()"
(currentMonthChange)="currentMonthChange.emit($event)"
(currentYearChange)="currentYearChange.emit($event)"
>
</ngxsmk-calendar-header>
<div
class="ngxsmk-multi-calendar-container"
[class.ngxsmk-multi-calendar]="calendarCount > 1"
[class.ngxsmk-calendar-horizontal]="calendarCount > 1 && calendarLayout === 'horizontal'"
[class.ngxsmk-calendar-vertical]="calendarCount > 1 && calendarLayout === 'vertical'"
[class.ngxsmk-calendar-auto]="calendarCount > 1 && calendarLayout === 'auto'"
[class.ngxsmk-sync-scroll-enabled]="syncScrollEnabled && calendarCount > 1"
>
@for (calendarMonth of calendarMonths; track calendarMonth.month + '-' + calendarMonth.year) {
<div class="ngxsmk-calendar-month" [class.ngxsmk-calendar-month-multi]="calendarCount > 1">
@if (calendarCount > 1) {
<div class="ngxsmk-calendar-month-header">
<span class="ngxsmk-calendar-month-title">{{
getMonthYearLabel(calendarMonth.month, calendarMonth.year)
}}</span>
</div>
}
<ngxsmk-calendar-month-view
[days]="calendarMonth.days"
[weekDays]="weekDays"
[weekDaysFull]="weekDaysFull"
[showOtherMonths]="showOtherMonths"
[showWeekNumbers]="showWeekNumbers"
[weekNumberLabel]="weekNumberLabel"
[secondaryCalendar]="secondaryCalendar"
[secondaryCalendarLocale]="secondaryCalendarLocale"
[classes]="classes"
[mode]="mode"
[selectedDate]="selectedDate"
[startDate]="startDate"
[endDate]="endDate"
[focusedDate]="focusedDate"
[today]="today"
[currentMonth]="calendarMonth.month"
[currentYear]="calendarMonth.year"
[ariaLabel]="getCalendarAriaLabelForMonth(calendarMonth.month, calendarMonth.year)"
[dateTemplate]="dateTemplate"
[isDateDisabled]="boundIsDateDisabled"
[getDayMetadata]="boundGetDayMetadata"
[isSameDay]="boundIsSameDay"
[isHoliday]="boundIsHoliday"
[isMultipleSelected]="boundIsMultipleSelected"
[isInRange]="boundIsInRange"
[isInComparisonRange]="boundIsInComparisonRange"
[isPreviewInRange]="boundIsPreviewInRange"
[getAriaLabel]="boundGetAriaLabel"
[getDayCellCustomClasses]="boundGetDayCellCustomClasses"
[getDayCellTooltip]="boundGetDayCellTooltip"
[formatDayNumber]="boundFormatDayNumber"
(dateClick)="dateClick.emit($event)"
(dateHover)="dateHover.emit($event)"
(dateFocus)="dateFocus.emit($event)"
(swipeStart)="swipeStart.emit($event)"
(swipeMove)="swipeMove.emit($event)"
(swipeEnd)="swipeEnd.emit($event)"
(touchStart)="touchStart.emit($event)"
(touchMove)="touchMove.emit($event)"
(touchEnd)="touchEnd.emit($event)"
>
</ngxsmk-calendar-month-view>
</div>
}
</div>
}
@if (calendarViewMode === 'year') {
<ngxsmk-calendar-year-view
viewMode="year"
[yearGrid]="yearGrid"
[currentYear]="currentYear"
[currentDecade]="currentDecade"
[today]="today"
[disabled]="disabled"
[isYearDisabled]="boundIsYearDisabled"
[selectedDate]="selectedDate"
[startDate]="startDate"
[mode]="mode"
[headerClass]="classes?.header ?? ''"
[navPrevClass]="classes?.navPrev ?? ''"
[navNextClass]="classes?.navNext ?? ''"
[previousYearsLabel]="translations?.previousYears ?? ''"
[nextYearsLabel]="translations?.nextYears ?? ''"
(viewModeChange)="viewModeChange.emit($event)"
(changeYear)="changeYear.emit($event)"
(yearClick)="yearClick.emit($event)"
>
</ngxsmk-calendar-year-view>
}
@if (calendarViewMode === 'decade') {
<ngxsmk-calendar-year-view
viewMode="decade"
[decadeGrid]="decadeGrid"
[currentDecade]="currentDecade"
[disabled]="disabled"
[isDecadeDisabled]="boundIsDecadeDisabled"
[headerClass]="classes?.header ?? ''"
[navPrevClass]="classes?.navPrev ?? ''"
[navNextClass]="classes?.navNext ?? ''"
[previousDecadeLabel]="translations?.previousDecade ?? ''"
[nextDecadeLabel]="translations?.nextDecade ?? ''"
(changeDecade)="changeDecade.emit($event)"
(decadeClick)="decadeClick.emit($event)"
>
</ngxsmk-calendar-year-view>
}
@if (calendarViewMode === 'timeline' && mode === 'range') {
<div class="ngxsmk-timeline-view">
<div class="ngxsmk-timeline-header">
<div class="ngxsmk-timeline-controls">
<button
type="button"
class="ngxsmk-timeline-zoom-out"
(click)="timelineZoomOut.emit()"
[disabled]="disabled"
>
-
</button>
<span class="ngxsmk-timeline-range"
>{{ timelineStartDate | date: 'shortDate' }} - {{ timelineEndDate | date: 'shortDate' }}</span
>
<button
type="button"
class="ngxsmk-timeline-zoom-in"
(click)="timelineZoomIn.emit()"
[disabled]="disabled"
>
+
</button>
</div>
</div>
<div class="ngxsmk-timeline-container" #timelineContainer>
<div class="ngxsmk-timeline-track">
@for (month of timelineMonths; track month.getTime()) {
<div
class="ngxsmk-timeline-month"
[class.selected]="isTimelineMonthSelected(month)"
(click)="onTimelineMonthClick(month, $event)"
(keydown.enter)="timelineMonthClick.emit(month)"
(keydown.space)="onTimelineMonthSpace(month, $event)"
role="button"
tabindex="0"
[attr.aria-label]="month | date: 'MMMM yyyy'"
>
<div class="ngxsmk-timeline-month-label">
{{ month | date: 'MMM' }}
</div>
<div class="ngxsmk-timeline-month-year">
{{ month | date: 'yyyy' }}
</div>
</div>
}
</div>
</div>
</div>
}
@if (calendarViewMode === 'time-slider' && mode === 'range' && showTime) {
<div class="ngxsmk-time-slider-view">
<div class="ngxsmk-time-slider-header">
<div class="ngxsmk-time-slider-label">
{{ translations?.startTime ?? '' }}
</div>
<div class="ngxsmk-time-slider-value">
{{ formatTimeSliderValue(startTimeSlider) }}
</div>
</div>
<div class="ngxsmk-time-slider-container">
<input
#startTimeInput
type="range"
class="ngxsmk-time-slider"
[min]="0"
[max]="1440"
[step]="minuteInterval"
[value]="startTimeSlider"
(input)="startTimeSliderChange.emit(+startTimeInput.value)"
[disabled]="disabled"
/>
</div>
<div class="ngxsmk-time-slider-header">
<div class="ngxsmk-time-slider-label">
{{ translations?.endTime ?? '' }}
</div>
<div class="ngxsmk-time-slider-value">
{{ formatTimeSliderValue(endTimeSlider) }}
</div>
</div>
<div class="ngxsmk-time-slider-container">
<input
#endTimeInput
type="range"
class="ngxsmk-time-slider"
[min]="0"
[max]="1440"
[step]="minuteInterval"
[value]="endTimeSlider"
(input)="endTimeSliderChange.emit(+endTimeInput.value)"
[disabled]="disabled"
/>
</div>
</div>
}
}
@if (showTime || timeOnly) {
@if (!timeRangeMode) {
<ngxsmk-time-selection
[hourOptions]="hourOptions"
[minuteOptions]="minuteOptions"
[secondOptions]="secondOptions"
[ampmOptions]="ampmOptions"
[currentDisplayHour]="currentDisplayHour"
[currentMinute]="currentMinute"
[currentSecond]="currentSecond"
[isPm]="isPm"
[showSeconds]="showSeconds"
[disabled]="disabled"
[timeLabel]="translations?.time ?? ''"
[showAmpm]="!use24Hour"
(currentDisplayHourChange)="currentDisplayHourChange.emit($event)"
(currentMinuteChange)="currentMinuteChange.emit($event)"
(currentSecondChange)="currentSecondChange.emit($event)"
(isPmChange)="isPmChange.emit($event)"
(timeChange)="timeChange.emit()"
>
</ngxsmk-time-selection>
} @else {
<div class="ngxsmk-time-range-container">
<div class="ngxsmk-time-range-start">
<span class="ngxsmk-time-range-label">{{ translations?.from ?? '' }}</span>
<ngxsmk-time-selection
[hourOptions]="hourOptions"
[minuteOptions]="minuteOptions"
[secondOptions]="secondOptions"
[ampmOptions]="ampmOptions"
[currentDisplayHour]="startDisplayHour"
[currentMinute]="startMinute"
[currentSecond]="startSecond"
[isPm]="startIsPm"
[showSeconds]="showSeconds"
[disabled]="disabled"
timeLabel=""
[showAmpm]="!use24Hour"
(currentDisplayHourChange)="startDisplayHourChange.emit($event)"
(currentMinuteChange)="startMinuteChange.emit($event)"
(currentSecondChange)="startSecondChange.emit($event)"
(isPmChange)="startIsPmChange.emit($event)"
(timeChange)="timeRangeChange.emit()"
>
</ngxsmk-time-selection>
</div>
<div class="ngxsmk-time-range-end">
<span class="ngxsmk-time-range-label">{{ translations?.to ?? '' }}</span>
<ngxsmk-time-selection
[hourOptions]="hourOptions"
[minuteOptions]="minuteOptions"
[secondOptions]="secondOptions"
[ampmOptions]="ampmOptions"
[currentDisplayHour]="endDisplayHour"
[currentMinute]="endMinute"
[currentSecond]="endSecond"
[isPm]="endIsPm"
[showSeconds]="showSeconds"
[disabled]="disabled"
timeLabel=""
[showAmpm]="!use24Hour"
(currentDisplayHourChange)="endDisplayHourChange.emit($event)"
(currentMinuteChange)="endMinuteChange.emit($event)"
(currentSecondChange)="endSecondChange.emit($event)"
(isPmChange)="endIsPmChange.emit($event)"
(timeChange)="timeRangeChange.emit()"
>
</ngxsmk-time-selection>
</div>
</div>
}
}
@if (showTimezoneSelector) {
<div class="ngxsmk-timezone-selection">
<span class="ngxsmk-timezone-label">{{ translations?.timezone || 'Timezone' }}:</span>
<ngxsmk-custom-select
[options]="timezoneOptions"
[value]="currentTimezone"
[disabled]="disabled"
(valueChange)="onTimezoneChange($event)"
></ngxsmk-custom-select>
</div>
}
@if (calendarFooterTemplate) {
<div class="ngxsmk-footer ngxsmk-custom-footer-slot" [ngClass]="classes?.footer">
<ng-container *ngTemplateOutlet="calendarFooterTemplate; context: slotContext"></ng-container>
</div>
} @else if (!isInlineMode) {
<div class="ngxsmk-footer" [ngClass]="classes?.footer">
<button
type="button"
class="ngxsmk-clear-button-footer"
(click)="clearValue.emit($event)"
[disabled]="disabled"
[attr.aria-label]="clearAriaLabel"
[ngClass]="classes?.clearBtn"
>
{{ clearLabel }}
</button>
<button
type="button"
class="ngxsmk-close-button"
(click)="closeCalendar.emit()"
[disabled]="disabled"
[attr.aria-label]="closeAriaLabel"
[ngClass]="classes?.closeBtn"
>
{{ closeLabel }}
</button>
</div>
}
</div>
</div>
</div>
}
`, isInline: true, dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: CalendarHeaderComponent, selector: "ngxsmk-calendar-header", inputs: ["monthOptions", "yearOptions", "currentMonth", "currentYear", "disabled", "isBackArrowDisabled", "prevMonthAriaLabel", "nextMonthAriaLabel", "headerClass", "navPrevClass", "navNextClass"], outputs: ["currentYearChange", "currentMonthChange", "previousMonth", "nextMonth"] }, { kind: "component", type: CalendarMonthViewComponent, selector: "ngxsmk-calendar-month-view", inputs: ["days", "weekDays", "weekDaysFull", "showOtherMonths", "classes", "dateTemplate", "dayTemplate", "mode", "selectedDate", "startDate", "endDate", "focusedDate", "today", "currentMonth", "currentYear", "ariaLabel", "showWeekNumbers", "weekNumberLabel", "secondaryCalendar", "secondaryCalendarLocale", "isDateDisabled", "isSameDay", "isHoliday", "isMultipleSelected", "isInRange", "isInComparisonRange", "isPreviewInRange", "getAriaLabel", "getDayCellCustomClasses", "getDayCellTooltip", "getDayMetadata", "formatDayNumber"], outputs: ["dateClick", "dateMouseDown", "dateMouseUp", "dateHover", "dateFocus", "swipeStart", "swipeMove", "swipeEnd", "touchStart", "touchMove", "touchEnd"] }, { kind: "component", type: CalendarYearViewComponent, selector: "ngxsmk-calendar-year-view", inputs: ["viewMode", "yearGrid", "decadeGrid", "currentYear", "currentDecade", "today", "disabled", "isYearDisabled", "isDecadeDisabled", "selectedDate", "startDate", "mode", "yearContainerHeight", "decadeContainerHeight", "previousYearsLabel", "nextYearsLabel", "previousDecadeLabel", "nextDecadeLabel", "headerClass", "navPrevClass", "navNextClass"], outputs: ["viewModeChange", "yearClick", "decadeClick", "changeYear", "changeDecade"] }, { kind: "component", type: TimeSelectionComponent, selector: "ngxsmk-time-selection", inputs: ["hourOptions", "minuteOptions", "secondOptions", "ampmOptions", "currentDisplayHour", "currentMinute", "currentSecond", "isPm", "disabled", "timeLabel", "showSeconds", "showAmpm"], outputs: ["timeChange", "currentDisplayHourChange", "currentMinuteChange", "currentSecondChange", "isPmChange"] }, { kind: "component", type: NgxsmkDatepickerPresetsComponent, selector: "ngxsmk-datepicker-presets", inputs: ["ranges", "disabled", "classes", "selectedRange"], outputs: ["rangeSelected"] }, { kind: "component", type: CustomSelectComponent, selector: "ngxsmk-custom-select", inputs: ["options", "value", "disabled"], outputs: ["valueChange"] }, { kind: "pipe", type: DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NgxsmkDatepickerContentComponent, decorators: [{
type: Component,
args: [{
selector: 'ngxsmk-datepicker-content',
standalone: true,
imports: [
NgClass,
NgTemplateOutlet,
DatePipe,
FormsModule,
CalendarHeaderComponent,
CalendarMonthViewComponent,
CalendarYearViewComponent,
TimeSelectionComponent,
NgxsmkDatepickerPresetsComponent,
CustomSelectComponent,
],
template: `
@if (isCalendarVisible) {
@if (!isInlineMode && isCalendarOpen) {
<div
class="ngxsmk-backdrop"
[class.ngxsmk-backdrop-allow-modal-scroll]="shouldAppendToBody"
[class.dark-theme]="theme === 'dark'"
role="button"
tabindex="0"
[attr.aria-label]="translations?.closeCalendarOverlay ?? ''"
(click)="backdropClick.emit($event)"
(keydown.enter)="backdropClick.emit($event)"
(keydown.space)="backdropClick.emit($event)"
></div>
}
<div
#popoverContainer
[id]="popoverId"
class="ngxsmk-popover-container"
[class.dark-theme]="theme === 'dark'"
[class.ngxsmk-inline-container]="isInlineMode"
[class.ngxsmk-popover-open]="isCalendarOpen && !isInlineMode"
[class.ngxsmk-time-only-popover]="timeOnly"
[class.ngxsmk-has-time-selection]="showTime || timeOnly"
[class.ngxsmk-bottom-sheet]="isMobile && mobileModalStyle === 'bottom-sheet' && !isInlineMode"
[class.ngxsmk-fullscreen]="isMobile && mobileModalStyle === 'fullscreen' && !isInlineMode"
[class.ngxsmk-align-left]="align === 'left'"
[class.ngxsmk-align-right]="align === 'right'"
[class.ngxsmk-align-center]="align === 'center'"
[ngClass]="classes?.popover"
tabindex="-1"
role="dialog"
[attr.aria-label]="ariaLabel"
[attr.aria-modal]="!isInlineMode"
(touchstart)="touchStartContainer.emit($event)"
(touchmove)="touchMoveContainer.emit($event)"
(touchend)="touchEndContainer.emit($event)"
(mousedown)="$event.stopPropagation()"
(keydown)="containerKeyDown.emit($event)"
(keydown.escape)="onPopoverEscape($event)"
>
<div class="ngxsmk-datepicker-container" [ngClass]="classes?.container">
@if (isCalendarOpening) {
<div class="ngxsmk-calendar-loading" role="status" aria-live="polite" [attr.aria-label]="loadingMessage">
<div class="ngxsmk-calendar-loading-spinner"></div>
<span class="ngxsmk-calendar-loading-text">{{ loadingMessage }}</span>
</div>
}
@if (enableAi) {
<div class="ngxsmk-ai-container">
<div class="ngxsmk-ai-bar" role="search">
<div class="ngxsmk-ai-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
<path
d="M19 9l1.25-2.75L23 5l-2.75-1.25L19 1l-1.25 2.75L15 5l2.75 1.25L19 9zm-7.5.5L9 4 6.5 9.5 1 12l5.5 2.5L9 20l2.5-5.5L17 12l-5.5-2.5zM19 15l-1.25 2.75L15 19l2.75 1.25L19 23l1.25-2.75L23 19l-2.75-1.25L19 15z"
/>
</svg>
</div>
<input
#aiInputElement
type="text"
class="ngxsmk-ai-input"
[placeholder]="aiPlaceholder"
[(ngModel)]="aiPromptText"
[disabled]="isAiResolving"
(keydown.enter)="onAiSubmit()"
(keydown)="$event.stopPropagation()"
[attr.aria-label]="aiPlaceholder"
/>
<button
type="button"
class="ngxsmk-ai-submit"
[class.is-loading]="isAiResolving"
(click)="onAiSubmit()"
[disabled]="isAiResolving || !aiPromptText || !aiPromptText.trim()"
aria-label="Submit AI prompt"
>
@if (isAiResolving) {
<span class="ngxsmk-ai-spinner" aria-hidden="true"></span>
} @else {
<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor">
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" />
</svg>
}
</button>
</div>
@if (showAiSuggestions && aiSuggestions.length > 0) {
<div class="ngxsmk-ai-chips">
@for (chip of aiSuggestions; track chip) {
<button
type="button"
class="ngxsmk-ai-chip"
(click)="onAiChipClick(chip)"
[disabled]="isAiResolving"
>
{{ chip }}
</button>
}
</div>
}
</div>
}
@if (calendarHeaderTemplate) {
<div class="ngxsmk-custom-header-slot">
<ng-container *ngTemplateOutlet="calendarHeaderTemplate; context: slotContext"></ng-container>
</div>
}
@if (showRanges && rangesArray.length > 0 && mode === 'range' && !timeOnly) {
<ngxsmk-datepicker-presets
[ranges]="rangesArray"
[selectedRange]="selectedRange"
[disabled]="disabled"
[classes]="classes"
(rangeSelected)="rangeSelect.emit($event)"
></ngxsmk-datepicker-presets>
}
<div
class="ngxsmk-calendar-container"
[class.ngxsmk-time-only-mode]="timeOnly"
[class.ngxsmk-has-multi-calendar]="calendarCount > 1"
[class.ngxsmk-calendar-layout-horizontal]="calendarCount > 1 && calendarLayout === 'horizontal'"
[class.ngxsmk-calendar-layout-vertical]="calendarCount > 1 && calendarLayout === 'vertical'"
[class.ngxsmk-calendar-layout-auto]="calendarCount > 1 && calendarLayout === 'auto'"
[ngClass]="classes?.calendar"
>
@if (!timeOnly) {
@if (calendarViewMode === 'month') {
<ngxsmk-calendar-header
[headerClass]="classes?.header ?? ''"
[navPrevClass]="classes?.navPrev ?? ''"
[navNextClass]="classes?.navNext ?? ''"
[monthOptions]="monthOptions"
[currentMonth]="currentMonth"
[yearOptions]="yearOptions"
[currentYear]="currentYear"
[disabled]="disabled"
[isBackArrowDisabled]="isBackArrowDisabled"
[prevMonthAriaLabel]="prevMonthAriaLabel"
[nextMonthAriaLabel]="nextMonthAriaLabel"
(previousMonth)="previousMonth.emit()"
(nextMonth)="nextMonth.emit()"
(currentMonthChange)="currentMonthChange.emit($event)"
(currentYearChange)="currentYearChange.emit($event)"
>
</ngxsmk-calendar-header>
<div
class="ngxsmk-multi-calendar-container"
[class.ngxsmk-multi-calendar]="calendarCount > 1"
[class.ngxsmk-calendar-horizontal]="calendarCount > 1 && calendarLayout === 'horizontal'"
[class.ngxsmk-calendar-vertical]="calendarCount > 1 && calendarLayout === 'vertical'"
[class.ngxsmk-calendar-auto]="calendarCount > 1 && calendarLayout === 'auto'"
[class.ngxsmk-sync-scroll-enabled]="syncScrollEnabled && calendarCount > 1"
>
@for (calendarMonth of calendarMonths; track calendarMonth.month + '-' + calendarMonth.year) {
<div class="ngxsmk-calendar-month" [class.ngxsmk-calendar-month-multi]="calendarCount > 1">
@if (calendarCount > 1) {
<div class="ngxsmk-calendar-month-header">
<span class="ngxsmk-calendar-month-title">{{
getMonthYearLabel(calendarMonth.month, calendarMonth.year)
}}</span>
</div>
}
<ngxsmk-calendar-month-view
[days]="calendarMonth.days"
[weekDays]="weekDays"
[weekDaysFull]="weekDaysFull"
[showOtherMonths]="showOtherMonths"
[showWeekNumbers]="showWeekNumbers"
[weekNumberLabel]="weekNumberLabel"
[secondaryCalendar]="secondaryCalendar"
[secondaryCalendarLocale]="secondaryCalendarLocale"
[classes]="classes"
[mode]="mode"
[selectedDate]="selectedDate"
[startDate]="startDate"
[endDate]="endDate"
[focusedDate]="focusedDate"
[today]="today"
[currentMonth]="calendarMonth.month"
[currentYear]="calendarMonth.year"
[ariaLabel]="getCalendarAriaLabelForMonth(calendarMonth.month, calendarMonth.year)"
[dateTemplate]="dateTemplate"
[isDateDisabled]="boundIsDateDisabled"
[getDayMetadata]="boundGetDayMetadata"
[isSameDay]="boundIsSameDay"
[isHoliday]="boundIsHoliday"
[isMultipleSelected]="boundIsMultipleSelected"
[isInRange]="boundIsInRange"
[isInComparisonRange]="boundIsInComparisonRange"
[isPreviewInRange]="boundIsPreviewInRange"
[getAriaLabel]="boundGetAriaLabel"
[getDayCellCustomClasses]="boundGetDayCellCustomClasses"
[getDayCellTooltip]="boundGetDayCellTooltip"
[formatDayNumber]="boundFormatDayNumber"
(dateClick)="dateClick.emit($event)"
(dateHover)="dateHover.emit($event)"
(dateFocus)="dateFocus.emit($event)"
(swipeStart)="swipeStart.emit($event)"
(swipeMove)="swipeMove.emit($event)"
(swipeEnd)="swipeEnd.emit($event)"
(touchStart)="touchStart.emit($event)"
(touchMove)="touchMove.emit($event)"
(touchEnd)="touchEnd.emit($event)"
>
</ngxsmk-calendar-month-view>
</div>
}
</div>
}
@if (calendarViewMode === 'year') {
<ngxsmk-calendar-year-view
viewMode="year"
[yearGrid]="yearGrid"
[currentYear]="currentYear"
[currentDecade]="currentDecade"
[today]="today"
[disabled]="disabled"
[isYearDisabled]="boundIsYearDisabled"
[selectedDate]="selectedDate"
[startDate]="startDate"
[mode]="mode"
[headerClass]="classes?.header ?? ''"
[navPrevClass]="classes?.navPrev ?? ''"
[navNextClass]="classes?.navNext ?? ''"
[previousYearsLabel]="translations?.previousYears ?? ''"
[nextYearsLabel]="translations?.nextYears ?? ''"
(viewModeChange)="viewModeChange.emit($event)"
(changeYear)="changeYear.emit($event)"
(yearClick)="yearClick.emit($event)"
>
</ngxsmk-calendar-year-view>
}
@if (calendarViewMode === 'decade') {
<ngxsmk-calendar-year-view
viewMode="decade"
[decadeGrid]="decadeGrid"
[currentDecade]="currentDecade"
[disabled]="disabled"
[isDecadeDisabled]="boundIsDecadeDisabled"
[headerClass]="classes?.header ?? ''"
[navPrevClass]="classes?.navPrev ?? ''"
[navNextClass]="classes?.navNext ?? ''"
[previousDecadeLabel]="translations?.previousDecade ?? ''"
[nextDecadeLabel]="translations?.nextDecade ?? ''"
(changeDecade)="changeDecade.emit($event)"
(decadeClick)="decadeClick.emit($event)"
>
</ngxsmk-calendar-year-view>
}
@if (calendarViewMode === 'timeline' && mode === 'range') {
<div class="ngxsmk-timeline-view">
<div class="ngxsmk-timeline-header">
<div class="ngxsmk-timeline-controls">
<button
type="button"
class="ngxsmk-timeline-zoom-out"
(click)="timelineZoomOut.emit()"
[disabled]="disabled"
>
-
</button>
<span class="ngxsmk-timeline-range"
>{{ timelineStartDate | date: 'shortDate' }} - {{ timelineEndDate | date: 'shortDate' }}</span
>
<button
type="button"
class="ngxsmk-timeline-zoom-in"
(click)="timelineZoomIn.emit()"
[disabled]="disabled"
>
+
</button>
</div>
</div>
<div class="ngxsmk-timeline-container" #timelineContainer>
<div class="ngxsmk-timeline-track">
@for (month of timelineMonths; track month.getTime()) {
<div
class="ngxsmk-timeline-month"
[class.selected]="isTimelineMonthSelected(month)"
(click)="onTimelineMonthClick(month, $event)"
(keydown.enter)="timelineMonthClick.emit(month)"
(keydown.space)="onTimelineMonthSpace(month, $event)"
role="button"
tabindex="0"
[attr.aria-label]="month | date: 'MMMM yyyy'"
>
<div class="ngxsmk-timeline-month-label">
{{ month | date: 'MMM' }}
</div>
<div class="ngxsmk-timeline-month-year">
{{ month | date: 'yyyy' }}
</div>
</div>
}
</div>
</div>
</div>
}
@if (calendarViewMode === 'time-slider' && mode === 'range' && showTime) {
<div class="ngxsmk-time-slider-view">
<div class="ngxsmk-time-slider-header">
<div class="ngxsmk-time-slider-label">
{{ translations?.startTime ?? '' }}
</div>
<div class="ngxsmk-time-slider-value">
{{ formatTimeSliderValue(startTimeSlider) }}
</div>
</div>
<div class="ngxsmk-time-slider-container">
<input
#startTimeInput
type="range"
class="ngxsmk-time-slider"
[min]="0"
[max]="1440"
[step]="minuteInterval"
[value]="startTimeSlider"
(input)="startTimeSliderChange.emit(+startTimeInput.value)"
[disabled]="disabled"
/>
</div>
<div class="ngxsmk-time-slider-header">
<div class="ngxsmk-time-slider-label">
{{ translations?.endTime ?? '' }}
</div>
<div class="ngxsmk-time-slider-value">
{{ formatTimeSliderValue(endTimeSlider) }}
</div>
</div>
<div class="ngxsmk-time-slider-container">
<input
#endTimeInput
type="range"
class="ngxsmk-time-slider"
[min]="0"
[max]="1440"
[step]="minuteInterval"
[value]="endTimeSlider"
(input)="endTimeSliderChange.emit(+endTimeInput.value)"
[disabled]="disabled"
/>
</div>
</div>
}
}
@if (showTime || timeOnly) {
@if (!timeRangeMode) {
<ngxsmk-time-selection
[hourOptions]="hourOptions"
[minuteOptions]="minuteOptions"
[secondOptions]="secondOptions"
[ampmOptions]="ampmOptions"
[currentDisplayHour]="currentDisplayHour"
[currentMinute]="currentMinute"
[currentSecond]="currentSecond"
[isPm]="isPm"
[showSeconds]="showSeconds"
[disabled]="disabled"
[timeLabel]="translations?.time ?? ''"
[showAmpm]="!use24Hour"
(currentDisplayHourChange)="currentDisplayHourChange.emit($event)"
(currentMinuteChange)="currentMinuteChange.emit($event)"
(currentSecondChange)="currentSecondChange.emit($event)"
(isPmChange)="isPmChange.emit($event)"
(timeChange)="timeChange.emit()"
>
</ngxsmk-time-selection>
} @else {
<div class="ngxsmk-time-range-container">
<div class="ngxsmk-time-range-start">
<span class="ngxsmk-time-range-label">{{ translations?.from ?? '' }}</span>
<ngxsmk-time-selection
[hourOptions]="hourOptions"
[minuteOptions]="minuteOptions"
[secondOptions]="secondOptions"
[ampmOptions]="ampmOptions"
[currentDisplayHour]="startDisplayHour"
[currentMinute]="startMinute"
[currentSecond]="startSecond"
[isPm]="startIsPm"
[showSeconds]="showSeconds"
[disabled]="disabled"
timeLabel=""
[showAmpm]="!use24Hour"
(currentDisplayHourChange)="startDisplayHourChange.emit($event)"
(currentMinuteChange)="startMinuteChange.emit($event)"
(currentSecondChange)="startSecondChange.emit($event)"
(isPmChange)="startIsPmChange.emit($event)"
(timeChange)="timeRangeChange.emit()"
>
</ngxsmk-time-selection>
</div>
<div class="ngxsmk-time-range-end">
<span class="ngxsmk-time-range-label">{{ translations?.to ?? '' }}</span>
<ngxsmk-time-selection
[hourOptions]="hourOptions"
[minuteOptions]="minuteOptions"
[secondOptions]="secondOptions"
[ampmOptions]="ampmOptions"
[currentDisplayHour]="endDisplayHour"
[currentMinute]="endMinute"
[currentSecond]="endSecond"
[isPm]="endIsPm"
[showSeconds]="showSeconds"
[disabled]="disabled"
timeLabel=""
[showAmpm]="!use24Hour"
(currentDisplayHourChange)="endDisplayHourChange.emit($event)"
(currentMinuteChange)="endMinuteChange.emit($event)"
(currentSecondChange)="endSecondChange.emit($event)"
(isPmChange)="endIsPmChange.emit($event)"
(timeChange)="timeRangeChange.emit()"
>
</ngxsmk-time-selection>
</div>
</div>
}
}
@if (showTimezoneSelector) {
<div class="ngxsmk-timezone-selection">
<span class="ngxsmk-timezone-label">{{ translations?.timezone || 'Timezone' }}:</span>
<ngxsmk-custom-select
[options]="timezoneOptions"
[value]="currentTimezone"
[disabled]="disabled"
(valueChange)="onTimezoneChange($event)"
></ngxsmk-custom-select>
</div>
}
@if (calendarFooterTemplate) {
<div class="ngxsmk-footer ngxsmk-custom-footer-slot" [ngClass]="classes?.footer">
<ng-container *ngTemplateOutlet="calendarFooterTemplate; context: slotContext"></ng-container>
</div>
} @else if (!isInlineMode) {
<div class="ngxsmk-footer" [ngClass]="classes?.footer">
<button
type="button"
class="ngxsmk-clear-button-footer"
(click)="clearValue.emit($event)"
[disabled]="disabled"
[attr.aria-label]="clearAriaLabel"
[ngClass]="classes?.clearBtn"
>
{{ clearLabel }}
</button>
<button
type="button"
class="ngxsmk-close-button"
(click)="closeCalendar.emit()"
[disabled]="disabled"
[attr.aria-label]="closeAriaLabel"
[ngClass]="classes?.closeBtn"
>
{{ closeLabel }}
</button>
</div>
}
</div>
</div>
</div>
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
}]
}], propDecorators: { isCalendarVisible: [{
type: Input
}], isCalendarOpen: [{
type: Input
}], isInlineMode: [{
type: Input
}], shouldAppendToBody: [{
type: Input
}], theme: [{
type: Input
}], popoverId: [{
type: Input
}], classes: [{
type: Input
}], timeOnly: [{
type: Input
}], showTime: [{
type: Input
}], isMobile: [{
type: Input
}], mobileModalStyle: [{
type: Input
}], align: [{
type: Input
}], ariaLabel: [{
type: Input
}], isCalendarOpening: [{
type: Input
}], loadingMessage: [{
type: Input
}], showRanges: [{
type: Input
}], rangesArray: [{
type: Input
}], mode: [{
type: Input
}], disabled: [{
type: Input
}], calendarCount: [{
type: Input
}], calendarLayout: [{
type: Input
}], syncScrollEnabled: [{
type: Input
}], calendarMonths: [{
type: Input
}], weekDays: [{
type: Input
}], weekDaysFull: [{
type: Input
}], showOtherMonths: [{
type: Input
}], showWeekNumbers: [{
type: Input
}], weekNumberLabel: [{
type: Input
}], secondaryCalendar: [{
type: Input
}], secondaryCalendarLocale: [{
type: Input
}], selectedDate: [{
type: Input
}], startDate: [{
type: Input
}], endDate: [{
type: Input
}], focusedDate: [{
type: Input
}], today: [{
type: Input
}], dateTemplate: [{
type: Input
}], calendarViewMode: [{
type: Input
}], monthOptions: [{
type: Input
}], currentMonth: [{
type: Input
}], yearOptions: [{
type: Input
}], currentYear: [{
type: Input
}], isBackArrowDisabled: [{
type: Input
}], prevMonthAriaLabel: [{
type: Input
}], nextMonthAriaLabel: [{
type: Input
}], yearGrid: [{
type: Input
}], currentDecade: [{
type: Input
}], decadeGrid: [{
type: Input
}], timelineStartDate: [{
type: Input
}], timelineEndDate: [{
type: Input
}], timelineMonths: [{
type: Input
}], minuteInterval: [{
type: Input
}], startTimeSlider: [{
type: Input
}], endTimeSlider: [{
type: Input
}], timeRangeMode: [{
type: Input
}], hourOptions: [{
type: Input
}], minuteOptions: [{
type: Input
}], secondOptions: [{
type: Input
}], ampmOptions: [{
type: Input
}], currentDisplayHour: [{
type: Input
}], currentMinute: [{
type: Input
}], currentSecond: [{
type: Input
}], isPm: [{
type: Input
}], showSeconds: [{
type: Input
}], use24Hour: [{
type: Input
}], startDisplayHour: [{
type: Input
}], startMinute: [{
type: Input
}], startSecond: [{
type: Input
}], startIsPm: [{
type: Input
}], endDisplayHour: [{
type: Input
}], endMinute: [{
type: Input
}], endSecond: [{
type: Input
}], endIsPm: [{
type: Input
}], clearAriaLabel: [{
type: Input
}], clearLabel: [{
type: Input
}], closeAriaLabel: [{
type: Input
}], closeLabel: [{
type: Input
}], translations: [{
type: Input
}], selectedRange: [{
type: Input
}], showTimezoneSelector: [{
type: Input
}], timezoneOptions: [{
type: Input
}], currentTimezone: [{
type: Input
}], timezoneChange: [{
type: Output
}], boundIsDateDisabled: [{
type: Input
}], boundIsYearDisabled: [{
type: Input
}], boundIsDecadeDisabled: [{
type: Input
}], boundGetDayMetadata: [{
type: Input
}], calendarHeaderTemplate: [{
type: Input
}], calendarFooterTemplate: [{
type: Input
}], boundIsSameDay: [{
type: Input
}], boundIsHoliday: [{
type: Input
}], boundIsMultipleSelected: [{
type: Input
}], boundIsInRange: [{
type: Input
}], boundIsInComparisonRange: [{
type: Input
}], boundIsPreviewInRange: [{
type: Input
}], boundGetAriaLabel: [{
type: Input
}], boundGetDayCellCustomClasses: [{
type: Input
}], boundGetDayCellTooltip: [{
type: Input
}], boundFormatDayNumber: [{
type: Input
}], getMonthYearLabel: [{
type: Input
}], getCalendarAriaLabelForMonth: [{
type: Input
}], isTimelineMonthSelected: [{
type: Input
}], formatTimeSliderValue: [{
type: Input
}], backdropClick: [{
type: Output
}], touchStartContainer: [{
type: Output
}], touchMoveContainer: [{
type: Output
}], touchEndContainer: [{
type: Output
}], rangeSelect: [{
type: Output
}], previousMonth: [{
type: Output
}], nextMonth: [{
type: Output
}], currentMonthChange: [{
type: Output
}], currentYearChange: [{
type: Output
}], dateClick: [{
type: Output
}], dateHover: [{
type: Output
}], dateFocus: [{
type: Output
}], swipeStart: [{
type: Output
}], swipeMove: [{
type: Output
}], swipeEnd: [{
type: Output
}], touchStart: [{
type: Output
}], touchMove: [{
type: Output
}], touchEnd: [{
type: Output
}], viewModeChange: [{
type: Output
}], changeYear: [{
type: Output
}], yearClick: [{
type: Output
}], changeDecade: [{
type: Output
}], decadeClick: [{
type: Output
}], timelineZoomOut: [{
type: Output
}], timelineZoomIn: [{
type: Output
}], timelineMonthClick: [{
type: Output
}], startTimeSliderChange: [{
type: Output
}], endTimeSliderChange: [{
type: Output
}], currentDisplayHourChange: [{
type: Output
}], currentMinuteChange: [{
type: Output
}], currentSecondChange: [{
type: Output
}], isPmChange: [{
type: Output
}], timeChange: [{
type: Output
}], startDisplayHourChange: [{
type: Output
}], startMinuteChange: [{
type: Output
}], startSecondChange: [{
type: Output
}], startIsPmChange: [{
type: Output
}], endDisplayHourChange: [{
type: Output
}], endMinuteChange: [{
type: Output
}], endSecondChange: [{
type: Output
}], endIsPmChange: [{
type: Output
}], timeRangeChange: [{
type: Output
}], clearValue: [{
type: Output
}], closeCalendar: [{
type: Output
}], escapeKey: [{
type: Output
}], containerKeyDown: [{
type: Output
}], header: [{
type: ViewChild,
args: [CalendarHeaderComponent]
}], popoverContainer: [{
type: ViewChild,
args: ['popoverContainer']
}], timelineContainer: [{
type: ViewChild,
args: ['timelineContainer']
}], aiInputElement: [{
type: ViewChild,
args: ['aiInputElement']
}], enableAi: [{
type: Input
}], aiPlaceholder: [{
type: Input
}], aiSuggestions: [{
type: Input
}], showAiSuggestions: [{
type: Input
}], isAiResolving: [{
type: Input
}], aiPromptSubmitted: [{
type: Output
}] } });
class NgxsmkDatepickerKeyboardHelpComponent {
constructor() {
this.title = input('Keyboard shortcuts', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
this.closeLabel = input('Close', ...(ngDevMode ? [{ debugName: "closeLabel" }] : /* istanbul ignore next */ []));
this.backdropLabel = input('Close overlay', ...(ngDevMode ? [{ debugName: "backdropLabel" }] : /* istanbul ignore next */ []));
this.closeRequested = output();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NgxsmkDatepickerKeyboardHelpComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.11", type: NgxsmkDatepickerKeyboardHelpComponent, isStandalone: true, selector: "ngxsmk-datepicker-keyboard-help", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null }, backdropLabel: { classPropertyName: "backdropLabel", publicName: "backdropLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closeRequested: "closeRequested" }, ngImport: i0, template: `
<div
class="ngxsmk-keyboard-help-backdrop"
(click)="closeRequested.emit()"
(keydown.enter)="closeRequested.emit()"
(keydown.space)="closeRequested.emit()"
tabindex="0"
role="button"
[attr.aria-label]="backdropLabel()"
></div>
<div class="ngxsmk-keyboard-help-dialog" role="dialog" aria-modal="true" [attr.aria-label]="title()">
<div class="ngxsmk-keyboard-help-header">
<h3 class="ngxsmk-keyboard-help-title">{{ title() }}</h3>
<button
type="button"
class="ngxsmk-keyboard-help-close"
(click)="closeRequested.emit()"
[attr.aria-label]="closeLabel()"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
<ul class="ngxsmk-keyboard-help-list">
<li class="ngxsmk-keyboard-help-item">
<span>Next/Prev day</span>
<div>
<span class="ngxsmk-keyboard-key">←</span>
<span class="ngxsmk-keyboard-key">→</span>
</div>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Next/Prev week</span>
<div>
<span class="ngxsmk-keyboard-key">↑</span>
<span class="ngxsmk-keyboard-key">↓</span>
</div>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Select date</span>
<div>
<span class="ngxsmk-keyboard-key">Enter</span>
<span class="ngxsmk-keyboard-key">Space</span>
</div>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Next/Prev month</span>
<div>
<span class="ngxsmk-keyboard-key">PgUp</span>
<span class="ngxsmk-keyboard-key">PgDn</span>
</div>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Next/Prev year</span>
<div>
<span class="ngxsmk-keyboard-key">Shift</span> +
<span class="ngxsmk-keyboard-key">PgUp/Dn</span>
</div>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>First/Last day</span>
<div>
<span class="ngxsmk-keyboard-key">Home</span>
<span class="ngxsmk-keyboard-key">End</span>
</div>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Close calendar</span>
<span class="ngxsmk-keyboard-key">Esc</span>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Today</span>
<span class="ngxsmk-keyboard-key">T</span>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Yesterday</span>
<span class="ngxsmk-keyboard-key">Y</span>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Tomorrow</span>
<span class="ngxsmk-keyboard-key">N</span>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Next Week</span>
<span class="ngxsmk-keyboard-key">W</span>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Show shortcuts</span>
<span class="ngxsmk-keyboard-key">?</span>
</li>
</ul>
</div>
`, isInline: true, styles: ["ngxsmk-datepicker,.ngxsmk-popover-container,.ngxsmk-backdrop{--datepicker-primary-color: var(--ion-color-primary, #6d28d9);--datepicker-primary-contrast: var(--ion-color-primary-contrast, #ffffff);--datepicker-range-background: var(--ion-color-primary-tint, #f5f3ff);--datepicker-comparison-range-color: #f59e0b;--datepicker-background: var(--ion-background-color, #ffffff);--datepicker-text-color: var(--ion-text-color, #1f2937);--datepicker-subtle-text-color: var(--ion-text-color-step-400, #6b7280);--datepicker-border-color: var(--ion-item-border-color, var(--ion-border-color, #e5e7eb));--datepicker-hover-background: #f3f4f6;--datepicker-shadow-focus: 0 0 0 3px color-mix(in srgb, var(--datepicker-primary-color) 15%, transparent);--ngxsmk-color-primary: var(--datepicker-primary-color);--ngxsmk-color-on-primary: var(--datepicker-primary-contrast);--ngxsmk-color-range-bg: var(--datepicker-range-background);--ngxsmk-color-surface: var(--datepicker-background);--ngxsmk-color-surface-hover: var(--datepicker-hover-background);--ngxsmk-color-text-main: var(--datepicker-text-color);--ngxsmk-color-text-muted: var(--datepicker-subtle-text-color);--ngxsmk-color-border: var(--datepicker-border-color);--datepicker-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);--datepicker-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .06);--datepicker-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--datepicker-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, .1), 0 10px 10px -5px rgba(0, 0, 0, .04);--datepicker-font-size-xs: 10px;--datepicker-font-size-sm: 12px;--datepicker-font-size-base: 14px;--datepicker-font-size-lg: 16px;--datepicker-font-size-xl: 18px;--datepicker-line-height: 1.5;--datepicker-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;--datepicker-spacing-xs: 4px;--datepicker-spacing-sm: 8px;--datepicker-spacing-md: 12px;--datepicker-spacing-lg: 16px;--datepicker-spacing-xl: 20px;--datepicker-spacing-2xl: 24px;--datepicker-radius-sm: 6px;--datepicker-radius-md: 8px;--datepicker-radius-lg: 12px;--datepicker-radius-xl: 16px;--datepicker-border-radius: var(--datepicker-radius-lg);--datepicker-transition-duration: .15s;--datepicker-transition-easing: cubic-bezier(.4, 0, .2, 1);--datepicker-transition-property: all;--datepicker-transition: var(--datepicker-transition-property) var(--datepicker-transition-duration) var(--datepicker-transition-easing);--datepicker-z-index-base: 2147483647;--datepicker-z-index-backdrop: 2147483646}ngxsmk-datepicker.dark-theme,.ngxsmk-popover-container.dark-theme,.ngxsmk-backdrop.dark-theme{--datepicker-range-background: rgba(139, 92, 246, .15);--datepicker-background: #1f2937;--datepicker-text-color: #f3f4f6;--datepicker-subtle-text-color: #9ca3af;--datepicker-border-color: #374151;--datepicker-hover-background: #374151;--datepicker-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .3);--datepicker-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .3), 0 2px 4px -1px rgba(0, 0, 0, .2);--datepicker-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .3), 0 4px 6px -2px rgba(0, 0, 0, .2)}ngxsmk-datepicker.dark-theme *{--datepicker-range-background: rgba(139, 92, 246, .15);--datepicker-background: #1f2937;--datepicker-text-color: #f3f4f6;--datepicker-subtle-text-color: #9ca3af;--datepicker-border-color: #374151;--datepicker-hover-background: #374151}ngxsmk-datepicker.glass-theme,.ngxsmk-popover-container.glass-theme{--datepicker-background: rgba(255, 255, 255, .7);--datepicker-border-color: rgba(255, 255, 255, .3);--datepicker-shadow-lg: 0 8px 32px 0 rgba(31, 38, 135, .37);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--datepicker-border-color)}ngxsmk-datepicker.glass-theme.dark-theme,.ngxsmk-popover-container.glass-theme.dark-theme{--datepicker-background: rgba(31, 41, 55, .7);--datepicker-border-color: rgba(255, 255, 255, .1)}ngxsmk-datepicker.md3-theme{--datepicker-primary-color: #6750a4;--datepicker-radius-lg: 28px;--datepicker-font-family: \"Roboto\", sans-serif;--datepicker-shadow-md: 0px 1px 3px 1px rgba(0, 0, 0, .15), 0px 1px 2px rgba(0, 0, 0, .3)}@media(prefers-contrast:high){ngxsmk-datepicker{--datepicker-border-color: #000000;--datepicker-text-color: #000000;--datepicker-subtle-text-color: #000000;--datepicker-background: #ffffff;--datepicker-hover-background: #f0f0f0;--datepicker-primary-color: #0000ff;--datepicker-primary-contrast: #ffffff;--datepicker-range-background: #e0e0e0;--datepicker-focus-outline: #000000}ngxsmk-datepicker.dark-theme{--datepicker-border-color: #ffffff;--datepicker-text-color: #ffffff;--datepicker-subtle-text-color: #ffffff;--datepicker-background: #000000;--datepicker-hover-background: #333333;--datepicker-primary-color: #ffffff;--datepicker-primary-contrast: #000000;--datepicker-range-background: #333333;--datepicker-focus-outline: #ffffff}ngxsmk-datepicker *{border-color:var(--datepicker-border-color)!important}.ngxsmk-day-cell{border:2px solid transparent!important}.ngxsmk-day-cell:not(.disabled):not(.empty):hover{border-color:var(--datepicker-border-color)!important;background-color:var(--datepicker-hover-background)!important}.ngxsmk-day-cell.disabled{opacity:.5!important;border-color:#ccc!important}.ngxsmk-day-cell.selected,.ngxsmk-day-cell.start-date,.ngxsmk-day-cell.end-date{border:3px solid var(--datepicker-border-color)!important;background-color:var(--datepicker-primary-color)!important;color:var(--datepicker-primary-contrast)!important}.ngxsmk-day-cell.focused,.ngxsmk-day-cell:focus-visible{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-nav-button{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-nav-button.focused,.ngxsmk-nav-button:focus-visible{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-nav-button:hover:not(:disabled){background-color:var(--datepicker-hover-background)!important;border-color:var(--datepicker-border-color)!important}.ngxsmk-input-group{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-input-group:focus-within{border-color:var(--datepicker-border-color)!important;outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-clear-button,.ngxsmk-calendar-button{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-clear-button.focused,.ngxsmk-clear-button:focus-visible,.ngxsmk-calendar-button.focused,.ngxsmk-calendar-button:focus-visible{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-popover-container{border:3px solid var(--datepicker-border-color)!important;box-shadow:0 4px 8px #0000004d!important}.ngxsmk-day-cell.in-range{background-color:var(--datepicker-range-background)!important;border-color:var(--datepicker-border-color)!important}.ngxsmk-custom-select{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-custom-select.focused,.ngxsmk-custom-select:focus-within{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-time-input{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-time-input.focused,.ngxsmk-time-input:focus{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}}@media(prefers-reduced-motion:reduce){ngxsmk-datepicker{--datepicker-transition: none}ngxsmk-datepicker *,ngxsmk-datepicker *:before,ngxsmk-datepicker *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}\n", ".ngxsmk-keyboard-help-dialog{display:block;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:var(--datepicker-background);border-radius:var(--datepicker-border-radius);box-shadow:var(--datepicker-shadow-lg);z-index:calc(var(--datepicker-z-index-backdrop) + 10);padding:20px;width:90%;max-width:400px;max-height:80vh;overflow-y:auto;color:var(--datepicker-text-color);animation:fadeInScale .2s ease-out}.ngxsmk-keyboard-help-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;border-bottom:1px solid var(--datepicker-border-color);padding-bottom:10px}.ngxsmk-keyboard-help-title{font-size:18px;font-weight:600;margin:0}.ngxsmk-keyboard-help-close{background:none;border:none;cursor:pointer;padding:4px;color:var(--datepicker-subtle-text-color);border-radius:var(--datepicker-radius-sm);display:flex;align-items:center;justify-content:center}.ngxsmk-keyboard-help-close:hover{background:var(--datepicker-hover-background);color:var(--datepicker-text-color)}.ngxsmk-keyboard-help-list{list-style:none;padding:0;margin:0;font-size:14px}.ngxsmk-keyboard-help-item{display:flex;justify-content:space-between;align-items:center;padding:8px 0;border-bottom:1px solid var(--datepicker-border-color)}.ngxsmk-keyboard-help-item:last-child{border-bottom:none}.ngxsmk-keyboard-key{background:var(--datepicker-hover-background);border:1px solid var(--datepicker-border-color);border-radius:4px;padding:2px 6px;font-family:monospace;font-size:12px;display:inline-block;min-width:20px;text-align:center;margin-left:4px}.ngxsmk-keyboard-help-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:#00000080;z-index:var(--datepicker-z-index-backdrop);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NgxsmkDatepickerKeyboardHelpComponent, decorators: [{
type: Component,
args: [{ selector: 'ngxsmk-datepicker-keyboard-help', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `
<div
class="ngxsmk-keyboard-help-backdrop"
(click)="closeRequested.emit()"
(keydown.enter)="closeRequested.emit()"
(keydown.space)="closeRequested.emit()"
tabindex="0"
role="button"
[attr.aria-label]="backdropLabel()"
></div>
<div class="ngxsmk-keyboard-help-dialog" role="dialog" aria-modal="true" [attr.aria-label]="title()">
<div class="ngxsmk-keyboard-help-header">
<h3 class="ngxsmk-keyboard-help-title">{{ title() }}</h3>
<button
type="button"
class="ngxsmk-keyboard-help-close"
(click)="closeRequested.emit()"
[attr.aria-label]="closeLabel()"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
<ul class="ngxsmk-keyboard-help-list">
<li class="ngxsmk-keyboard-help-item">
<span>Next/Prev day</span>
<div>
<span class="ngxsmk-keyboard-key">←</span>
<span class="ngxsmk-keyboard-key">→</span>
</div>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Next/Prev week</span>
<div>
<span class="ngxsmk-keyboard-key">↑</span>
<span class="ngxsmk-keyboard-key">↓</span>
</div>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Select date</span>
<div>
<span class="ngxsmk-keyboard-key">Enter</span>
<span class="ngxsmk-keyboard-key">Space</span>
</div>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Next/Prev month</span>
<div>
<span class="ngxsmk-keyboard-key">PgUp</span>
<span class="ngxsmk-keyboard-key">PgDn</span>
</div>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Next/Prev year</span>
<div>
<span class="ngxsmk-keyboard-key">Shift</span> +
<span class="ngxsmk-keyboard-key">PgUp/Dn</span>
</div>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>First/Last day</span>
<div>
<span class="ngxsmk-keyboard-key">Home</span>
<span class="ngxsmk-keyboard-key">End</span>
</div>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Close calendar</span>
<span class="ngxsmk-keyboard-key">Esc</span>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Today</span>
<span class="ngxsmk-keyboard-key">T</span>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Yesterday</span>
<span class="ngxsmk-keyboard-key">Y</span>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Tomorrow</span>
<span class="ngxsmk-keyboard-key">N</span>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Next Week</span>
<span class="ngxsmk-keyboard-key">W</span>
</li>
<li class="ngxsmk-keyboard-help-item">
<span>Show shortcuts</span>
<span class="ngxsmk-keyboard-key">?</span>
</li>
</ul>
</div>
`, styles: ["ngxsmk-datepicker,.ngxsmk-popover-container,.ngxsmk-backdrop{--datepicker-primary-color: var(--ion-color-primary, #6d28d9);--datepicker-primary-contrast: var(--ion-color-primary-contrast, #ffffff);--datepicker-range-background: var(--ion-color-primary-tint, #f5f3ff);--datepicker-comparison-range-color: #f59e0b;--datepicker-background: var(--ion-background-color, #ffffff);--datepicker-text-color: var(--ion-text-color, #1f2937);--datepicker-subtle-text-color: var(--ion-text-color-step-400, #6b7280);--datepicker-border-color: var(--ion-item-border-color, var(--ion-border-color, #e5e7eb));--datepicker-hover-background: #f3f4f6;--datepicker-shadow-focus: 0 0 0 3px color-mix(in srgb, var(--datepicker-primary-color) 15%, transparent);--ngxsmk-color-primary: var(--datepicker-primary-color);--ngxsmk-color-on-primary: var(--datepicker-primary-contrast);--ngxsmk-color-range-bg: var(--datepicker-range-background);--ngxsmk-color-surface: var(--datepicker-background);--ngxsmk-color-surface-hover: var(--datepicker-hover-background);--ngxsmk-color-text-main: var(--datepicker-text-color);--ngxsmk-color-text-muted: var(--datepicker-subtle-text-color);--ngxsmk-color-border: var(--datepicker-border-color);--datepicker-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);--datepicker-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .06);--datepicker-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--datepicker-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, .1), 0 10px 10px -5px rgba(0, 0, 0, .04);--datepicker-font-size-xs: 10px;--datepicker-font-size-sm: 12px;--datepicker-font-size-base: 14px;--datepicker-font-size-lg: 16px;--datepicker-font-size-xl: 18px;--datepicker-line-height: 1.5;--datepicker-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;--datepicker-spacing-xs: 4px;--datepicker-spacing-sm: 8px;--datepicker-spacing-md: 12px;--datepicker-spacing-lg: 16px;--datepicker-spacing-xl: 20px;--datepicker-spacing-2xl: 24px;--datepicker-radius-sm: 6px;--datepicker-radius-md: 8px;--datepicker-radius-lg: 12px;--datepicker-radius-xl: 16px;--datepicker-border-radius: var(--datepicker-radius-lg);--datepicker-transition-duration: .15s;--datepicker-transition-easing: cubic-bezier(.4, 0, .2, 1);--datepicker-transition-property: all;--datepicker-transition: var(--datepicker-transition-property) var(--datepicker-transition-duration) var(--datepicker-transition-easing);--datepicker-z-index-base: 2147483647;--datepicker-z-index-backdrop: 2147483646}ngxsmk-datepicker.dark-theme,.ngxsmk-popover-container.dark-theme,.ngxsmk-backdrop.dark-theme{--datepicker-range-background: rgba(139, 92, 246, .15);--datepicker-background: #1f2937;--datepicker-text-color: #f3f4f6;--datepicker-subtle-text-color: #9ca3af;--datepicker-border-color: #374151;--datepicker-hover-background: #374151;--datepicker-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .3);--datepicker-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .3), 0 2px 4px -1px rgba(0, 0, 0, .2);--datepicker-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .3), 0 4px 6px -2px rgba(0, 0, 0, .2)}ngxsmk-datepicker.dark-theme *{--datepicker-range-background: rgba(139, 92, 246, .15);--datepicker-background: #1f2937;--datepicker-text-color: #f3f4f6;--datepicker-subtle-text-color: #9ca3af;--datepicker-border-color: #374151;--datepicker-hover-background: #374151}ngxsmk-datepicker.glass-theme,.ngxsmk-popover-container.glass-theme{--datepicker-background: rgba(255, 255, 255, .7);--datepicker-border-color: rgba(255, 255, 255, .3);--datepicker-shadow-lg: 0 8px 32px 0 rgba(31, 38, 135, .37);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--datepicker-border-color)}ngxsmk-datepicker.glass-theme.dark-theme,.ngxsmk-popover-container.glass-theme.dark-theme{--datepicker-background: rgba(31, 41, 55, .7);--datepicker-border-color: rgba(255, 255, 255, .1)}ngxsmk-datepicker.md3-theme{--datepicker-primary-color: #6750a4;--datepicker-radius-lg: 28px;--datepicker-font-family: \"Roboto\", sans-serif;--datepicker-shadow-md: 0px 1px 3px 1px rgba(0, 0, 0, .15), 0px 1px 2px rgba(0, 0, 0, .3)}@media(prefers-contrast:high){ngxsmk-datepicker{--datepicker-border-color: #000000;--datepicker-text-color: #000000;--datepicker-subtle-text-color: #000000;--datepicker-background: #ffffff;--datepicker-hover-background: #f0f0f0;--datepicker-primary-color: #0000ff;--datepicker-primary-contrast: #ffffff;--datepicker-range-background: #e0e0e0;--datepicker-focus-outline: #000000}ngxsmk-datepicker.dark-theme{--datepicker-border-color: #ffffff;--datepicker-text-color: #ffffff;--datepicker-subtle-text-color: #ffffff;--datepicker-background: #000000;--datepicker-hover-background: #333333;--datepicker-primary-color: #ffffff;--datepicker-primary-contrast: #000000;--datepicker-range-background: #333333;--datepicker-focus-outline: #ffffff}ngxsmk-datepicker *{border-color:var(--datepicker-border-color)!important}.ngxsmk-day-cell{border:2px solid transparent!important}.ngxsmk-day-cell:not(.disabled):not(.empty):hover{border-color:var(--datepicker-border-color)!important;background-color:var(--datepicker-hover-background)!important}.ngxsmk-day-cell.disabled{opacity:.5!important;border-color:#ccc!important}.ngxsmk-day-cell.selected,.ngxsmk-day-cell.start-date,.ngxsmk-day-cell.end-date{border:3px solid var(--datepicker-border-color)!important;background-color:var(--datepicker-primary-color)!important;color:var(--datepicker-primary-contrast)!important}.ngxsmk-day-cell.focused,.ngxsmk-day-cell:focus-visible{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-nav-button{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-nav-button.focused,.ngxsmk-nav-button:focus-visible{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-nav-button:hover:not(:disabled){background-color:var(--datepicker-hover-background)!important;border-color:var(--datepicker-border-color)!important}.ngxsmk-input-group{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-input-group:focus-within{border-color:var(--datepicker-border-color)!important;outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-clear-button,.ngxsmk-calendar-button{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-clear-button.focused,.ngxsmk-clear-button:focus-visible,.ngxsmk-calendar-button.focused,.ngxsmk-calendar-button:focus-visible{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-popover-container{border:3px solid var(--datepicker-border-color)!important;box-shadow:0 4px 8px #0000004d!important}.ngxsmk-day-cell.in-range{background-color:var(--datepicker-range-background)!important;border-color:var(--datepicker-border-color)!important}.ngxsmk-custom-select{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-custom-select.focused,.ngxsmk-custom-select:focus-within{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-time-input{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-time-input.focused,.ngxsmk-time-input:focus{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}}@media(prefers-reduced-motion:reduce){ngxsmk-datepicker{--datepicker-transition: none}ngxsmk-datepicker *,ngxsmk-datepicker *:before,ngxsmk-datepicker *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}\n", ".ngxsmk-keyboard-help-dialog{display:block;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:var(--datepicker-background);border-radius:var(--datepicker-border-radius);box-shadow:var(--datepicker-shadow-lg);z-index:calc(var(--datepicker-z-index-backdrop) + 10);padding:20px;width:90%;max-width:400px;max-height:80vh;overflow-y:auto;color:var(--datepicker-text-color);animation:fadeInScale .2s ease-out}.ngxsmk-keyboard-help-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;border-bottom:1px solid var(--datepicker-border-color);padding-bottom:10px}.ngxsmk-keyboard-help-title{font-size:18px;font-weight:600;margin:0}.ngxsmk-keyboard-help-close{background:none;border:none;cursor:pointer;padding:4px;color:var(--datepicker-subtle-text-color);border-radius:var(--datepicker-radius-sm);display:flex;align-items:center;justify-content:center}.ngxsmk-keyboard-help-close:hover{background:var(--datepicker-hover-background);color:var(--datepicker-text-color)}.ngxsmk-keyboard-help-list{list-style:none;padding:0;margin:0;font-size:14px}.ngxsmk-keyboard-help-item{display:flex;justify-content:space-between;align-items:center;padding:8px 0;border-bottom:1px solid var(--datepicker-border-color)}.ngxsmk-keyboard-help-item:last-child{border-bottom:none}.ngxsmk-keyboard-key{background:var(--datepicker-hover-background);border:1px solid var(--datepicker-border-color);border-radius:4px;padding:2px 6px;font-family:monospace;font-size:12px;display:inline-block;min-width:20px;text-align:center;margin-left:4px}.ngxsmk-keyboard-help-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:#00000080;z-index:var(--datepicker-z-index-backdrop);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}\n"] }]
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], backdropLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "backdropLabel", required: false }] }], closeRequested: [{ type: i0.Output, args: ["closeRequested"] }] } });
function memoize(fn, keyGenerator) {
const cache = new Map();
return ((...args) => {
const key = keyGenerator ? keyGenerator(...args) : JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;
});
}
function debounce(func, wait) {
let timeout = null;
return (...args) => {
if (timeout !== null) {
clearTimeout(timeout);
}
if (typeof setTimeout !== 'undefined') {
timeout = setTimeout(() => {
func(...args);
}, wait);
}
};
}
function throttle(func, limit) {
let inThrottle = false;
return (...args) => {
if (!inThrottle) {
func(...args);
inThrottle = true;
if (typeof setTimeout !== 'undefined') {
setTimeout(() => (inThrottle = false), limit);
}
else {
inThrottle = false;
}
}
};
}
function shallowEqual(a, b) {
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) {
return false;
}
for (const key of keysA) {
if (a[key] !== b[key]) {
return false;
}
}
return true;
}
function createDateComparator() {
const cache = new Map();
const MAX_CACHE_SIZE = 1000;
return (date1, date2) => {
if (!date1 || !date2)
return date1 === date2;
const key = `${date1.getTime()}-${date2.getTime()}`;
if (cache.has(key)) {
return cache.get(key);
}
const result = date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate();
if (cache.size >= MAX_CACHE_SIZE) {
const firstKey = cache.keys().next().value;
if (firstKey !== undefined) {
cache.delete(firstKey);
}
}
cache.set(key, result);
return result;
};
}
// Module-level cache for createFilteredArray
const filteredArrayCache = new Map();
const MAX_FILTERED_ARRAY_CACHE_SIZE = 100;
function createFilteredArray(source, filterFn, cacheKey) {
const key = cacheKey || JSON.stringify(source);
if (filteredArrayCache.has(key)) {
return filteredArrayCache.get(key);
}
const result = source.filter(filterFn);
// Limit cache size to prevent memory leaks
if (filteredArrayCache.size >= MAX_FILTERED_ARRAY_CACHE_SIZE) {
const firstKey = filteredArrayCache.keys().next().value;
if (firstKey !== undefined) {
filteredArrayCache.delete(firstKey);
}
}
filteredArrayCache.set(key, result);
return result;
}
function clearAllCaches() {
filteredArrayCache.clear();
}
/**
* Simple input masking utility for date formats
*/
function applyDateMask(value, format) {
if (!value)
return '';
const cleanValue = value.replace(/\D/g, '');
let result = '';
let cleanIdx = 0;
for (let i = 0; i < format.length && cleanIdx < cleanValue.length; i++) {
const char = format[i];
if (char && /[a-zA-Z]/.test(char)) {
result += cleanValue[cleanIdx++];
}
else {
result += char || '';
}
}
return result;
}
/**
* Calculates virtual scroll window for large date ranges
*/
function getVirtualScrollWindow(totalItems, scrollTop, itemHeight, buffer = 5) {
const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - buffer);
const visibleCount = Math.ceil(400 / itemHeight); // Approximate container height
const endIndex = Math.min(totalItems, startIndex + visibleCount + buffer * 2);
return {
startIndex,
endIndex,
offsetY: startIndex * itemHeight,
};
}
/**
* Default Date Adapter using native JavaScript Date
*/
class NativeDateAdapter {
parse(value, onError) {
if (!value)
return null;
try {
if (value instanceof Date) {
if (isNaN(value.getTime())) {
onError?.(new Error(`Invalid Date object: ${value}`));
return null;
}
return new Date(value.getTime());
}
if (typeof value === 'string') {
const parsed = new Date(value);
if (isNaN(parsed.getTime())) {
onError?.(new Error(`Invalid date string: "${value}"`));
return null;
}
return parsed;
}
if (typeof value === 'number') {
const parsed = new Date(value);
if (isNaN(parsed.getTime())) {
onError?.(new Error(`Invalid date timestamp: ${value}`));
return null;
}
return parsed;
}
}
catch (error) {
onError?.(error instanceof Error ? error : new Error(String(error)));
return null;
}
return null;
}
format(date, _format, locale) {
if (!date || isNaN(date.getTime()))
return '';
const options = {
year: 'numeric',
month: 'short',
day: '2-digit',
};
return new Intl.DateTimeFormat(locale || 'en-US', options).format(date);
}
isValid(value) {
if (!value)
return false;
if (value instanceof Date) {
return !isNaN(value.getTime());
}
return false;
}
startOfDay(date) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
}
endOfDay(date) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);
}
addMonths(date, months) {
const newDate = new Date(date);
newDate.setMonth(date.getMonth() + months);
return newDate;
}
addDays(date, days) {
const newDate = new Date(date);
newDate.setDate(date.getDate() + days);
return newDate;
}
isSameDay(date1, date2) {
if (!date1 || !date2)
return false;
return (date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate());
}
}
const DEFAULT_ANIMATION_CONFIG = {
enabled: true,
duration: 150,
easing: 'ease-in-out',
property: 'all',
respectReducedMotion: true,
};
const DATEPICKER_CONFIG = new InjectionToken('DATEPICKER_CONFIG');
const DEFAULT_DATEPICKER_CONFIG = {
weekStart: null,
minuteInterval: 1,
holidayProvider: null,
yearRange: 10,
dateAdapter: new NativeDateAdapter(),
animations: DEFAULT_ANIMATION_CONFIG,
};
function provideDatepickerConfig(config) {
return {
provide: DATEPICKER_CONFIG,
useValue: { ...DEFAULT_DATEPICKER_CONFIG, ...config },
};
}
function safeIsSignal(value) {
if (value === null || value === undefined) {
return false;
}
try {
const ngCore = globalThis.ng?.core;
if (ngCore?.isSignal && typeof ngCore.isSignal === 'function') {
return ngCore.isSignal(value);
}
}
catch { }
if (typeof value === 'function') {
const fn = value;
try {
if (fn.length === 0 || fn.length === undefined) {
return true;
}
}
catch {
return false;
}
}
return false;
}
class FieldSyncService {
constructor() {
this._fieldEffectRef = null;
this._lastKnownFieldValue = undefined;
this._isUpdatingFromInternal = false;
this.injector = inject(Injector);
}
readFieldValue(field) {
if (!field || typeof field !== 'object' || field.value === undefined) {
return null;
}
try {
const fieldValue = field.value;
if (typeof fieldValue === 'function') {
try {
const result = fieldValue();
if (safeIsSignal(result)) {
const signalResult = result();
return signalResult !== undefined && signalResult !== null ? signalResult : null;
}
if (result !== undefined && result !== null) {
return result;
}
return null;
}
catch {
if (safeIsSignal(fieldValue)) {
try {
const signalResult = fieldValue();
return signalResult !== undefined && signalResult !== null ? signalResult : null;
}
catch {
return null;
}
}
return null;
}
}
if (safeIsSignal(fieldValue)) {
const result = fieldValue();
return result !== undefined && result !== null ? result : null;
}
if (fieldValue !== null && typeof fieldValue === 'object') {
if (fieldValue instanceof Date) {
return fieldValue;
}
try {
const result = fieldValue();
if (result !== undefined && result !== null) {
return result;
}
}
catch {
return fieldValue;
}
}
// Direct value (shouldn't happen often, but handle it)
if (fieldValue !== undefined && fieldValue !== null) {
return fieldValue;
}
return null;
}
catch {
return null;
}
}
readDisabledState(field) {
if (!field || (typeof field !== 'object' && typeof field !== 'function')) {
return false;
}
try {
if ('disabled' in field && field.disabled !== undefined) {
const disabledVal = field.disabled;
if (safeIsSignal(disabledVal)) {
return !!disabledVal();
}
if (typeof disabledVal === 'function') {
return !!disabledVal();
}
if (typeof disabledVal === 'object' && disabledVal !== null) {
try {
return !!disabledVal();
}
catch {
return !!disabledVal;
}
}
return !!disabledVal;
}
return false;
}
catch {
// Silently return false on error to allow graceful degradation
return false;
}
}
readFieldErrors(field) {
if (!field || (typeof field !== 'object' && typeof field !== 'function')) {
return [];
}
try {
if ('errors' in field && field.errors !== undefined) {
const errorsVal = field.errors;
if (safeIsSignal(errorsVal)) {
const result = errorsVal();
return Array.isArray(result) ? result : [];
}
if (typeof errorsVal === 'function') {
const result = errorsVal();
return Array.isArray(result) ? result : [];
}
if (typeof errorsVal === 'object' && errorsVal !== null) {
try {
const result = errorsVal();
return Array.isArray(result) ? result : [];
}
catch {
return Array.isArray(errorsVal) ? errorsVal : [];
}
}
return Array.isArray(errorsVal) ? errorsVal : [];
}
return [];
}
catch {
return [];
}
}
readRequiredState(field) {
if (!field || (typeof field !== 'object' && typeof field !== 'function')) {
return false;
}
try {
const errors = this.readFieldErrors(field);
const hasRequiredError = errors.some((error) => error.kind === 'required');
if (hasRequiredError) {
return true;
}
if ('required' in field && field.required !== undefined) {
const requiredVal = field.required;
if (safeIsSignal(requiredVal)) {
return !!requiredVal();
}
if (typeof requiredVal === 'function') {
return !!requiredVal();
}
if (typeof requiredVal === 'object' && requiredVal !== null) {
try {
return !!requiredVal();
}
catch {
return !!requiredVal;
}
}
return !!requiredVal;
}
return false;
}
catch {
return false;
}
}
hasValidationErrors(field) {
if (!field || (typeof field !== 'object' && typeof field !== 'function')) {
return false;
}
try {
if ('invalid' in field && field.invalid !== undefined) {
const invalidVal = field.invalid;
if (safeIsSignal(invalidVal)) {
return !!invalidVal();
}
if (typeof invalidVal === 'function') {
return !!invalidVal();
}
if (typeof invalidVal === 'object' && invalidVal !== null) {
try {
return !!invalidVal();
}
catch {
return !!invalidVal;
}
}
return !!invalidVal;
}
// Fallback: check if errors array has any errors
const errors = this.readFieldErrors(field);
return errors.length > 0;
}
catch {
return false;
}
}
resolveField(field) {
if (!field)
return null;
if (typeof field === 'object') {
return field;
}
if (typeof field === 'function') {
const fieldFn = field;
const hasFieldProps = 'value' in fieldFn ||
'disabled' in fieldFn ||
'required' in fieldFn ||
'setValue' in fieldFn ||
'markAsDirty' in fieldFn ||
'errors' in fieldFn;
if (hasFieldProps) {
return fieldFn;
}
if (safeIsSignal(field)) {
try {
const result = field();
if (result && typeof result === 'object') {
return result;
}
}
catch { }
}
// Generic fallback for getters
try {
const result = field();
if (result && typeof result === 'object') {
return result;
}
}
catch {
// Fall through
}
}
return null;
}
setupFieldSync(fieldInput, callbacks) {
this.cleanup();
if (!fieldInput) {
return null;
}
try {
const effectRef = runInInjectionContext(this.injector, () => effect(() => {
if (this._isUpdatingFromInternal) {
return;
}
const field = this.resolveField(fieldInput);
if (!field) {
return;
}
let fieldValue = null;
try {
const fieldValueRef = field.value;
if (fieldValueRef === undefined || fieldValueRef === null) {
fieldValue = null;
}
else if (typeof fieldValueRef === 'function') {
try {
const funcResult = fieldValueRef();
if (safeIsSignal(funcResult)) {
const signalResult = funcResult();
fieldValue = signalResult !== undefined && signalResult !== null ? signalResult : null;
}
else if (safeIsSignal(fieldValueRef)) {
const signalResult = fieldValueRef();
fieldValue = signalResult !== undefined && signalResult !== null ? signalResult : null;
}
else {
fieldValue = funcResult !== undefined && funcResult !== null ? funcResult : null;
}
}
catch {
if (safeIsSignal(fieldValueRef)) {
try {
const signalResult = fieldValueRef();
fieldValue = signalResult !== undefined && signalResult !== null ? signalResult : null;
}
catch {
fieldValue = null;
}
}
else {
fieldValue = null;
}
}
}
else if (safeIsSignal(fieldValueRef)) {
const signalResult = fieldValueRef();
fieldValue =
signalResult !== undefined && signalResult !== null ? signalResult : null;
}
else if (fieldValueRef instanceof Date) {
fieldValue = fieldValueRef;
}
else if (typeof fieldValueRef === 'object') {
fieldValue = fieldValueRef;
}
else {
fieldValue = this.readFieldValue(field);
}
}
catch {
fieldValue = this.readFieldValue(field);
}
const normalizedValue = callbacks.normalizeValue(fieldValue);
const isInitialLoad = this._lastKnownFieldValue === undefined;
const valuesAreEqual = this._lastKnownFieldValue !== undefined &&
callbacks.isValueEqual(normalizedValue, this._lastKnownFieldValue);
const valueChanged = !isInitialLoad && !valuesAreEqual;
const isValueTransition = (this._lastKnownFieldValue === null || this._lastKnownFieldValue === undefined) &&
fieldValue !== null &&
fieldValue !== undefined;
if ((isInitialLoad || valueChanged || isValueTransition) && !valuesAreEqual) {
this._lastKnownFieldValue = normalizedValue;
callbacks.onValueChanged(normalizedValue);
callbacks.onCalendarGenerated?.();
callbacks.onStateChanged?.();
}
else if (!valuesAreEqual && this._lastKnownFieldValue !== normalizedValue) {
this._lastKnownFieldValue = normalizedValue;
}
const disabled = this.readDisabledState(field);
callbacks.onDisabledChanged(disabled);
const required = this.readRequiredState(field);
callbacks.onRequiredChanged?.(required);
const hasError = this.hasValidationErrors(field);
callbacks.onErrorStateChanged?.(hasError);
}));
this._fieldEffectRef = effectRef;
return effectRef;
}
catch (error) {
// Fall back to manual sync if effect setup fails
callbacks.onSyncError?.(error);
this.syncFieldValue(fieldInput, callbacks);
return null;
}
}
syncFieldValue(fieldInput, callbacks) {
const field = this.resolveField(fieldInput);
if (!field)
return false;
const fieldValue = this.readFieldValue(field);
const normalizedValue = callbacks.normalizeValue(fieldValue);
const hasValueChanged = !callbacks.isValueEqual(normalizedValue, this._lastKnownFieldValue);
const isInitialLoad = this._lastKnownFieldValue === undefined;
const isValueTransition = (this._lastKnownFieldValue === null || this._lastKnownFieldValue === undefined) &&
fieldValue !== null &&
fieldValue !== undefined;
if (isInitialLoad || hasValueChanged || isValueTransition) {
this._lastKnownFieldValue = normalizedValue;
callbacks.onValueChanged(normalizedValue);
callbacks.onCalendarGenerated?.();
callbacks.onStateChanged?.();
const disabled = this.readDisabledState(field);
callbacks.onDisabledChanged(disabled);
const required = this.readRequiredState(field);
callbacks.onRequiredChanged?.(required);
const hasError = this.hasValidationErrors(field);
callbacks.onErrorStateChanged?.(hasError);
return true;
}
if (this._lastKnownFieldValue !== normalizedValue) {
this._lastKnownFieldValue = normalizedValue;
}
const disabled = this.readDisabledState(field);
callbacks.onDisabledChanged(disabled);
const required = this.readRequiredState(field);
callbacks.onRequiredChanged?.(required);
const hasError = this.hasValidationErrors(field);
callbacks.onErrorStateChanged?.(hasError);
return false;
}
updateFieldFromInternal(value, fieldInput) {
const field = this.resolveField(fieldInput);
if (!field || typeof field !== 'object') {
return;
}
this._isUpdatingFromInternal = true;
try {
const normalizedValue = value;
this._lastKnownFieldValue = normalizedValue;
if (typeof field.setValue === 'function') {
try {
field.setValue(normalizedValue);
if (typeof field.markAsDirty === 'function') {
field.markAsDirty();
}
Promise.resolve().then(() => {
this._isUpdatingFromInternal = false;
});
return;
}
catch { }
}
if (typeof field.updateValue === 'function') {
try {
field.updateValue(() => normalizedValue);
if (typeof field.markAsDirty === 'function') {
field.markAsDirty();
}
Promise.resolve().then(() => {
this._isUpdatingFromInternal = false;
});
return;
}
catch { }
}
try {
const val = field.value;
if (typeof val === 'function') {
try {
const signalOrValue = val();
if (safeIsSignal(signalOrValue)) {
const writableSignal = signalOrValue;
if (typeof writableSignal.set === 'function') {
writableSignal.set(normalizedValue);
if (typeof field.markAsDirty === 'function') {
field.markAsDirty();
}
Promise.resolve().then(() => {
this._isUpdatingFromInternal = false;
});
return;
}
}
}
catch { }
}
if (safeIsSignal(val)) {
const writableSignal = val;
if (typeof writableSignal.set === 'function') {
writableSignal.set(normalizedValue);
if (typeof field.markAsDirty === 'function') {
field.markAsDirty();
}
Promise.resolve().then(() => {
this._isUpdatingFromInternal = false;
});
return;
}
}
}
catch { }
this._isUpdatingFromInternal = false;
}
catch (error) {
if (isDevMode()) {
console.warn('[ngxsmk-datepicker] Field sync error:', error);
}
this._isUpdatingFromInternal = false;
}
}
getLastKnownValue() {
return this._lastKnownFieldValue;
}
markAsTouched(fieldInput) {
const field = this.resolveField(fieldInput);
if (!field || typeof field !== 'object') {
return;
}
try {
if (typeof field.markAsTouched === 'function') {
field.markAsTouched();
}
}
catch {
// Ignore errors when marking as touched
}
}
cleanup() {
if (this._fieldEffectRef) {
this._fieldEffectRef.destroy();
this._fieldEffectRef = null;
}
this._lastKnownFieldValue = undefined;
this._isUpdatingFromInternal = false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FieldSyncService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FieldSyncService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FieldSyncService, decorators: [{
type: Injectable
}] });
/**
* Service for managing locale data and providing fallback mechanisms
* Supports multiple calendar systems and provides locale-specific formatting
*/
class LocaleRegistryService {
constructor() {
this.localeData = new Map();
this.defaultLocale = 'en-US';
this.registerDefaultLocales();
}
/**
* Register locale data for a specific locale
*/
register(locale, data) {
this.localeData.set(locale.toLowerCase(), data);
}
/**
* Get locale data for a specific locale, with fallback support
*/
getLocaleData(locale) {
const normalizedLocale = locale.toLowerCase();
// Try exact match first
if (this.localeData.has(normalizedLocale)) {
return this.localeData.get(normalizedLocale);
}
// Try language code only (e.g., 'en' from 'en-US')
const parts = normalizedLocale.split('-');
const languageCode = parts.length > 0 ? parts[0] : normalizedLocale;
if (languageCode && this.localeData.has(languageCode)) {
return this.localeData.get(languageCode);
}
// Try fallback chain
const fallbackLocale = this.getFallbackLocale(normalizedLocale);
if (fallbackLocale && this.localeData.has(fallbackLocale)) {
return this.localeData.get(fallbackLocale);
}
// Return default locale as last resort
return this.localeData.get(this.defaultLocale) || this.getDefaultLocaleData();
}
/**
* Get fallback locale for an unsupported locale
*/
getFallbackLocale(unsupportedLocale) {
const normalized = unsupportedLocale.toLowerCase();
// Check if locale has explicit fallback
const localeData = this.localeData.get(normalized);
if (localeData?.fallbackLocale) {
return localeData.fallbackLocale;
}
// Try language code only
const parts = normalized.split('-');
const languageCode = parts.length > 0 ? parts[0] : normalized;
if (languageCode && this.localeData.has(languageCode)) {
return languageCode;
}
// Common fallback patterns
const fallbackMap = {
en: 'en-US',
ar: 'ar-SA',
zh: 'zh-CN',
fr: 'fr-FR',
de: 'de-DE',
es: 'es-ES',
it: 'it-IT',
ja: 'ja-JP',
ko: 'ko-KR',
pt: 'pt-BR',
ru: 'ru-RU',
};
if (languageCode && fallbackMap[languageCode]) {
return fallbackMap[languageCode];
}
// Return default locale
return this.defaultLocale;
}
/**
* Check if a locale is RTL
*/
isRtlLocale(locale) {
const localeData = this.getLocaleData(locale);
return localeData.isRtl;
}
/**
* Get calendar system for a locale
*/
getCalendarSystem(locale) {
const localeData = this.getLocaleData(locale);
return localeData.calendar;
}
/**
* Register default locale data for common locales
*/
registerDefaultLocales() {
// English (US) - Default
this.register('en-US', {
calendar: 'gregorian',
firstDayOfWeek: 0,
dateFormat: 'MM/DD/YYYY',
monthNames: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
],
monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
weekdayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
isRtl: false,
dateFormatOptions: {
year: 'numeric',
month: 'short',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: true,
},
});
// English (UK)
this.register('en-GB', {
calendar: 'gregorian',
firstDayOfWeek: 1,
dateFormat: 'DD/MM/YYYY',
monthNames: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
],
monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
weekdayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
isRtl: false,
fallbackLocale: 'en-US',
dateFormatOptions: {
year: 'numeric',
month: 'short',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: true,
},
});
// Arabic (Saudi Arabia) - RTL
this.register('ar-SA', {
calendar: 'islamic',
firstDayOfWeek: 6, // Saturday
dateFormat: 'DD/MM/YYYY',
monthNames: [
'محرم',
'صفر',
'ربيع الأول',
'ربيع الثاني',
'جمادى الأولى',
'جمادى الثانية',
'رجب',
'شعبان',
'رمضان',
'شوال',
'ذو القعدة',
'ذو الحجة',
],
monthNamesShort: [
'محرم',
'صفر',
'ربيع 1',
'ربيع 2',
'جمادى 1',
'جمادى 2',
'رجب',
'شعبان',
'رمضان',
'شوال',
'قعدة',
'حجة',
],
weekdayNames: ['الأحد', 'الإثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
weekdayNamesShort: ['أحد', 'إثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'],
isRtl: true,
dateFormatOptions: {
year: 'numeric',
month: 'short',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: true,
},
});
// Hebrew (Israel) - RTL
this.register('he-IL', {
calendar: 'hebrew',
firstDayOfWeek: 0,
dateFormat: 'DD/MM/YYYY',
monthNames: [
'ינואר',
'פברואר',
'מרץ',
'אפריל',
'מאי',
'יוני',
'יולי',
'אוגוסט',
'ספטמבר',
'אוקטובר',
'נובמבר',
'דצמבר',
],
monthNamesShort: ['ינו', 'פבר', 'מרץ', 'אפר', 'מאי', 'יונ', 'יול', 'אוג', 'ספט', 'אוק', 'נוב', 'דצמ'],
weekdayNames: ['ראשון', 'שני', 'שלישי', 'רביעי', 'חמישי', 'שישי', 'שבת'],
weekdayNamesShort: ['א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ש'],
isRtl: true,
dateFormatOptions: {
year: 'numeric',
month: 'short',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: true,
},
});
// Persian/Farsi (Iran) - RTL
this.register('fa-IR', {
calendar: 'persian',
firstDayOfWeek: 6, // Saturday
dateFormat: 'YYYY/MM/DD',
monthNames: [
'فروردین',
'اردیبهشت',
'خرداد',
'تیر',
'مرداد',
'شهریور',
'مهر',
'آبان',
'آذر',
'دی',
'بهمن',
'اسفند',
],
monthNamesShort: [
'فروردین',
'اردیبهشت',
'خرداد',
'تیر',
'مرداد',
'شهریور',
'مهر',
'آبان',
'آذر',
'دی',
'بهمن',
'اسفند',
],
weekdayNames: ['یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
weekdayNamesShort: ['ی', 'د', 'س', 'چ', 'پ', 'ج', 'ش'],
isRtl: true,
dateFormatOptions: {
year: 'numeric',
month: 'short',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: true,
},
});
// Urdu (Pakistan) - RTL
this.register('ur-PK', {
calendar: 'gregorian',
firstDayOfWeek: 0,
dateFormat: 'DD/MM/YYYY',
monthNames: [
'جنوری',
'فروری',
'مارچ',
'اپریل',
'مئی',
'جون',
'جولائی',
'اگست',
'ستمبر',
'اکتوبر',
'نومبر',
'دسمبر',
],
monthNamesShort: [
'جنوری',
'فروری',
'مارچ',
'اپریل',
'مئی',
'جون',
'جولائی',
'اگست',
'ستمبر',
'اکتوبر',
'نومبر',
'دسمبر',
],
weekdayNames: ['اتوار', 'پیر', 'منگل', 'بدھ', 'جمعرات', 'جمعہ', 'ہفتہ'],
weekdayNamesShort: ['اتوار', 'پیر', 'منگل', 'بدھ', 'جمعرات', 'جمعہ', 'ہفتہ'],
isRtl: true,
fallbackLocale: 'en-US',
dateFormatOptions: {
year: 'numeric',
month: 'short',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: true,
},
});
// Chinese (Simplified)
this.register('zh-CN', {
calendar: 'gregorian',
firstDayOfWeek: 1,
dateFormat: 'YYYY-MM-DD',
monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
monthNamesShort: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
weekdayNames: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
weekdayNamesShort: ['日', '一', '二', '三', '四', '五', '六'],
isRtl: false,
dateFormatOptions: {
year: 'numeric',
month: 'short',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false,
},
});
// Japanese
this.register('ja-JP', {
calendar: 'japanese',
firstDayOfWeek: 0,
dateFormat: 'YYYY/MM/DD',
monthNames: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
monthNamesShort: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
weekdayNames: ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'],
weekdayNamesShort: ['日', '月', '火', '水', '木', '金', '土'],
isRtl: false,
dateFormatOptions: {
year: 'numeric',
month: 'short',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false,
},
});
// French
this.register('fr-FR', {
calendar: 'gregorian',
firstDayOfWeek: 1,
dateFormat: 'DD/MM/YYYY',
monthNames: [
'janvier',
'février',
'mars',
'avril',
'mai',
'juin',
'juillet',
'août',
'septembre',
'octobre',
'novembre',
'décembre',
],
monthNamesShort: [
'janv.',
'févr.',
'mars',
'avr.',
'mai',
'juin',
'juil.',
'août',
'sept.',
'oct.',
'nov.',
'déc.',
],
weekdayNames: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
weekdayNamesShort: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
isRtl: false,
dateFormatOptions: {
year: 'numeric',
month: 'short',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false,
},
});
// German
this.register('de-DE', {
calendar: 'gregorian',
firstDayOfWeek: 1,
dateFormat: 'DD.MM.YYYY',
monthNames: [
'Januar',
'Februar',
'März',
'April',
'Mai',
'Juni',
'Juli',
'August',
'September',
'Oktober',
'November',
'Dezember',
],
monthNamesShort: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
weekdayNames: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
weekdayNamesShort: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
isRtl: false,
dateFormatOptions: {
year: 'numeric',
month: 'short',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false,
},
});
// Spanish
this.register('es-ES', {
calendar: 'gregorian',
firstDayOfWeek: 1,
dateFormat: 'DD/MM/YYYY',
monthNames: [
'enero',
'febrero',
'marzo',
'abril',
'mayo',
'junio',
'julio',
'agosto',
'septiembre',
'octubre',
'noviembre',
'diciembre',
],
monthNamesShort: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'],
weekdayNames: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'],
weekdayNamesShort: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'],
isRtl: false,
dateFormatOptions: {
year: 'numeric',
month: 'short',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: true,
},
});
// Register language-only codes as fallbacks
this.register('en', this.getLocaleData('en-US'));
this.register('ar', this.getLocaleData('ar-SA'));
this.register('he', this.getLocaleData('he-IL'));
this.register('fa', this.getLocaleData('fa-IR'));
this.register('ur', this.getLocaleData('ur-PK'));
this.register('zh', this.getLocaleData('zh-CN'));
this.register('ja', this.getLocaleData('ja-JP'));
this.register('fr', this.getLocaleData('fr-FR'));
this.register('de', this.getLocaleData('de-DE'));
this.register('es', this.getLocaleData('es-ES'));
}
/**
* Get default locale data (English US)
*/
getDefaultLocaleData() {
return {
calendar: 'gregorian',
firstDayOfWeek: 0,
dateFormat: 'MM/DD/YYYY',
monthNames: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
],
monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
weekdayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
isRtl: false,
dateFormatOptions: {
year: 'numeric',
month: 'short',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: true,
},
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: LocaleRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: LocaleRegistryService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: LocaleRegistryService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [] });
/**
* Service for managing datepicker translations
* Provides default translations for major languages
*/
class TranslationRegistryService {
constructor() {
this.translations = new Map();
this.registerDefaultTranslations();
}
/**
* Register translations for a locale
*/
register(locale, translations) {
this.translations.set(locale.toLowerCase(), translations);
}
/**
* Get translations for a locale with fallback support
*/
getTranslations(locale) {
if (!locale) {
return this.translations.get('en') || this.getEnglishTranslations();
}
const normalized = locale.toLowerCase();
if (this.translations.has(normalized)) {
return this.translations.get(normalized);
}
if (normalized.startsWith('zh-')) {
if (normalized === 'zh-tw' || normalized === 'zh-hk' || normalized === 'zh-mo') {
if (this.translations.has('zh-tw')) {
return this.translations.get('zh-tw');
}
return this.translations.get('en') || this.getEnglishTranslations();
}
if (this.translations.has('zh')) {
return this.translations.get('zh');
}
return this.translations.get('en') || this.getEnglishTranslations();
}
// Try language code only (for all other languages)
const parts = normalized.split('-');
const languageCode = parts.length > 0 ? parts[0] : normalized;
if (languageCode && languageCode !== normalized && this.translations.has(languageCode)) {
return this.translations.get(languageCode);
}
return this.translations.get('en') || this.getEnglishTranslations();
}
/**
* Register default translations for major languages
*/
registerDefaultTranslations() {
this.register('en', this.getEnglishTranslations());
this.register('en-US', this.getEnglishTranslations());
this.register('en-GB', this.getEnglishTranslations());
/**
* Spanish (Español)
* Includes support for ES and ES-ES locales
*/
const spanishTranslations = {
selectDate: 'Seleccionar fecha',
selectTime: 'Seleccionar hora',
clear: 'Limpiar',
close: 'Cerrar',
today: 'Hoy',
selectEndDate: 'Seleccionar fecha de fin',
day: 'Día',
days: 'Días',
previousMonth: 'Mes anterior',
nextMonth: 'Mes siguiente',
previousYear: 'Año anterior',
nextYear: 'Año siguiente',
previousYears: 'Años anteriores',
nextYears: 'Años siguientes',
previousDecade: 'Década anterior',
nextDecade: 'Década siguiente',
clearSelection: 'Limpiar selección',
closeCalendar: 'Cerrar calendario',
closeCalendarOverlay: 'Cerrar superposición del calendario',
calendarFor: 'Calendario para {{month}} {{year}}',
selectYear: 'Seleccionar año {{year}}',
selectDecade: 'Seleccionar década {{start}} - {{end}}',
datesSelected: '{{count}} fechas seleccionadas',
timesSelected: '{{count}} veces seleccionadas',
time: 'Hora:',
startTime: 'Hora de inicio',
endTime: 'Hora de fin',
from: 'Desde',
to: 'Hasta',
holiday: 'Festivo',
month: 'Mes',
year: 'Año',
decade: 'Década',
timeline: 'Línea de tiempo',
timeSlider: 'Control deslizante de tiempo',
calendarOpened: 'Calendario abierto para {{month}} {{year}}',
calendarClosed: 'Calendario cerrado',
dateSelected: 'Fecha seleccionada: {{date}}',
rangeSelected: 'Rango seleccionado: {{start}} a {{end}}',
monthChanged: 'Cambiado a {{month}} {{year}}',
yearChanged: 'Cambiado al año {{year}}',
calendarLoading: 'Cargando calendario...',
calendarReady: 'Calendario listo',
keyboardShortcuts: 'Atajos de teclado',
invalidDateFormat: 'Ingrese una fecha válida.',
dateBeforeMin: 'La fecha debe ser igual o posterior a {{minDate}}.',
dateAfterMax: 'La fecha debe ser igual o anterior a {{maxDate}}.',
invalidDate: 'Fecha no válida.',
};
this.register('es', spanishTranslations);
this.register('es-ES', spanishTranslations);
/**
* French (Français)
* Includes support for FR and FR-FR locales
*/
const frenchTranslations = {
selectDate: 'Sélectionner une date',
selectTime: 'Sélectionner une heure',
clear: 'Effacer',
close: 'Fermer',
today: "Aujourd'hui",
selectEndDate: 'Sélectionner la date de fin',
day: 'Jour',
days: 'Jours',
previousMonth: 'Mois précédent',
nextMonth: 'Mois suivant',
previousYear: 'Année précédente',
nextYear: 'Année suivante',
previousYears: 'Années précédentes',
nextYears: 'Années suivantes',
previousDecade: 'Décennie précédente',
nextDecade: 'Décennie suivante',
clearSelection: 'Effacer la sélection',
closeCalendar: 'Fermer le calendrier',
closeCalendarOverlay: 'Fermer la superposition du calendrier',
calendarFor: 'Calendrier pour {{month}} {{year}}',
selectYear: "Sélectionner l'année {{year}}",
selectDecade: 'Sélectionner la décennie {{start}} - {{end}}',
datesSelected: '{{count}} dates sélectionnées',
timesSelected: '{{count}} fois sélectionnées',
time: 'Heure:',
startTime: 'Heure de début',
endTime: 'Heure de fin',
from: 'De',
to: 'À',
holiday: 'Jour férié',
month: 'Mois',
year: 'Année',
decade: 'Décennie',
timeline: 'Chronologie',
timeSlider: 'Curseur de temps',
calendarOpened: 'Calendrier ouvert pour {{month}} {{year}}',
calendarClosed: 'Calendrier fermé',
dateSelected: 'Date sélectionnée : {{date}}',
rangeSelected: 'Plage sélectionnée : {{start}} à {{end}}',
monthChanged: 'Changé pour {{month}} {{year}}',
yearChanged: "Changé pour l'année {{year}}",
calendarLoading: 'Chargement du calendrier...',
calendarReady: 'Calendrier prêt',
keyboardShortcuts: 'Raccourcis clavier',
invalidDateFormat: 'Veuillez saisir une date valide.',
dateBeforeMin: 'La date doit être égale ou postérieure à {{minDate}}.',
dateAfterMax: 'La date doit être égale ou antérieure à {{maxDate}}.',
invalidDate: 'Date invalide.',
};
this.register('fr', frenchTranslations);
this.register('fr-FR', frenchTranslations);
/**
* German (Deutsch)
* Includes support for DE and DE-DE locales
*/
const germanTranslations = {
selectDate: 'Datum auswählen',
selectTime: 'Uhrzeit auswählen',
clear: 'Löschen',
close: 'Schließen',
today: 'Heute',
selectEndDate: 'Enddatum auswählen',
day: 'Tag',
days: 'Tage',
previousMonth: 'Vorheriger Monat',
nextMonth: 'Nächster Monat',
previousYear: 'Vorheriges Jahr',
nextYear: 'Nächstes Jahr',
previousYears: 'Vorherige Jahre',
nextYears: 'Nächste Jahre',
previousDecade: 'Vorheriges Jahrzehnt',
nextDecade: 'Nächstes Jahrzehnt',
clearSelection: 'Auswahl löschen',
closeCalendar: 'Kalender schließen',
closeCalendarOverlay: 'Kalender-Overlay schließen',
calendarFor: 'Kalender für {{month}} {{year}}',
selectYear: 'Jahr {{year}} auswählen',
selectDecade: 'Jahrzehnt {{start}} - {{end}} auswählen',
datesSelected: '{{count}} Datumsangaben ausgewählt',
timesSelected: '{{count}} Zeiten ausgewählt',
time: 'Uhrzeit:',
startTime: 'Startzeit',
endTime: 'Endzeit',
from: 'Von',
to: 'Bis',
holiday: 'Feiertag',
month: 'Monat',
year: 'Jahr',
decade: 'Jahrzehnt',
timeline: 'Zeitachse',
timeSlider: 'Zeitschieberegler',
calendarOpened: 'Kalender geöffnet für {{month}} {{year}}',
calendarClosed: 'Kalender geschlossen',
dateSelected: 'Datum ausgewählt: {{date}}',
rangeSelected: 'Bereich ausgewählt: {{start}} bis {{end}}',
monthChanged: 'Geändert zu {{month}} {{year}}',
yearChanged: 'Geändert zu Jahr {{year}}',
calendarLoading: 'Kalender wird geladen...',
calendarReady: 'Kalender bereit',
keyboardShortcuts: 'Tastaturkürzel',
invalidDateFormat: 'Bitte geben Sie ein gültiges Datum ein.',
dateBeforeMin: 'Das Datum muss am oder nach {{minDate}} liegen.',
dateAfterMax: 'Das Datum muss am oder vor {{maxDate}} liegen.',
invalidDate: 'Ungültiges Datum.',
};
this.register('de', germanTranslations);
this.register('de-DE', germanTranslations);
/**
* Arabic (العربية)
* Includes support for AR and AR-SA locales (RTL)
*/
const arabicTranslations = {
selectDate: 'اختر التاريخ',
selectTime: 'اختر الوقت',
clear: 'مسح',
close: 'إغلاق',
today: 'اليوم',
selectEndDate: 'اختر تاريخ الانتهاء',
day: 'يوم',
days: 'أيام',
previousMonth: 'الشهر السابق',
nextMonth: 'الشهر التالي',
previousYear: 'السنة السابقة',
nextYear: 'السنة التالية',
previousYears: 'السنوات السابقة',
nextYears: 'السنوات التالية',
previousDecade: 'العقد السابق',
nextDecade: 'العقد التالي',
clearSelection: 'مسح التحديد',
closeCalendar: 'إغلاق التقويم',
closeCalendarOverlay: 'إغلاق تراكب التقويم',
calendarFor: 'تقويم لـ {{month}} {{year}}',
selectYear: 'اختر السنة {{year}}',
selectDecade: 'اختر العقد {{start}} - {{end}}',
datesSelected: '{{count}} تواريخ محددة',
timesSelected: '{{count}} مرات محددة',
time: 'الوقت:',
startTime: 'وقت البدء',
endTime: 'وقت الانتهاء',
from: 'من',
to: 'إلى',
holiday: 'عطلة',
month: 'شهر',
year: 'سنة',
decade: 'عقد',
timeline: 'الجدول الزمني',
timeSlider: 'منزلق الوقت',
calendarOpened: 'تم فتح التقويم لـ {{month}} {{year}}',
calendarClosed: 'تم إغلاق التقويم',
dateSelected: 'تم تحديد التاريخ: {{date}}',
rangeSelected: 'تم تحديد النطاق: {{start}} إلى {{end}}',
monthChanged: 'تم التغيير إلى {{month}} {{year}}',
yearChanged: 'تم التغيير إلى السنة {{year}}',
calendarLoading: 'جارٍ تحميل التقويم...',
calendarReady: 'التقويم جاهز',
keyboardShortcuts: 'اختصارات لوحة المفاتيح',
invalidDateFormat: 'يرجى إدخال تاريخ صالح.',
dateBeforeMin: 'يجب أن يكون التاريخ في أو بعد {{minDate}}.',
dateAfterMax: 'يجب أن يكون التاريخ في أو قبل {{maxDate}}.',
invalidDate: 'تاريخ غير صالح.',
};
this.register('ar', arabicTranslations);
this.register('ar-SA', arabicTranslations);
/**
* Chinese Simplified (简体中文)
* Includes support for ZH and ZH-CN locales
*/
const chineseSimplifiedTranslations = {
selectDate: '选择日期',
selectTime: '选择时间',
clear: '清除',
close: '关闭',
today: '今天',
selectEndDate: '选择结束日期',
day: '天',
days: '天',
previousMonth: '上个月',
nextMonth: '下个月',
previousYear: '上一年',
nextYear: '下一年',
previousYears: '上几年',
nextYears: '下几年',
previousDecade: '上十年',
nextDecade: '下十年',
clearSelection: '清除选择',
closeCalendar: '关闭日历',
closeCalendarOverlay: '关闭日历叠加层',
calendarFor: '{{year}}年{{month}}的日历',
selectYear: '选择年份 {{year}}',
selectDecade: '选择十年 {{start}} - {{end}}',
datesSelected: '已选择 {{count}} 个日期',
timesSelected: '已选择 {{count}} 次',
time: '时间:',
startTime: '开始时间',
endTime: '结束时间',
from: '从',
to: '到',
holiday: '节假日',
month: '月',
year: '年',
decade: '十年',
timeline: '时间线',
timeSlider: '时间滑块',
calendarOpened: '已打开 {{year}}年{{month}} 的日历',
calendarClosed: '日历已关闭',
dateSelected: '已选择日期: {{date}}',
rangeSelected: '已选择范围: {{start}} 至 {{end}}',
monthChanged: '已更改为 {{month}} {{year}}',
yearChanged: '已更改为 {{year}} 年',
calendarLoading: '正在加载日历...',
calendarReady: '日历已就绪',
keyboardShortcuts: '键盘快捷键',
invalidDateFormat: '请输入有效日期。',
dateBeforeMin: '日期必须为 {{minDate}} 或之后。',
dateAfterMax: '日期必须为 {{maxDate}} 或之前。',
invalidDate: '无效日期。',
};
this.register('zh', chineseSimplifiedTranslations);
this.register('zh-CN', chineseSimplifiedTranslations);
/**
* Japanese (日本語)
* Includes support for JA and JA-JP locales
*/
const japaneseTranslations = {
selectDate: '日付を選択',
selectTime: '時刻を選択',
clear: 'クリア',
close: '閉じる',
today: '今日',
selectEndDate: '終了日を選択',
day: '日',
days: '日間',
previousMonth: '前の月',
nextMonth: '次の月',
previousYear: '前の年',
nextYear: '次の年',
previousYears: '前の年',
nextYears: '次の年',
previousDecade: '前の10年',
nextDecade: '次の10年',
clearSelection: '選択をクリア',
closeCalendar: 'カレンダーを閉じる',
closeCalendarOverlay: 'カレンダーオーバーレイを閉じる',
calendarFor: '{{year}}年{{month}}のカレンダー',
selectYear: '年 {{year}} を選択',
selectDecade: '10年 {{start}} - {{end}} を選択',
datesSelected: '{{count}} 日付が選択されました',
timesSelected: '{{count}} 回選択されました',
time: '時刻:',
startTime: '開始時刻',
endTime: '終了時刻',
from: 'から',
to: 'まで',
holiday: '祝日',
month: '月',
year: '年',
decade: '10年',
timeline: 'タイムライン',
timeSlider: 'タイムスライダー',
calendarOpened: '{{year}}年{{month}}のカレンダーを開きました',
calendarClosed: 'カレンダーを閉じました',
dateSelected: '日付を選択しました: {{date}}',
rangeSelected: '範囲を選択しました: {{start}} から {{end}}',
monthChanged: '{{month}} {{year}}に変更しました',
yearChanged: '{{year}}年に変更しました',
calendarLoading: 'カレンダーを読み込み中...',
calendarReady: 'カレンダーの準備ができました',
keyboardShortcuts: 'キーボードショートカット',
invalidDateFormat: '有効な日付を入力してください。',
dateBeforeMin: '日付は {{minDate}} 以降である必要があります。',
dateAfterMax: '日付は {{maxDate}} 以前である必要があります。',
invalidDate: '無効な日付です。',
};
this.register('ja', japaneseTranslations);
this.register('ja-JP', japaneseTranslations);
/**
* Portuguese - Brazil (Português)
* Includes support for PT and PT-BR locales
*/
const portugueseTranslations = {
selectDate: 'Selecionar data',
selectTime: 'Selecionar hora',
clear: 'Limpar',
close: 'Fechar',
today: 'Hoje',
selectEndDate: 'Selecionar data de término',
day: 'Dia',
days: 'Dias',
previousMonth: 'Mês anterior',
nextMonth: 'Próximo mês',
previousYear: 'Ano anterior',
nextYear: 'Próximo ano',
previousYears: 'Anos anteriores',
nextYears: 'Próximos anos',
previousDecade: 'Década anterior',
nextDecade: 'Próxima década',
clearSelection: 'Limpar seleção',
closeCalendar: 'Fechar calendário',
closeCalendarOverlay: 'Fechar sobreposição do calendário',
calendarFor: 'Calendário para {{month}} {{year}}',
selectYear: 'Selecionar ano {{year}}',
selectDecade: 'Selecionar década {{start}} - {{end}}',
datesSelected: '{{count}} datas selecionadas',
timesSelected: '{{count}} vezes selecionadas',
time: 'Hora:',
startTime: 'Hora de início',
endTime: 'Hora de término',
from: 'De',
to: 'Para',
holiday: 'Feriado',
month: 'Mês',
year: 'Ano',
decade: 'Década',
timeline: 'Linha do tempo',
timeSlider: 'Controle deslizante de tempo',
calendarOpened: 'Calendário aberto para {{month}} {{year}}',
calendarClosed: 'Calendário fechado',
dateSelected: 'Data selecionada: {{date}}',
rangeSelected: 'Intervalo selecionado: {{start}} a {{end}}',
monthChanged: 'Alterado para {{month}} {{year}}',
yearChanged: 'Alterado para o ano {{year}}',
calendarLoading: 'Carregando calendário...',
calendarReady: 'Calendário pronto',
keyboardShortcuts: 'Atalhos de teclado',
invalidDateFormat: 'Insira uma data válida.',
dateBeforeMin: 'A data deve ser em ou após {{minDate}}.',
dateAfterMax: 'A data deve ser em ou antes de {{maxDate}}.',
invalidDate: 'Data inválida.',
};
this.register('pt', portugueseTranslations);
this.register('pt-BR', portugueseTranslations);
/**
* Russian (Русский)
* Includes support for RU and RU-RU locales
*/
const russianTranslations = {
selectDate: 'Выбрать дату',
selectTime: 'Выбрать время',
clear: 'Очистить',
close: 'Закрыть',
today: 'Сегодня',
selectEndDate: 'Выбрать дату окончания',
day: 'День',
days: 'Дней',
previousMonth: 'Предыдущий месяц',
nextMonth: 'Следующий месяц',
previousYear: 'Предыдущий год',
nextYear: 'Следующий год',
previousYears: 'Предыдущие годы',
nextYears: 'Следующие годы',
previousDecade: 'Предыдущее десятилетие',
nextDecade: 'Следующее десятилетие',
clearSelection: 'Очистить выбор',
closeCalendar: 'Закрыть календарь',
closeCalendarOverlay: 'Закрыть наложение календаря',
calendarFor: 'Календарь на {{month}} {{year}}',
selectYear: 'Выбрать год {{year}}',
selectDecade: 'Выбрать десятилетие {{start}} - {{end}}',
datesSelected: 'Выбрано дат: {{count}}',
timesSelected: 'Выбрано раз: {{count}}',
time: 'Время:',
startTime: 'Время начала',
endTime: 'Время окончания',
from: 'С',
to: 'По',
holiday: 'Праздник',
month: 'Месяц',
year: 'Год',
decade: 'Десятилетие',
timeline: 'Временная шкала',
timeSlider: 'Ползунок времени',
calendarOpened: 'Календарь открыт для {{month}} {{year}}',
calendarClosed: 'Календарь закрыт',
dateSelected: 'Выбрана дата: {{date}}',
rangeSelected: 'Выбран диапазон: {{start}} по {{end}}',
monthChanged: 'Изменено на {{month}} {{year}}',
yearChanged: 'Изменено на год {{year}}',
calendarLoading: 'Загрузка календаря...',
calendarReady: 'Календарь готов',
keyboardShortcuts: 'Сочетания клавиш',
invalidDateFormat: 'Введите правильную дату.',
dateBeforeMin: 'Дата должна быть не ранее {{minDate}}.',
dateAfterMax: 'Дата должна быть не позднее {{maxDate}}.',
invalidDate: 'Недопустимая дата.',
};
this.register('ru', russianTranslations);
this.register('ru-RU', russianTranslations);
/**
* Swedish (Svenska)
* Includes support for SV and SV-SE locales
*/
const swedishTranslations = {
selectDate: 'Välj datum',
selectTime: 'Välj tid',
clear: 'Rensa',
close: 'Stäng',
today: 'Idag',
selectEndDate: 'Välj slutdatum',
day: 'Dag',
days: 'Dagar',
previousMonth: 'Föregående månad',
nextMonth: 'Nästa månad',
previousYear: 'Föregående år',
nextYear: 'Nästa år',
previousYears: 'Föregående år',
nextYears: 'Nästa år',
previousDecade: 'Föregående decennium',
nextDecade: 'Nästa decennium',
clearSelection: 'Rensa val',
closeCalendar: 'Stäng kalender',
closeCalendarOverlay: 'Stäng kalenderöverlägg',
calendarFor: 'Kalender för {{month}} {{year}}',
selectYear: 'Välj år {{year}}',
selectDecade: 'Välj decennium {{start}} - {{end}}',
datesSelected: '{{count}} datum valda',
timesSelected: '{{count}} tider valda',
time: 'Tid:',
startTime: 'Starttid',
endTime: 'Sluttid',
from: 'Från',
to: 'Till',
holiday: 'Helgdag',
month: 'Månad',
year: 'År',
decade: 'Decennium',
timeline: 'Tidslinje',
timeSlider: 'Tidsskjutare',
calendarOpened: 'Kalender öppnad för {{month}} {{year}}',
calendarClosed: 'Kalender stängd',
dateSelected: 'Datum valt: {{date}}',
rangeSelected: 'Omfång valt: {{start}} till {{end}}',
monthChanged: 'Ändrat till {{month}} {{year}}',
yearChanged: 'Ändrat till år {{year}}',
calendarLoading: 'Laddar kalender...',
calendarReady: 'Kalender redo',
keyboardShortcuts: 'Kortkommandon',
invalidDateFormat: 'Ange ett giltigt datum.',
dateBeforeMin: 'Datumet måste vara den {{minDate}} eller senare.',
dateAfterMax: 'Datumet måste vara den {{maxDate}} eller tidigare.',
invalidDate: 'Ogiltigt datum.',
};
this.register('sv', swedishTranslations);
this.register('sv-SE', swedishTranslations);
/**
* Korean (한국어)
* Includes support for KO and KO-KR locales
*/
const koreanTranslations = {
selectDate: '날짜 선택',
selectTime: '시간 선택',
clear: '지우기',
close: '닫기',
today: '오늘',
selectEndDate: '종료일 선택',
day: '일',
days: '일',
previousMonth: '이전 달',
nextMonth: '다음 달',
previousYear: '이전 해',
nextYear: '다음 해',
previousYears: '이전 해',
nextYears: '다음 해',
previousDecade: '이전 10년',
nextDecade: '다음 10년',
clearSelection: '선택 지우기',
closeCalendar: '달력 닫기',
closeCalendarOverlay: '달력 오버레이 닫기',
calendarFor: '{{year}}년 {{month}} 달력',
selectYear: '{{year}}년 선택',
selectDecade: '{{start}} - {{end}} 10년 선택',
datesSelected: '{{count}}개 날짜 선택됨',
timesSelected: '{{count}}번 선택됨',
time: '시간:',
startTime: '시작 시간',
endTime: '종료 시간',
from: '부터',
to: '까지',
holiday: '공휴일',
month: '월',
year: '년',
decade: '10년',
timeline: '타임라인',
timeSlider: '시간 슬라이더',
calendarOpened: '{{year}}년 {{month}} 달력이 열렸습니다',
calendarClosed: '달력이 닫혔습니다',
dateSelected: '날짜 선택됨: {{date}}',
rangeSelected: '범위 선택됨: {{start}} ~ {{end}}',
monthChanged: '{{month}} {{year}}로 변경됨',
yearChanged: '{{year}}년으로 변경됨',
calendarLoading: '달력 로딩 중...',
calendarReady: '달력 준비됨',
keyboardShortcuts: '키보드 단축키',
invalidDateFormat: '올바른 날짜를 입력하세요.',
dateBeforeMin: '날짜는 {{minDate}} 또는 그 이후여야 합니다.',
dateAfterMax: '날짜는 {{maxDate}} 또는 그 이전이어야 합니다.',
invalidDate: '잘못된 날짜입니다.',
};
this.register('ko', koreanTranslations);
this.register('ko-KR', koreanTranslations);
/**
* Chinese Traditional (繁體中文)
* Specific support for ZH-TW locale
*/
this.register('zh-TW', {
selectDate: '選擇日期',
selectTime: '選擇時間',
clear: '清除',
close: '關閉',
today: '今天',
selectEndDate: '選擇結束日期',
day: '天',
days: '天',
previousMonth: '上個月',
nextMonth: '下個月',
previousYear: '上一年',
nextYear: '下一年',
previousYears: '上幾年',
nextYears: '下幾年',
previousDecade: '上十年',
nextDecade: '下十年',
clearSelection: '清除選擇',
closeCalendar: '關閉日曆',
closeCalendarOverlay: '關閉日曆疊加層',
calendarFor: '{{year}}年{{month}}的日曆',
selectYear: '選擇年份 {{year}}',
selectDecade: '選擇十年 {{start}} - {{end}}',
datesSelected: '已選擇 {{count}} 個日期',
timesSelected: '已選擇 {{count}} 次',
time: '時間:',
startTime: '開始時間',
endTime: '結束時間',
from: '從',
to: '到',
holiday: '節假日',
month: '月',
year: '年',
decade: '十年',
timeline: '時間線',
timeSlider: '時間滑塊',
calendarOpened: '已打開 {{year}}年{{month}} 的日曆',
calendarClosed: '日曆已關閉',
dateSelected: '已選擇日期: {{date}}',
rangeSelected: '已選擇範圍: {{start}} 至 {{end}}',
monthChanged: '已更改為 {{month}} {{year}}',
yearChanged: '已更改為 {{year}} 年',
calendarLoading: '正在載入日曆...',
calendarReady: '日曆已就緒',
keyboardShortcuts: '鍵盤快捷鍵',
invalidDateFormat: '請輸入有效日期。',
dateBeforeMin: '日期必須為 {{minDate}} 或之後。',
dateAfterMax: '日期必須為 {{maxDate}} 或之前。',
invalidDate: '無效日期。',
});
}
/**
* Get English translations (default)
*/
getEnglishTranslations() {
return {
selectDate: 'Select date',
selectTime: 'Select time',
clear: 'Clear',
close: 'Close',
today: 'Today',
selectEndDate: 'Select end date',
day: 'Day',
days: 'Days',
previousMonth: 'Previous month',
nextMonth: 'Next month',
previousYear: 'Previous year',
nextYear: 'Next year',
previousYears: 'Previous years',
nextYears: 'Next years',
previousDecade: 'Previous decade',
nextDecade: 'Next decade',
clearSelection: 'Clear selection',
closeCalendar: 'Close calendar',
closeCalendarOverlay: 'Close calendar overlay',
calendarFor: 'Calendar for {{month}} {{year}}',
selectYear: 'Select year {{year}}',
selectDecade: 'Select decade {{start}} - {{end}}',
datesSelected: '{{count}} dates selected',
timesSelected: '{{count}} times selected',
time: 'Time:',
startTime: 'Start Time',
endTime: 'End Time',
from: 'From',
to: 'To',
holiday: 'Holiday',
month: 'Month',
year: 'Year',
decade: 'Decade',
timeline: 'Timeline',
timeSlider: 'Time Slider',
calendarOpened: 'Calendar opened for {{month}} {{year}}',
calendarClosed: 'Calendar closed',
dateSelected: 'Date selected: {{date}}',
startDateSelected: 'Start date set to {{date}}. Select end date.',
rangeSelected: 'Range selected: {{start}} to {{end}}',
monthChanged: 'Changed to {{month}} {{year}}',
yearChanged: 'Changed to year {{year}}',
calendarLoading: 'Loading calendar...',
calendarReady: 'Calendar ready',
keyboardShortcuts: 'Keyboard shortcuts',
invalidDateFormat: 'Please enter a valid date.',
dateBeforeMin: 'Date must be on or after {{minDate}}.',
dateAfterMax: 'Date must be on or before {{maxDate}}.',
invalidDate: 'Invalid date.',
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: TranslationRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: TranslationRegistryService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: TranslationRegistryService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [] });
class FocusTrapService {
constructor() {
this.activeTraps = new Map();
this.focusableSelectors = [
'a[href]',
'button:not([disabled])',
'textarea:not([disabled])',
'input:not([disabled])',
'select:not([disabled])',
'[tabindex]:not([tabindex="-1"])',
].join(', ');
}
/**
* Trap focus within an element and restore focus on cleanup
*/
trapFocus(elementRef) {
if (!elementRef?.nativeElement) {
return () => { };
}
const element = elementRef.nativeElement;
const previousActiveElement = document.activeElement;
const firstFocusable = this.getFirstFocusable(element);
const lastFocusable = this.getLastFocusable(element);
const handleKeyDown = (event) => {
if (event.key !== 'Tab') {
return;
}
// Handle Tab key navigation
if (event.shiftKey) {
// Shift+Tab: move backwards
if (document.activeElement === firstFocusable) {
event.preventDefault();
if (lastFocusable) {
lastFocusable.focus();
}
}
}
else {
// Tab: move forwards
if (document.activeElement === lastFocusable) {
event.preventDefault();
if (firstFocusable) {
firstFocusable.focus();
}
}
}
};
element.addEventListener('keydown', handleKeyDown);
// Store state for cleanup
this.activeTraps.set(elementRef, {
element,
handleKeyDown,
previousActiveElement,
});
// Focus first focusable element after a short delay to ensure DOM is ready
if (firstFocusable) {
// Use requestAnimationFrame to ensure focus happens after render
requestAnimationFrame(() => {
firstFocusable.focus();
});
}
return () => {
this.removeFocusTrap(elementRef);
};
}
/**
* Remove focus trap and restore previous focus
*/
removeFocusTrap(elementRef) {
const state = this.activeTraps.get(elementRef);
if (!state) {
return;
}
state.element.removeEventListener('keydown', state.handleKeyDown);
// Restore focus to previous element if it still exists in the DOM
if (state.previousActiveElement && document.body.contains(state.previousActiveElement)) {
// Use requestAnimationFrame to ensure focus restoration happens after trap removal
requestAnimationFrame(() => {
try {
state.previousActiveElement?.focus();
}
catch {
// Element may not be focusable, ignore error
}
});
}
this.activeTraps.delete(elementRef);
}
getFirstFocusable(element) {
const focusableElements = Array.from(element.querySelectorAll(this.focusableSelectors));
return (focusableElements.find((el) => {
const style = window.getComputedStyle(el);
return style.display !== 'none' && style.visibility !== 'hidden';
}) || null);
}
getLastFocusable(element) {
const focusableElements = Array.from(element.querySelectorAll(this.focusableSelectors));
return (focusableElements
.filter((el) => {
const style = window.getComputedStyle(el);
return style.display !== 'none' && style.visibility !== 'hidden';
})
.pop() || null);
}
ngOnDestroy() {
// Clean up all active traps
for (const [elementRef] of this.activeTraps) {
this.removeFocusTrap(elementRef);
}
this.activeTraps.clear();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FocusTrapService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FocusTrapService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FocusTrapService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
class AriaLiveService {
constructor() {
this.platformId = inject(PLATFORM_ID);
this.isBrowser = isPlatformBrowser(this.platformId);
this.politeRegion = null;
this.assertiveRegion = null;
this.politeClearTimeoutId = null;
this.assertiveClearTimeoutId = null;
this.debounceTimeoutId = null;
this.announcementQueue = [];
this.DEBOUNCE_DELAY = 100;
this.CLEAR_DELAY = 2000;
}
/**
* Announce a message to screen readers with improved timing and queue management
*/
announce(message, priority = 'polite') {
if (!this.isBrowser || !message || message.trim() === '') {
return;
}
const timestamp = Date.now();
this.announcementQueue.push({ message, priority, timestamp });
// Debounce rapid announcements
if (this.announcementQueue.length === 1) {
// Clear any existing debounce timeout before setting a new one
if (this.debounceTimeoutId !== null) {
clearTimeout(this.debounceTimeoutId);
}
this.debounceTimeoutId = setTimeout(() => {
this.debounceTimeoutId = null;
this.processAnnouncementQueue();
}, this.DEBOUNCE_DELAY);
}
}
/**
* Process queued announcements, keeping only the most recent for each priority
*/
processAnnouncementQueue() {
if (this.announcementQueue.length === 0) {
return;
}
// Filter by priority and take the last one pushed (the latest)
// We don't use sort() because timestamps might be identical in the same tick
const politeAnnouncements = this.announcementQueue.filter((a) => a.priority === 'polite');
const assertiveAnnouncements = this.announcementQueue.filter((a) => a.priority === 'assertive');
const latestPolite = politeAnnouncements[politeAnnouncements.length - 1];
const latestAssertive = assertiveAnnouncements[assertiveAnnouncements.length - 1];
this.announcementQueue = [];
if (latestPolite) {
this.announceToRegion(latestPolite.message, 'polite');
}
if (latestAssertive) {
this.announceToRegion(latestAssertive.message, 'assertive');
}
}
/**
* Announce to a specific live region
*/
announceToRegion(message, priority) {
const region = priority === 'polite' ? this.politeRegion : this.assertiveRegion;
const clearTimeoutId = priority === 'polite' ? this.politeClearTimeoutId : this.assertiveClearTimeoutId;
if (!region) {
this.createLiveRegion(priority);
const newRegion = priority === 'polite' ? this.politeRegion : this.assertiveRegion;
if (newRegion) {
this.setAnnouncement(newRegion, message, priority);
}
return;
}
// Clear existing timeout
if (clearTimeoutId !== null) {
clearTimeout(clearTimeoutId);
if (priority === 'polite') {
this.politeClearTimeoutId = null;
}
else {
this.assertiveClearTimeoutId = null;
}
}
this.setAnnouncement(region, message, priority);
}
/**
* Set announcement text and schedule cleanup
*/
setAnnouncement(region, message, priority) {
// Clear and set new content to ensure screen readers detect the change
region.textContent = '';
// Use setTimeout to ensure the clear-then-set pattern is detected by screen readers.
// A small delay (16ms ~ 1 frame) is used to ensure the DOM update of clearing is processed.
setTimeout(() => {
if (!region)
return;
region.textContent = message;
const timeoutId = setTimeout(() => {
if (region) {
region.textContent = '';
}
if (priority === 'polite') {
this.politeClearTimeoutId = null;
}
else {
this.assertiveClearTimeoutId = null;
}
}, this.CLEAR_DELAY);
if (priority === 'polite') {
this.politeClearTimeoutId = timeoutId;
}
else {
this.assertiveClearTimeoutId = timeoutId;
}
}, 16);
}
/**
* Create a live region for announcements
*/
createLiveRegion(priority) {
if (!this.isBrowser) {
return;
}
const region = document.createElement('div');
region.setAttribute('aria-live', priority);
region.setAttribute('aria-atomic', 'true');
region.setAttribute('role', 'status');
region.setAttribute('class', `ngxsmk-aria-live-region ngxsmk-aria-live-${priority}`);
// Apply styles to hide the region while keeping it accessible
Object.assign(region.style, {
position: 'absolute',
left: '-10000px',
width: '1px',
height: '1px',
overflow: 'hidden',
clip: 'rect(0, 0, 0, 0)',
clipPath: 'inset(50%)',
});
document.body.appendChild(region);
if (priority === 'polite') {
this.politeRegion = region;
}
else {
this.assertiveRegion = region;
}
}
ngOnDestroy() {
if (this.politeClearTimeoutId !== null) {
clearTimeout(this.politeClearTimeoutId);
this.politeClearTimeoutId = null;
}
if (this.assertiveClearTimeoutId !== null) {
clearTimeout(this.assertiveClearTimeoutId);
this.assertiveClearTimeoutId = null;
}
if (this.debounceTimeoutId !== null) {
clearTimeout(this.debounceTimeoutId);
this.debounceTimeoutId = null;
}
if (this.politeRegion && this.isBrowser) {
this.politeRegion.remove();
this.politeRegion = null;
}
if (this.assertiveRegion && this.isBrowser) {
this.assertiveRegion.remove();
this.assertiveRegion = null;
}
this.announcementQueue = [];
}
destroy() {
this.ngOnDestroy();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AriaLiveService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AriaLiveService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AriaLiveService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: () => [] });
class HapticFeedbackService {
constructor() {
this.platformId = inject(PLATFORM_ID);
this.isBrowser = isPlatformBrowser(this.platformId);
this.isSupported = this.isBrowser && 'vibrate' in navigator;
}
/**
* Trigger light haptic feedback (short vibration)
*/
light() {
if (!this.isSupported)
return;
try {
// Very short single pulse for subtle interaction
navigator.vibrate(5);
}
catch { }
}
selection() {
if (!this.isSupported)
return;
try {
// Tiny double pulse for selecting list items/scrolling "ticks"
navigator.vibrate([1, 5, 1]);
}
catch { }
}
medium() {
if (!this.isSupported)
return;
try {
// Success-like double pulse
navigator.vibrate([10, 5, 10]);
}
catch { }
}
heavy() {
if (!this.isSupported)
return;
try {
// Error-like or confirmation-like pattern
navigator.vibrate([15, 30, 15]);
}
catch { }
}
/**
* Trigger custom vibration pattern
* @param pattern Vibration pattern in milliseconds
*/
custom(pattern) {
if (!this.isSupported)
return;
try {
navigator.vibrate(pattern);
}
catch {
// Silently fail if vibration is not supported
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: HapticFeedbackService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: HapticFeedbackService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: HapticFeedbackService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
class CalendarGenerationService {
constructor() {
this.monthCache = new Map();
this.MAX_CACHE_SIZE = 24; // Cache up to 24 months (2 years)
}
/**
* Generate calendar days for a specific month
*/
generateMonthDays(year, month, firstDayOfWeek, normalizeDateFn) {
const cacheKey = `${year}-${month}-${firstDayOfWeek}`;
let days = this.monthCache.get(cacheKey);
if (!days) {
days = this._generateMonthDays(year, month, firstDayOfWeek, normalizeDateFn);
// Add to cache and manage cache size
if (this.monthCache.size >= this.MAX_CACHE_SIZE) {
// Remove oldest entry (first key in Map)
const firstKey = this.monthCache.keys().next().value;
if (firstKey) {
this.monthCache.delete(firstKey);
}
}
this.monthCache.set(cacheKey, days);
}
return days;
}
/**
* Generate multiple calendar months
*/
generateMultipleMonths(startYear, startMonth, count, firstDayOfWeek, normalizeDateFn) {
const months = [];
for (let calIndex = 0; calIndex < count; calIndex++) {
const calMonth = (startMonth + calIndex) % 12;
const calYear = startYear + Math.floor((startMonth + calIndex) / 12);
const days = this.generateMonthDays(calYear, calMonth, firstDayOfWeek, normalizeDateFn);
months.push({
month: calMonth,
year: calYear,
days: days,
});
}
return months;
}
/**
* Preload adjacent months for smoother navigation
*/
preloadAdjacentMonths(currentYear, currentMonth, firstDayOfWeek, normalizeDateFn) {
const monthsToPreload = [
{ year: currentMonth === 0 ? currentYear - 1 : currentYear, month: currentMonth === 0 ? 11 : currentMonth - 1 },
{ year: currentMonth === 11 ? currentYear + 1 : currentYear, month: currentMonth === 11 ? 0 : currentMonth + 1 },
];
for (const { year, month } of monthsToPreload) {
const cacheKey = `${year}-${month}-${firstDayOfWeek}`;
if (!this.monthCache.has(cacheKey)) {
const days = this._generateMonthDays(year, month, firstDayOfWeek, normalizeDateFn);
if (this.monthCache.size >= this.MAX_CACHE_SIZE) {
const firstKey = this.monthCache.keys().next().value;
if (firstKey) {
this.monthCache.delete(firstKey);
}
}
this.monthCache.set(cacheKey, days);
}
}
}
/**
* Clear the month cache
*/
clearCache() {
this.monthCache.clear();
}
/**
* Get year grid for year view (12 years around current)
*/
getYearGrid(currentYear) {
return generateYearGrid(currentYear);
}
/**
* Get decade grid for decade view (12 decades)
*/
getDecadeGrid(currentDecade) {
return generateDecadeGrid(currentDecade);
}
/**
* Get timeline months for range mode timeline view
*/
getTimelineMonths(zoomLevel) {
const today = new Date();
const startDate = new Date(today);
startDate.setMonth(today.getMonth() - 6 * zoomLevel);
const endDate = new Date(today);
endDate.setMonth(today.getMonth() + 6 * zoomLevel);
const timelineMonths = [];
const current = new Date(startDate);
while (current <= endDate) {
timelineMonths.push(new Date(current));
current.setMonth(current.getMonth() + 1);
}
return {
timelineStartDate: startDate,
timelineEndDate: endDate,
timelineMonths,
};
}
/**
* Internal method to generate days for a month
*/
_generateMonthDays(year, month, firstDayOfWeek, normalizeDateFn) {
const days = [];
const firstDayOfMonth = new Date(year, month, 1);
const lastDayOfMonth = new Date(year, month + 1, 0);
const startDayOfWeek = firstDayOfMonth.getDay();
const emptyCellCount = (startDayOfWeek - firstDayOfWeek + 7) % 7;
const previousMonth = month === 0 ? 11 : month - 1;
const previousYear = month === 0 ? year - 1 : year;
const lastDayOfPreviousMonth = new Date(previousYear, previousMonth + 1, 0);
// Add days from previous month
for (let i = 0; i < emptyCellCount; i++) {
const dayNumber = lastDayOfPreviousMonth.getDate() - emptyCellCount + i + 1;
days.push(normalizeDateFn(new Date(previousYear, previousMonth, dayNumber)));
}
// Add days from current month
for (let i = 1; i <= lastDayOfMonth.getDate(); i++) {
days.push(normalizeDateFn(new Date(year, month, i)));
}
// Add days from next month to reach exactly 42 days (6 weeks)
// This ensures a consistent calendar grid height and prevents layout shifts
const nextMonth = month === 11 ? 0 : month + 1;
const nextYear = month === 11 ? year + 1 : year;
const remainingCells = 42 - days.length;
for (let i = 1; i <= remainingCells; i++) {
days.push(normalizeDateFn(new Date(nextYear, nextMonth, i)));
}
return days;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CalendarGenerationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CalendarGenerationService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CalendarGenerationService, decorators: [{
type: Injectable
}] });
class DatepickerParsingService {
constructor() {
this.datePipe = inject(DatePipe);
}
/**
* Formats a date or range for display in the input field
*/
formatDisplayValue(value, mode, format, locale) {
if (!value)
return '';
if (mode === 'single' && value instanceof Date) {
return this.formatDate(value, format, locale);
}
if (mode === 'range' && typeof value === 'object' && 'start' in value) {
const start = value.start ? this.formatDate(value.start, format, locale) : '';
const end = value.end ? this.formatDate(value.end, format, locale) : '';
return start || end ? `${start} - ${end}` : '';
}
if (mode === 'multiple' && Array.isArray(value)) {
return value.map((d) => this.formatDate(d, format, locale)).join(', ');
}
return '';
}
/**
* Parses a string input back into a Date object
* (Foundational for Relative Date Support)
*/
parseInput(input, _format) {
if (!input)
return null;
// Basic native parsing for now
// Future: Add relative date logic like "next Friday" here
const timestamp = Date.parse(input);
return isNaN(timestamp) ? null : new Date(timestamp);
}
parseDateString(dateString, adapter) {
if (adapter && typeof adapter.parse === 'function') {
// Use adapter with error callback for better error handling
const onError = (error) => {
if (isDevMode()) {
console.warn(`[ngxsmk-datepicker] Date parsing failed: ${error.message}`, dateString);
}
};
const parsed = adapter.parse(dateString, onError);
if (parsed) {
return getStartOfDay(parsed);
}
// If parsing failed, error was logged via callback
return null;
}
// Fallback to native Date parsing
try {
const date = new Date(dateString);
if (isNaN(date.getTime())) {
if (isDevMode()) {
console.warn(`[ngxsmk-datepicker] Invalid date string: "${dateString}"`);
}
return null;
}
return getStartOfDay(date);
}
catch (error) {
if (isDevMode()) {
console.warn(`[ngxsmk-datepicker] Date parsing error:`, error);
}
return null;
}
}
parseTypedInput(value, displayFormat) {
if (!value || !value.trim())
return null;
if (displayFormat) {
return this.parseCustomDateString(value, displayFormat);
}
const isoDate = new Date(value);
if (!isNaN(isoDate.getTime())) {
return isoDate;
}
const formats = [
/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/,
/^(\d{4})-(\d{1,2})-(\d{1,2})$/,
/^(\d{1,2})-(\d{1,2})-(\d{4})$/,
/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/,
];
for (const format of formats) {
const match = value.match(format);
if (match && match[1] && match[2] && match[3]) {
const date1 = new Date(parseInt(match[3]), parseInt(match[1]) - 1, parseInt(match[2]));
const date2 = new Date(parseInt(match[3]), parseInt(match[2]) - 1, parseInt(match[1]));
if (!isNaN(date1.getTime()) && date1.getMonth() === parseInt(match[1]) - 1) {
return date1;
}
if (!isNaN(date2.getTime()) && date2.getMonth() === parseInt(match[2]) - 1) {
return date2;
}
}
}
return null;
}
parseCustomDateString(dateString, format) {
if (!dateString || !format)
return null;
try {
const formatTokens = {
YYYY: {
regex: /(\d{4})/,
extractor: (match) => parseInt(match[1] || '0', 10),
},
YY: {
regex: /(\d{2})/,
extractor: (match) => 2000 + parseInt(match[1] || '0', 10),
},
MM: {
regex: /(\d{2})/,
extractor: (match) => parseInt(match[1] || '0', 10) - 1,
},
M: {
regex: /(\d{1,2})/,
extractor: (match) => parseInt(match[1] || '0', 10) - 1,
},
DD: {
regex: /(\d{2})/,
extractor: (match) => parseInt(match[1] || '0', 10),
},
D: {
regex: /(\d{1,2})/,
extractor: (match) => parseInt(match[1] || '0', 10),
},
hh: {
regex: /(\d{2})/,
extractor: (match) => parseInt(match[1] || '0', 10),
},
h: {
regex: /(\d{1,2})/,
extractor: (match) => parseInt(match[1] || '0', 10),
},
HH: {
regex: /(\d{2})/,
extractor: (match) => parseInt(match[1] || '0', 10),
},
H: {
regex: /(\d{1,2})/,
extractor: (match) => parseInt(match[1] || '0', 10),
},
mm: {
regex: /(\d{2})/,
extractor: (match) => parseInt(match[1] || '0', 10),
},
m: {
regex: /(\d{1,2})/,
extractor: (match) => parseInt(match[1] || '0', 10),
},
ss: {
regex: /(\d{2})/,
extractor: (match) => parseInt(match[1] || '0', 10),
},
s: {
regex: /(\d{1,2})/,
extractor: (match) => parseInt(match[1] || '0', 10),
},
a: {
regex: /(am|pm)/i,
extractor: (match) => ((match[1] || '').toLowerCase() === 'pm' ? 1 : 0),
},
A: {
regex: /(AM|PM)/,
extractor: (match) => ((match[1] || '') === 'PM' ? 1 : 0),
},
};
const dateParts = {};
let remainingFormat = format;
let remainingString = dateString;
const sortedTokens = Object.keys(formatTokens).sort((a, b) => b.length - a.length);
for (const token of sortedTokens) {
if (remainingFormat.includes(token)) {
const tokenInfo = formatTokens[token];
if (!tokenInfo)
continue;
const match = remainingString.match(tokenInfo.regex);
if (match) {
dateParts[token] = tokenInfo.extractor(match);
const matchIndex = remainingString.indexOf(match[0]);
remainingString =
remainingString.substring(0, matchIndex) + remainingString.substring(matchIndex + match[0].length);
remainingFormat = remainingFormat.replace(token, '');
}
}
}
const now = new Date();
const year = dateParts['YYYY'] !== undefined ? dateParts['YYYY'] : now.getFullYear();
const month = dateParts['MM'] !== undefined
? dateParts['MM']
: dateParts['M'] !== undefined
? dateParts['M']
: now.getMonth();
const day = dateParts['DD'] !== undefined ? dateParts['DD'] : dateParts['D'] !== undefined ? dateParts['D'] : now.getDate();
let hours = 0;
let minutes = 0;
let seconds = 0;
if (dateParts['hh'] !== undefined || dateParts['h'] !== undefined) {
const hour12 = dateParts['hh'] !== undefined ? dateParts['hh'] : dateParts['h'] !== undefined ? dateParts['h'] : 0;
const isPm = dateParts['a'] !== undefined ? dateParts['a'] : dateParts['A'] !== undefined ? dateParts['A'] : 0;
hours = (hour12 % 12) + (isPm ? 12 : 0);
}
else if (dateParts['HH'] !== undefined || dateParts['H'] !== undefined) {
hours = dateParts['HH'] !== undefined ? dateParts['HH'] : dateParts['H'] !== undefined ? dateParts['H'] : 0;
}
minutes = dateParts['mm'] !== undefined ? dateParts['mm'] : dateParts['m'] !== undefined ? dateParts['m'] : 0;
seconds = dateParts['ss'] !== undefined ? dateParts['ss'] : dateParts['s'] !== undefined ? dateParts['s'] : 0;
const date = new Date(year, month, day, hours, minutes, seconds);
if (isNaN(date.getTime())) {
return null;
}
return date;
}
catch {
return null;
}
}
formatValueForNativeInput(value, mode, showTime, timeOnly) {
if (!value) {
return '';
}
if (mode === 'range') {
if (Array.isArray(value) && value.length === 2 && value[0]) {
return this.formatDateForNativeInput(value[0], showTime, timeOnly);
}
return '';
}
if (value instanceof Date) {
return this.formatDateForNativeInput(value, showTime, timeOnly);
}
return '';
}
formatDateForNativeInput(date, showTime, timeOnly) {
if (!date || isNaN(date.getTime())) {
return '';
}
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
let result = `${year}-${month}-${day}`;
if (showTime || timeOnly) {
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
if (timeOnly) {
return `${hours}:${minutes}`;
}
result += `T${hours}:${minutes}`;
}
return result;
}
parseNativeInputValue(value, mode) {
if (!value) {
return null;
}
try {
const date = new Date(value);
if (isNaN(date.getTime())) {
return null;
}
if (mode === 'range') {
return [date, null];
}
return date;
}
catch {
return null;
}
}
/**
* Format a date using a custom pattern (e.g. MM/DD/YYYY, DD.MM.YYYY).
* Supports YYYY, YY, MM, M, DD, D, hh, h, HH, H, mm, m, ss, s, a, A.
*/
formatDateWithPattern(date, format) {
if (!date || isNaN(date.getTime()))
return '';
const pad = (n, len = 2) => n.toString().padStart(len, '0');
const month = date.getMonth() + 1;
const day = date.getDate();
const year = date.getFullYear();
const hours = date.getHours();
const minutes = date.getMinutes();
const seconds = date.getSeconds();
const ampm = hours >= 12 ? 'PM' : 'AM';
const hours12 = hours % 12 || 12;
return format
.replace(/YYYY/g, year.toString())
.replace(/YY/g, year.toString().slice(-2))
.replace(/MM/g, pad(month))
.replace(/M/g, month.toString())
.replace(/DD/g, pad(day))
.replace(/D/g, day.toString())
.replace(/hh/g, pad(hours12))
.replace(/h/g, hours12.toString())
.replace(/HH/g, pad(hours))
.replace(/H/g, hours.toString())
.replace(/mm/g, pad(minutes))
.replace(/m/g, minutes.toString())
.replace(/ss/g, pad(seconds))
.replace(/s/g, seconds.toString())
.replace(/a/g, ampm.toLowerCase())
.replace(/A/g, ampm);
}
formatDate(date, format, locale) {
try {
return this.datePipe.transform(date, format || 'mediumDate', undefined, locale) || '';
}
catch {
return date.toDateString();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DatepickerParsingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DatepickerParsingService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DatepickerParsingService, decorators: [{
type: Injectable
}] });
/**
* Service for handling touch gestures
*/
class TouchGestureHandlerService {
/**
* Handle date cell touch start
*/
handleDateCellTouchStart(event, day, state, config, callbacks) {
if (config.disabled || !day || callbacks.isDateDisabled(day)) {
return;
}
event.stopPropagation();
state.dateCellTouchHandled = false;
state.isDateCellTouching = true;
const touch = event.touches[0];
if (touch) {
state.dateCellTouchStartTime = Date.now();
state.dateCellTouchStartDate = day;
state.dateCellTouchStartX = touch.clientX;
state.dateCellTouchStartY = touch.clientY;
state.lastDateCellTouchDate = day;
}
else {
state.isDateCellTouching = false;
}
}
/**
* Handle date cell touch move
*/
handleDateCellTouchMove(event, state, config, callbacks, startDate) {
if (config.disabled || !state.isDateCellTouching || !state.dateCellTouchStartDate) {
return;
}
if (config.mode === 'range' && startDate) {
const touch = event.touches[0];
if (touch) {
const deltaX = Math.abs(touch.clientX - state.dateCellTouchStartX);
const deltaY = Math.abs(touch.clientY - state.dateCellTouchStartY);
const isSignificantMove = deltaX > 5 || deltaY > 5;
if (isSignificantMove) {
event.preventDefault();
}
try {
const elementFromPoint = document.elementFromPoint(touch.clientX, touch.clientY);
if (elementFromPoint) {
const dateCell = elementFromPoint.closest('.ngxsmk-day-cell');
if (dateCell && !dateCell.classList.contains('empty') && !dateCell.classList.contains('disabled')) {
const dateTimestamp = dateCell.getAttribute('data-date');
if (dateTimestamp) {
const dateValue = parseInt(dateTimestamp, 10);
if (!isNaN(dateValue)) {
const day = new Date(dateValue);
if (day && !isNaN(day.getTime()) && !callbacks.isDateDisabled(day)) {
const dayTime = getStartOfDay(day).getTime();
const startTime = getStartOfDay(startDate).getTime();
if (dayTime >= startTime) {
callbacks.onHoverChanged(day);
state.lastDateCellTouchDate = day;
}
else {
callbacks.onHoverChanged(null);
}
}
}
}
}
}
}
catch (error) {
// Silently handle touch move errors - fallback to default behavior
// Error is non-critical and doesn't affect core functionality
if (isDevMode()) {
console.warn('[ngxsmk-datepicker] Touch gesture error:', error);
}
}
}
}
}
/**
* Handle date cell touch end
*/
handleDateCellTouchEnd(event, day, state, config, callbacks) {
if (config.disabled) {
this.resetDateCellTouchState(state);
return;
}
if (!state.isDateCellTouching || !state.dateCellTouchStartDate) {
state.isDateCellTouching = false;
return;
}
const now = Date.now();
const touchDuration = state.dateCellTouchStartTime > 0 ? now - state.dateCellTouchStartTime : 0;
const touch = event.changedTouches[0];
let endDay = day || state.dateCellTouchStartDate;
if (touch) {
try {
const elementFromPoint = document.elementFromPoint(touch.clientX, touch.clientY);
if (elementFromPoint) {
const dateCell = elementFromPoint.closest('.ngxsmk-day-cell');
if (dateCell) {
const dateTimestamp = dateCell.getAttribute('data-date');
if (dateTimestamp) {
const dateValue = parseInt(dateTimestamp, 10);
if (!isNaN(dateValue)) {
const parsedDay = new Date(dateValue);
if (parsedDay && !isNaN(parsedDay.getTime())) {
endDay = parsedDay;
}
}
}
}
}
}
catch {
// Silently handle touch end date determination errors - use fallback
// Error is non-critical and fallback value ensures functionality continues
endDay = day || state.dateCellTouchStartDate;
}
}
const finalDay = state.lastDateCellTouchDate || endDay || state.dateCellTouchStartDate;
if (!finalDay || callbacks.isDateDisabled(finalDay)) {
this.resetDateCellTouchState(state);
return;
}
// Handle touch duration for click vs drag
if (touchDuration < 300 && finalDay === state.dateCellTouchStartDate) {
// Quick tap - treat as click
state.dateCellTouchHandled = true;
callbacks.onDateClick(finalDay);
}
else if (config.mode === 'range' &&
state.lastDateCellTouchDate &&
state.lastDateCellTouchDate !== state.dateCellTouchStartDate) {
// Drag gesture - select range
callbacks.onDateClick(state.lastDateCellTouchDate);
}
this.resetDateCellTouchState(state);
callbacks.onStateChanged();
}
/**
* Handle calendar swipe start
*/
handleCalendarSwipeStart(event, state) {
const touch = event.touches[0];
if (touch) {
state.calendarSwipeStartX = touch.clientX;
state.calendarSwipeStartY = touch.clientY;
state.calendarSwipeStartTime = Date.now();
state.isCalendarSwiping = true;
}
}
/**
* Handle calendar swipe move
*/
handleCalendarSwipeMove(event, state) {
if (!state.isCalendarSwiping)
return;
const touch = event.touches[0];
if (touch) {
const deltaX = Math.abs(touch.clientX - state.calendarSwipeStartX);
const deltaY = Math.abs(touch.clientY - state.calendarSwipeStartY);
// If significant movement, prevent default scrolling
if (deltaX > 10 || deltaY > 10) {
event.preventDefault();
}
}
}
/**
* Handle calendar swipe end
*/
handleCalendarSwipeEnd(event, state, config, callbacks) {
if (!state.isCalendarSwiping)
return;
const touch = event.changedTouches[0];
if (!touch) {
this.resetSwipeState(state);
return;
}
const deltaX = touch.clientX - state.calendarSwipeStartX;
const deltaY = touch.clientY - state.calendarSwipeStartY;
const deltaTime = Date.now() - state.calendarSwipeStartTime;
const absDeltaX = Math.abs(deltaX);
const absDeltaY = Math.abs(deltaY);
// Check if it's a valid swipe
if (deltaTime < config.swipeTimeThreshold) {
if (absDeltaX > config.swipeThreshold && absDeltaY < absDeltaX) {
// Horizontal swipe - Month change
if (deltaX > 0) {
callbacks.changeMonth(-1);
}
else {
callbacks.changeMonth(1);
}
}
else if (absDeltaY > config.swipeThreshold && absDeltaX < absDeltaY) {
// Vertical swipe - Year change (if callback provided)
if (callbacks.changeYear) {
if (deltaY < 0) {
callbacks.changeYear(1); // Swipe Up -> Next Year
}
else {
callbacks.changeYear(-1); // Swipe Down -> Prev Year
}
}
}
}
this.resetSwipeState(state);
callbacks.onStateChanged();
}
/**
* Reset date cell touch state
*/
resetDateCellTouchState(state) {
state.isDateCellTouching = false;
state.dateCellTouchStartTime = 0;
state.dateCellTouchStartDate = null;
state.lastDateCellTouchDate = null;
}
/**
* Reset swipe state
*/
resetSwipeState(state) {
state.calendarSwipeStartX = 0;
state.calendarSwipeStartY = 0;
state.calendarSwipeStartTime = 0;
state.isCalendarSwiping = false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: TouchGestureHandlerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: TouchGestureHandlerService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: TouchGestureHandlerService, decorators: [{
type: Injectable
}] });
class PopoverPositioningService {
/**
* 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.
*/
positionRelativeToInput(popover, inputGroup, options) {
if (!popover || !inputGroup || options.isInlineMode) {
return;
}
const desktopBreakpoint = options.desktopBreakpoint ?? 1025;
const isDesktop = window.innerWidth >= desktopBreakpoint;
if (isDesktop && !options.shouldAppendToBody) {
this.clearPositionStyles(popover, false);
return;
}
if (!isDesktop && options.centerOnMobile) {
this.clearPositionStyles(popover, true);
return;
}
try {
this.applyPosition(popover, inputGroup, options);
}
catch (error) {
if (isDevMode()) {
console.warn('[ngxsmk-datepicker] Error positioning popover:', error);
}
}
}
clearPositionStyles(popover, includePositionRight) {
popover.style.removeProperty('top');
popover.style.removeProperty('left');
popover.style.removeProperty('bottom');
popover.style.removeProperty('transform');
popover.style.removeProperty('width');
popover.style.removeProperty('min-width');
popover.style.removeProperty('max-width');
if (includePositionRight) {
popover.style.removeProperty('position');
popover.style.removeProperty('right');
}
}
applyPosition(popover, inputGroup, options) {
const narrowViewport = options.narrowViewport ?? 500;
const minHeight = options.minHeight ?? 400;
const minWidth = options.minWidth ?? 360;
const gap = options.gap ?? 8;
const inputRect = inputGroup.getBoundingClientRect();
const popoverRect = popover.getBoundingClientRect();
const viewportHeight = window.innerHeight;
const viewportWidth = window.innerWidth;
const spaceBelow = viewportHeight - inputRect.bottom;
const spaceAbove = inputRect.top;
const spaceRight = viewportWidth - inputRect.left;
const resolvedMinHeight = popoverRect.height || minHeight;
const resolvedMinWidth = popoverRect.width || minWidth;
const fitsHorizontal = spaceRight >= resolvedMinWidth || viewportWidth < narrowViewport;
const useViewportCoords = options.shouldAppendToBody;
const setStyle = this.createStyleSetter(popover, useViewportCoords);
const popoverWidth = Math.max(minWidth, Math.round(inputRect.width));
if (spaceBelow >= resolvedMinHeight && fitsHorizontal) {
const top = inputRect.bottom + window.scrollY + gap;
const left = inputRect.left + window.scrollX;
this.setPlacement(setStyle, top, left);
this.setPopoverWidth(popoverWidth, popover);
return;
}
if (spaceAbove >= resolvedMinHeight && fitsHorizontal) {
const top = inputRect.top + window.scrollY - resolvedMinHeight - gap;
const left = inputRect.left + window.scrollX;
this.setPlacement(setStyle, top, left);
this.setPopoverWidth(popoverWidth, popover);
return;
}
// When appended to body we must always set position so CSS (e.g. left: 0 !important) does not win
if (useViewportCoords) {
// Prioritize space above if below doesn't fit, otherwise fallback to bound below
let top;
if (spaceAbove > spaceBelow && spaceBelow < resolvedMinHeight) {
top = Math.max(window.scrollY + gap, inputRect.top + window.scrollY - resolvedMinHeight - gap);
}
else {
const spaceExceeded = resolvedMinHeight - spaceBelow;
top = inputRect.bottom + window.scrollY + gap;
if (spaceExceeded > 0 && spaceBelow < resolvedMinHeight) {
top = Math.max(window.scrollY, top - spaceExceeded - gap * 2);
}
}
const left = inputRect.left + window.scrollX;
this.setPlacement(setStyle, top, left);
this.setPopoverWidth(popoverWidth, popover);
return;
}
this.clearPositionStyles(popover, false);
}
setPopoverWidth(widthPx, popover) {
const w = `${widthPx}px`;
// Avoid !important wrapper to allow consumer CSS overrides
popover.style.setProperty('min-width', w);
// Allow intrinsic sizing and consumer CSS overrides by clearing hard constraints
popover.style.removeProperty('width');
popover.style.removeProperty('max-width');
}
createStyleSetter(popover, useViewportCoords) {
if (useViewportCoords) {
return (key, value) => popover.style.setProperty(key, value, 'important');
}
return (key, value) => {
popover.style[key] = value;
};
}
setPlacement(setStyle, top, left) {
setStyle('position', 'absolute');
setStyle('top', `${top}px`);
setStyle('left', `${left}px`);
setStyle('transform', 'none');
setStyle('right', 'auto');
setStyle('bottom', 'auto');
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: PopoverPositioningService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: PopoverPositioningService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: PopoverPositioningService, decorators: [{
type: Injectable
}] });
/**
* 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
*/
class CustomDateFormatService {
constructor(locale = 'en-US') {
this.locale = locale;
this.monthNames = new Map();
this.weekdayNames = new Map();
this.initializeLocaleData();
}
/**
* Set the locale for formatting
*/
setLocale(locale) {
this.locale = locale;
// Clear cached data so it's regenerated with new locale
this.monthNames.clear();
this.weekdayNames.clear();
this.initializeLocaleData();
}
/**
* 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, pattern) {
if (!date) {
return '';
}
const year = date.getFullYear();
const month = date.getMonth();
const dayOfMonth = date.getDate();
const dayOfWeek = date.getDay();
const hours24 = date.getHours();
const hours12 = hours24 % 12 || 12;
const minutes = date.getMinutes();
const seconds = date.getSeconds();
const isAM = hours24 < 12;
const monthNames = this.getMonthNames();
const weekdayNames = this.getWeekdayNames();
const replacements = {
YYYY: year.toString(),
YY: year.toString().slice(-2),
MMMM: monthNames.full[month] || '',
MMM: monthNames.abbreviated[month] || '',
MM: (month + 1).toString().padStart(2, '0'),
M: (month + 1).toString(),
DDDD: weekdayNames.full[dayOfWeek] || '',
DDD: weekdayNames.abbreviated[dayOfWeek] || '',
DD: dayOfMonth.toString().padStart(2, '0'),
D: dayOfMonth.toString(),
HH: hours24.toString().padStart(2, '0'),
H: hours24.toString(),
hh: hours12.toString().padStart(2, '0'),
h: hours12.toString(),
mm: minutes.toString().padStart(2, '0'),
m: minutes.toString(),
ss: seconds.toString().padStart(2, '0'),
s: seconds.toString(),
A: isAM ? 'AM' : 'PM',
a: isAM ? 'am' : 'pm',
};
const regex = /YYYY|YY|MMMM|MMM|MM|M|DDDD|DDD|DD|D|HH|H|hh|h|mm|m|ss|s|[Aa]/g;
return pattern.replaceAll(regex, (match) => replacements[match] || match);
}
/**
* 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, _pattern) {
try {
// For now, use basic date parsing as full pattern parsing is complex
// This can be enhanced in the future to support full pattern parsing
const date = new Date(dateString);
return Number.isNaN(date.getTime()) ? null : date;
}
catch {
return null;
}
}
getMonthNames() {
if (!this.monthNames.has(this.locale)) {
const months = Array.from({ length: 12 }).map((_, i) => {
const date = new Date(2000, i, 1);
return {
full: date.toLocaleDateString(this.locale, { month: 'long' }),
abbreviated: date.toLocaleDateString(this.locale, { month: 'short' }),
};
});
this.monthNames.set(this.locale, [...months.map((m) => m.full), ...months.map((m) => m.abbreviated)]);
}
const monthData = this.monthNames.get(this.locale) || [];
return {
full: monthData.slice(0, 12),
abbreviated: monthData.slice(12),
};
}
initializeLocaleData() {
// Ensure month and weekday names are cached
this.getMonthNames();
this.getWeekdayNames();
}
getWeekdayNames() {
if (!this.weekdayNames.has(this.locale)) {
const weekdays = Array.from({ length: 7 }).map((_, i) => {
const date = new Date(2000, 0, 2 + i); // Jan 2, 2000 was a Sunday
return {
full: date.toLocaleDateString(this.locale, { weekday: 'long' }),
abbreviated: date.toLocaleDateString(this.locale, {
weekday: 'short',
}),
};
});
this.weekdayNames.set(this.locale, [...weekdays.map((w) => w.full), ...weekdays.map((w) => w.abbreviated)]);
}
const weekdayData = this.weekdayNames.get(this.locale) || [];
return {
full: weekdayData.slice(0, 7),
abbreviated: weekdayData.slice(7),
};
}
}
class NaturalLanguageParserService {
/**
* Parse relative time expressions into actual Date or Range objects.
* e.g., 'today', 'tomorrow', 'in 3 weeks', 'Q3 2026'
*/
parse(text) {
if (!text)
return null;
const clean = text.toLowerCase().trim();
const today = new Date();
if (clean === 'today')
return new Date(today);
if (clean === 'tomorrow') {
return new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
}
if (clean === 'yesterday') {
return new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1);
}
// Relative offsets: "in 3 weeks", "2 days ago", "5 months"
const relativeMatch = clean.match(/^(?:in\s+)?(\d+)\s+(day|week|month|year)s?(?:\s+ago)?$/);
if (relativeMatch) {
const amount = parseInt(relativeMatch[1] || '0', 10);
const unit = relativeMatch[2];
const direction = clean.includes('ago') ? -1 : 1;
const result = new Date(today);
if (unit === 'day') {
result.setDate(today.getDate() + amount * direction);
}
else if (unit === 'week') {
result.setDate(today.getDate() + amount * 7 * direction);
}
else if (unit === 'month') {
result.setMonth(today.getMonth() + amount * direction);
}
else if (unit === 'year') {
result.setFullYear(today.getFullYear() + amount * direction);
}
return result;
}
// Calendar quarter mapping: "q3 2025" or "Q2 2026"
const quarterMatch = clean.match(/^q([1-4])\s+(\d{4})$/);
if (quarterMatch) {
const quarter = parseInt(quarterMatch[1] || '1', 10);
const year = parseInt(quarterMatch[2] || '2026', 10);
const startMonth = (quarter - 1) * 3;
return {
start: new Date(year, startMonth, 1),
end: new Date(year, startMonth + 3, 0, 23, 59, 59, 999),
};
}
return null;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NaturalLanguageParserService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NaturalLanguageParserService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NaturalLanguageParserService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
/**
* A comprehensive, production-ready Angular datepicker component with extensive features.
*
* @remarks
* ## Performance Characteristics
*
* - **Calendar Generation**: O(1) per month when cached, O(n) for first generation where n = days in month
* - **Date Validation**: O(n) where n = disabledDates.length + disabledRanges.length
* - **Range Selection**: O(1) for single date, O(n) for multiple selection where n = selectedDates.length
* - **Change Detection**: Optimized with OnPush strategy and manual scheduling for zoneless compatibility
* - **Memory Management**: LRU cache for calendar months (max 24 entries), comprehensive cleanup in ngOnDestroy
*
* ## Key Features
*
* - Multiple selection modes: single, range, multiple, week, month, quarter, year
* - Full keyboard navigation and accessibility (WCAG 2.1 AA compliant)
* - SSR and zoneless Angular compatible
* - Signal Forms integration (Angular 21+)
* - Custom date adapters (Native, date-fns, Luxon, Day.js)
* - Internationalization with RTL support
* - Time selection with timezone support
* - Holiday provider system
* - Custom hooks for extensibility
* - Mobile-optimized with touch gestures
*
* ## Usage Example
*
* ```typescript
* // Basic usage
* <ngxsmk-datepicker
* [(ngModel)]="selectedDate"
* [mode]="'single'"
* [locale]="'en-US'">
* </ngxsmk-datepicker>
*
* // With Reactive Forms
* <ngxsmk-datepicker
* [formControl]="dateControl"
* [minDate]="minDate"
* [maxDate]="maxDate">
* </ngxsmk-datepicker>
*
* // With Signal Forms (Angular 21+)
* <ngxsmk-datepicker
* [field]="form.field('date')"
* [mode]="'range'">
* </ngxsmk-datepicker>
* ```
*
* ## Performance Optimization Tips
*
* 1. **Large Disabled Date Lists**: For lists >1000 dates, consider using a Set or DateRange tree
* 2. **Multiple Instances**: The component uses a static registry for efficient instance management
* 3. **Calendar Caching**: Months are automatically cached (LRU, max 24 entries)
* 4. **Change Detection**: Uses OnPush strategy - call `markForCheck()` only when needed
* 5. **Memoization**: Internal memoization optimizes date comparisons and validation
*
* ## Memory Management
*
* The component implements comprehensive cleanup:
* - All timeouts and animation frames are tracked and cleared
* - Event listeners are properly removed
* - RxJS subscriptions are completed
* - Effects are destroyed
* - Cache is invalidated on relevant changes
*
* ## Browser Compatibility
*
* - Modern browsers (Chrome, Firefox, Safari, Edge)
* - Mobile browsers (iOS Safari, Chrome Mobile)
* - SSR compatible (Angular Universal)
* - Works with and without Zone.js
*
* @see {@link DatepickerConfig} for global configuration options
* @see {@link DatepickerHooks} for extension hooks
* @see {@link HolidayProvider} for custom holiday support
*/
class NgxsmkDatepickerComponent {
static { this._idCounter = 0; }
static { this._allInstances = new Set(); }
static { this._materialSupportRegistered = false; }
static {
const globalToken = globalThis['__NGXSMK_MAT_FORM_FIELD_CONTROL__'];
if (globalToken)
NgxsmkDatepickerComponent.withMaterialSupport(globalToken);
}
static _patchMetadataArrays(target, token, provider) {
const metadataKeys = ['__annotations__', 'decorators'];
const record = target;
for (const key of metadataKeys) {
const list = record[key] ?? [];
for (const entry of list) {
const decorated = entry;
const config = (decorated?.args?.[0] ?? entry);
if (config?.providers && Array.isArray(config.providers)) {
const providers = config.providers;
if (!providers.some((p) => p === token || (p && p.provide === token))) {
providers.push(provider);
}
}
}
}
}
static withMaterialSupport(matFormFieldControlToken, targetCmp = NgxsmkDatepickerComponent) {
if (targetCmp === NgxsmkDatepickerComponent) {
if (NgxsmkDatepickerComponent._materialSupportRegistered)
return;
NgxsmkDatepickerComponent._materialSupportRegistered = true;
}
const token = globalThis['__NGXSMK_MAT_FORM_FIELD_CONTROL__'] ?? matFormFieldControlToken;
const provider = {
provide: token,
useExisting: forwardRef(() => targetCmp),
multi: false,
};
NgxsmkDatepickerComponent._patchMetadataArrays(targetCmp, token, provider);
}
set disabledDates(val) {
this._disabledDates = Array.isArray(val) ? val : [];
this._syncDisabledDatesCache();
this._updateMemoSignals();
}
get disabledDates() {
return this._disabledDates;
}
set disabledRanges(val) {
this._disabledRanges = Array.isArray(val) ? val : [];
this._syncDisabledDatesCache();
this._updateMemoSignals();
}
get disabledRanges() {
return this._disabledRanges;
}
set placeholder(value) {
this._placeholder = value;
}
get placeholder() {
if (this._placeholder !== null) {
return this._placeholder;
}
return this.getTranslation(this.timeOnly ? 'selectTime' : 'selectDate');
}
set inputId(value) {
this._inputId = value;
this.scheduleChangeDetection();
}
get inputId() {
return this._inputId;
}
set name(value) {
this._name = value;
this.scheduleChangeDetection();
}
get name() {
return this._name;
}
set autocomplete(value) {
this._autocomplete = value;
this.scheduleChangeDetection();
}
get autocomplete() {
return this._autocomplete;
}
get _clearLabel() {
return this.clearLabel || this.getTranslation('clear');
}
get _closeLabel() {
return this.closeLabel || this.getTranslation('close');
}
get _prevMonthAriaLabel() {
return this.prevMonthAriaLabel || this.getTranslation('previousMonth');
}
get _nextMonthAriaLabel() {
return this.nextMonthAriaLabel || this.getTranslation('nextMonth');
}
get _clearAriaLabel() {
return this.clearAriaLabel || this.getTranslation('clearSelection');
}
get _closeAriaLabel() {
return this.closeAriaLabel || this.getTranslation('closeCalendar');
}
set yearRange(value) {
this._yearRange.set(value);
}
get yearRange() {
return this._yearRange();
}
set calendars(value) {
this.calendarCount = value;
}
get calendars() {
return this.calendarCount;
}
getTimezoneOptions() {
const tz = this.timezone || this.defaultTimezone;
const exists = this.timezoneOptions.some((o) => o.value === tz);
if (!exists && tz) {
return [{ label: tz, value: tz }, ...this.timezoneOptions];
}
return this.timezoneOptions;
}
set calendarCount(value) {
const coerced = typeof value === 'string' ? Number.parseInt(value, 10) : Number(value);
const n = Number.isFinite(coerced) ? coerced : Number.NaN;
// Clamp calendarCount to valid range (1-12) for performance
if (!Number.isFinite(n) || n < 1) {
if (isDevMode()) {
console.warn(`[ngxsmk-datepicker] calendarCount must be at least 1. ` + `Received: ${value}. Setting to 1.`);
}
this._calendarCount = 1;
}
else if (n > 12) {
if (isDevMode()) {
console.warn(`[ngxsmk-datepicker] calendarCount should not exceed 12 for performance reasons. ` +
`Received: ${value}. Setting to 12.`);
}
this._calendarCount = 12;
}
else {
this._calendarCount = Math.trunc(n);
}
}
get calendarCount() {
return this._calendarCount;
}
get _shouldAppendToBody() {
if (this.isInlineMode)
return false;
return this.appendToBody() || (this.autoDetectMobile && this.isMobileDevice()) || this.isInsideModal();
}
/**
* Detects if the datepicker is rendered inside a modal/dialog so the calendar
* can be appended to body and positioned above the modal.
*/
isInsideModal() {
if (!this.isBrowser || !this.elementRef?.nativeElement) {
return false;
}
let el = this.elementRef.nativeElement;
const modalRoles = new Set(['dialog', 'alertdialog']);
const modalClassPatterns = [
'mat-dialog-container',
'mat-mdc-dialog-container',
'modal',
'modal-dialog',
'modal-content',
'cdk-dialog-container',
'p-dialog',
'mdc-dialog',
'overlay-container',
];
while (el) {
const role = el.getAttribute?.('role');
if (role && modalRoles.has(role)) {
return true;
}
const className = el.className?.toString?.() ?? '';
if (typeof className === 'string' && modalClassPatterns.some((p) => className.includes(p))) {
return true;
}
el = el.parentElement;
}
return false;
}
get isCalendarOpen() {
return this._isCalendarOpen();
}
set isCalendarOpen(value) {
if (this._isCalendarOpen() !== value) {
this._isCalendarOpen.set(value);
this.stateChanges.next();
}
// Signal update handles change detection
}
/** Public getter for template: true while calendar is opening/generating (loading state). */
get isCalendarOpening() {
return this._isCalendarOpening();
}
/** Returns translated "Loading calendar..." for template and ARIA. */
getCalendarLoadingMessage() {
return this.getTranslation('calendarLoading') || 'Loading calendar...';
}
set value(val) {
if (!this._field && val !== undefined) {
const normalizedValue = this._normalizeValue(val);
if (!this.isValueEqual(normalizedValue, this._value)) {
this._value = normalizedValue;
this.initializeValue(normalizedValue);
this.generateCalendar();
}
}
}
get value() {
return this._value;
}
set field(field) {
this.fieldSyncService.cleanup();
if (this._fieldEffectRef) {
this._fieldEffectRef.destroy();
this._fieldEffectRef = null;
}
this._field = field;
if (field && (typeof field === 'object' || typeof field === 'function')) {
this._fieldEffectRef = this.fieldSyncService.setupFieldSync(field, {
onValueChanged: (value) => {
this._value = value;
this.initializeValue(value);
this.generateCalendar();
if (this._field === field) {
this.scheduleChangeDetection();
}
},
onDisabledChanged: (disabled) => {
if (this.disabled !== disabled) {
this.disabled = disabled;
this.scheduleChangeDetection();
}
},
onRequiredChanged: (required) => {
this.required = required;
},
onErrorStateChanged: (hasError) => {
this.errorState = hasError;
},
onSyncError: (_error) => { },
normalizeValue: (value) => {
return this._normalizeValue(value);
},
isValueEqual: (val1, val2) => {
return this.isValueEqual(val1, val2);
},
onCalendarGenerated: () => { },
onStateChanged: () => {
this.scheduleChangeDetection();
},
});
this.syncFieldValue(field);
}
else {
this._value = null;
this.initializeValue(null);
this.generateCalendar();
}
}
get field() {
return this._field;
}
syncFieldValue(field) {
const result = this.fieldSyncService.syncFieldValue(field, {
onValueChanged: (value) => {
this._value = value;
this.initializeValue(value);
this.generateCalendar();
this.scheduleChangeDetection();
},
onDisabledChanged: (disabled) => {
if (this.disabled !== disabled) {
this.disabled = disabled;
this.scheduleChangeDetection();
this.stateChanges.next();
}
},
onRequiredChanged: (required) => {
this._required = required;
this.stateChanges.next();
},
onErrorStateChanged: (hasError) => {
this._errorState = hasError;
this.stateChanges.next();
},
onSyncError: (_error) => { },
normalizeValue: (value) => {
return this._normalizeValue(value);
},
isValueEqual: (val1, val2) => {
return this.isValueEqual(val1, val2);
},
onCalendarGenerated: () => {
this.generateCalendar();
},
onStateChanged: () => {
this.stateChanges.next();
this.scheduleChangeDetection();
},
});
return result;
}
set startAt(value) {
this._startAtDate = this._normalizeDate(value);
}
set locale(value) {
if (value && value !== this._locale) {
this._locale = value;
this._localeSignal.set(value);
if (this.translationRegistry) {
this.updateRtlState();
this.initializeTranslations();
this.generateLocaleData();
this.generateCalendar();
this.scheduleChangeDetection();
}
}
else if (value) {
this._locale = value;
this._localeSignal.set(value);
}
}
get locale() {
return this._locale;
}
get isDarkMode() {
return this.theme === 'dark';
}
set dateFormatPattern(value) {
this._dateFormatPattern = value;
if (value) {
// Initialize or update the custom format service with current locale
if (this.customDateFormatService) {
this.customDateFormatService.setLocale(this._locale);
}
else {
this.customDateFormatService = new CustomDateFormatService(this._locale);
}
}
}
get dateFormatPattern() {
return this._dateFormatPattern;
}
/**
* 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) {
this._animationConfig = value;
// Merge with global config: component input takes precedence
const mergedConfig = {
...this.globalConfig?.animations,
...this._animationConfig,
};
this.applyAnimationConfig(mergedConfig);
}
get animationConfig() {
return this._animationConfig;
}
set rtl(value) {
this._rtl = value;
this.updateRtlState();
}
get rtl() {
return this._rtl;
}
get isRtl() {
if (this._rtl !== null) {
return this._rtl;
}
if (this.isBrowser && typeof document !== 'undefined') {
const docDir = document.documentElement.dir || document.body.dir;
if (docDir === 'rtl' || docDir === 'ltr') {
return docDir === 'rtl';
}
}
return this.localeRegistry.isRtlLocale(this._locale);
}
get rtlClass() {
return this.isRtl;
}
set disabledState(isDisabled) {
this.setDisabledState(isDisabled);
}
get focused() {
return this._focused || this.isCalendarOpen;
}
get empty() {
const value = this._value;
if (!value || value === null)
return true;
if (this.mode === 'range' || this.mode === 'multiple') {
return !Array.isArray(value) || value.length === 0;
}
return false;
}
get shouldLabelFloat() {
return this.focused || !this.empty;
}
get required() {
return this._required;
}
set required(value) {
if (this._required !== value) {
this._required = value;
this.stateChanges.next();
this.scheduleChangeDetection();
}
}
get errorState() {
return this._errorState;
}
set errorState(value) {
if (this._errorState !== value) {
this._errorState = value;
this.stateChanges.next();
this.scheduleChangeDetection();
}
}
get controlType() {
return 'ngxsmk-datepicker';
}
get autofilled() {
return false;
}
get id() {
return this._uniqueId;
}
get describedBy() {
return this.userAriaDescribedBy || `datepicker-help-${this._uniqueId}`;
}
setDescribedByIds(ids) {
if (ids && ids.length > 0) {
this.userAriaDescribedBy = ids.join(' ');
}
else {
this.userAriaDescribedBy = '';
}
this.stateChanges.next();
}
onContainerClick(_event) {
if (!this.disabled && !this.isCalendarOpen) {
this.focusInput();
}
}
/** User-facing validation error message when set (e.g. from typed input or min/max). */
get validationErrorMessage() {
return this._validationErrorMessage();
}
setValidationError(code, message) {
// Signal write auto-notifies change detection (no manual markForCheck needed).
this._validationErrorMessage.set(message);
this.validationError.emit({ code, message });
}
clearValidationError() {
this._validationErrorMessage.set(null);
}
set minDate(value) {
this._minDate = this._normalizeDate(value);
this.adjustDisplayedDateToRange();
this._updateMemoSignals();
this._invalidateMemoCache();
this.cdr.markForCheck();
}
get minDate() {
return this._minDate;
}
set maxDate(value) {
this._maxDate = this._normalizeDate(value);
this.adjustDisplayedDateToRange();
this._updateMemoSignals();
this._invalidateMemoCache();
this.cdr.markForCheck();
}
get maxDate() {
return this._maxDate;
}
set ranges(value) {
this._ranges = processDateRanges(value);
this.updateRangesArray();
}
get naturalLanguagePreview() {
return this._naturalLanguagePreview();
}
set naturalLanguagePreview(value) {
this._naturalLanguagePreview.set(value);
}
get showNaturalLanguagePreview() {
return this._showNaturalLanguagePreview();
}
set showNaturalLanguagePreview(value) {
this._showNaturalLanguagePreview.set(value);
}
constructor() {
this._uniqueId = `ngxsmk-datepicker-${NgxsmkDatepickerComponent._idCounter++}`;
this.mode = 'single';
this.calendarViewMode = 'month';
this.isInvalidDate = () => false;
/**
* 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()}`));
* ```
*/
this.asyncDateFilter = null;
/** Emits while an asyncDateFilter request is in flight (true) and when it settles (false). */
this.asyncDateFilterLoading = output();
/** Emits when asyncDateFilter rejects; the previous disabled set is kept. */
this.asyncDateFilterError = output();
this.showRanges = input(true, ...(ngDevMode ? [{ debugName: "showRanges" }] : /* istanbul ignore next */ []));
this.showPresets = input(false, ...(ngDevMode ? [{ debugName: "showPresets" }] : /* istanbul ignore next */ []));
this.showTime = false;
this.timeOnly = false;
this.timeRangeMode = input(false, ...(ngDevMode ? [{ debugName: "timeRangeMode" }] : /* istanbul ignore next */ []));
this.showCalendarButton = false;
this.minuteInterval = 1;
this.use24Hour = false;
this.secondInterval = 1;
this.showSeconds = false;
this.holidayProvider = null;
this.disableHolidays = false;
this._disabledDates = [];
this._disabledRanges = [];
this.dateTemplate = input(null, ...(ngDevMode ? [{ debugName: "dateTemplate" }] : /* istanbul ignore next */ []));
this._placeholder = null;
this.inline = false;
/**
* 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" />
* ```
*/
this.responsive = true;
this._inputId = '';
this._name = '';
this._autocomplete = 'off';
this.clearLabel = '';
this.closeLabel = '';
this.prevMonthAriaLabel = '';
this.nextMonthAriaLabel = '';
this.clearAriaLabel = '';
this.closeAriaLabel = '';
this.weekStart = null;
this._yearRange = signal(10, ...(ngDevMode ? [{ debugName: "_yearRange" }] : /* istanbul ignore next */ []));
/**
* 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`.
*/
this.showOtherMonths = false;
this.weekDaysFull = [];
this.hooks = null;
this.enableKeyboardShortcuts = true;
this.customShortcuts = null;
this.autoApplyClose = input(false, ...(ngDevMode ? [{ debugName: "autoApplyClose" }] : /* istanbul ignore next */ []));
/**
* 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).
*/
this.allowSameDay = input(false, ...(ngDevMode ? [{ debugName: "allowSameDay" }] : /* istanbul ignore next */ []));
this.allowTyping = false;
/**
* 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.
*/
this.inputMask = false;
this.enableNaturalLanguage = false;
this.naturalLanguagePreviewTemplate = input(...(ngDevMode ? [undefined, { debugName: "naturalLanguagePreviewTemplate" }] : /* istanbul ignore next */ []));
this.naturalLanguageResolved = output();
this.enableAi = false;
this.aiPlaceholder = 'Ask AI (e.g. "next Friday", "last 7 days")...';
this.aiSuggestions = ['Tomorrow', 'Next Friday', 'In 3 days', 'Next month'];
this.showAiSuggestions = true;
this.aiPromptSubmitted = output();
this.isAiResolving = false;
this.invalidRange = output();
this.showTimezoneSelector = input(false, ...(ngDevMode ? [{ debugName: "showTimezoneSelector" }] : /* istanbul ignore next */ []));
this.defaultTimezone = 'UTC';
this.timezoneChange = output();
this.timezoneOptions = [
{ label: 'UTC', value: 'UTC' },
{ label: 'New York (EST/EDT)', value: 'America/New_York' },
{ label: 'Chicago (CST/CDT)', value: 'America/Chicago' },
{ label: 'Los Angeles (PST/PDT)', value: 'America/Los_Angeles' },
{ label: 'London (GMT/BST)', value: 'Europe/London' },
{ label: 'Paris (CET/CEST)', value: 'Europe/Paris' },
{ label: 'Tokyo (JST)', value: 'Asia/Tokyo' },
{ label: 'Kolkata (IST)', value: 'Asia/Kolkata' },
{ label: 'Sydney (AEST/AEDT)', value: 'Australia/Sydney' },
];
this._calendarCount = 1;
this.calendarLayout = '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.
*/
this.changeActiveMonthOnSelection = true;
/** Shows an ISO 8601 week-number column on the left of the day grid. */
this.showWeekNumbers = false;
/** Header label for the week-number column (e.g. "Wk", "KW", "S"). */
this.weekNumberLabel = 'Wk';
/**
* 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.
*/
this.secondaryCalendar = 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).
*/
this.dayMetadata = null;
/**
* Custom content rendered at the top of the popover/inline calendar.
* Template context: `let-actions` → `{ clear(): void; close(): void }`.
*/
this.calendarHeaderTemplate = 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.
*/
this.calendarFooterTemplate = null;
this.defaultMonthOffset = 0;
/**
* 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>
* ```
*/
this.syncScroll = input({ enabled: false, monthGap: 1 }, ...(ngDevMode ? [{ debugName: "syncScroll" }] : /* istanbul ignore next */ []));
this.align = 'left';
this.useNativePicker = false;
this.enableHapticFeedback = input(false, ...(ngDevMode ? [{ debugName: "enableHapticFeedback" }] : /* istanbul ignore next */ []));
this.mobileModalStyle = 'center';
this.mobileTimePickerStyle = 'slider';
this.enablePullToRefresh = input(false, ...(ngDevMode ? [{ debugName: "enablePullToRefresh" }] : /* istanbul ignore next */ []));
this.mobileTheme = input('comfortable', ...(ngDevMode ? [{ debugName: "mobileTheme" }] : /* istanbul ignore next */ []));
this.enableVoiceInput = false;
this.autoDetectMobile = true;
this.disableFocusTrap = input(false, ...(ngDevMode ? [{ debugName: "disableFocusTrap" }] : /* istanbul ignore next */ []));
this.appendToBody = input(false, ...(ngDevMode ? [{ debugName: "appendToBody" }] : /* istanbul ignore next */ []));
this.appRef = inject(ApplicationRef);
this.document = inject(DOCUMENT);
this.portalViewRef = null;
this._isCalendarOpen = signal(false, ...(ngDevMode ? [{ debugName: "_isCalendarOpen" }] : /* istanbul ignore next */ []));
this._isCalendarOpening = signal(false, ...(ngDevMode ? [{ debugName: "_isCalendarOpening" }] : /* istanbul ignore next */ []));
this.openCalendarTimeoutId = null;
this.lastToggleTime = 0;
this.touchStartTime = 0;
this.touchStartElement = null;
this.pointerDownTime = 0;
this.isPointerEvent = false;
this.previousFocusElement = null;
this._value = null;
this._field = null;
this._fieldEffectRef = null;
this._startAtDate = null;
this._locale = 'en-US';
this.theme = 'light';
this._dateFormatPattern = null;
this.customDateFormatService = null;
this._animationConfig = null;
this._rtl = null;
this.classes = input(...(ngDevMode ? [undefined, { debugName: "classes" }] : /* istanbul ignore next */ []));
this.onChange = (_) => { };
this.onTouched = () => { };
this.disabled = false;
/**
* 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.
*/
this.stateChanges = new Subject();
this._focused = false;
this._required = false;
this._errorState = false;
/**
* Aria describedby ID provided by the user or the parent form field.
* Required for Angular Material form field control interface.
*/
this.userAriaDescribedBy = '';
this.valueChange = output();
this.action = output();
/** Emitted when validation fails (e.g. invalid typed date, date before min, after max). Message is translated. */
this.validationError = output();
this._validationErrorMessage = signal(null, ...(ngDevMode ? [{ debugName: "_validationErrorMessage" }] : /* istanbul ignore next */ []));
this._minDate = null;
this._maxDate = null;
this._ranges = null;
this.currentDate = new Date();
this.daysInMonth = [];
this.multiCalendarMonths = [];
/**
* 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.
*/
this.weekDays = [];
this.today = getStartOfDay(new Date());
this.selectedDate = null;
this.selectedDates = [];
this.startDate = null;
this.endDate = null;
this.hoveredDate = null;
this.rangesArray = [];
this.touchState = {
touchStartTime: 0,
touchStartElement: null,
dateCellTouchStartTime: 0,
dateCellTouchStartDate: null,
dateCellTouchStartX: 0,
dateCellTouchStartY: 0,
isDateCellTouching: false,
lastDateCellTouchDate: null,
dateCellTouchHandled: false,
calendarSwipeStartX: 0,
calendarSwipeStartY: 0,
calendarSwipeStartTime: 0,
isCalendarSwiping: false,
hoveredDate: null,
};
this.dateCellTouchHandledTime = 0;
this.touchHandledTimeout = null;
this.activeTimeouts = new Set();
this.activeAnimationFrames = new Set();
this.fieldSyncTimeoutId = null;
this.lazyLoadingObserver = null;
this.touchListenersSetup = new WeakMap();
this.touchListenersAttached = new WeakMap();
this.bottomSheetSwipeStartY = 0;
this.bottomSheetSwipeCurrentY = 0;
this.isBottomSheetSwiping = false;
this.bottomSheetSwipeThreshold = 100;
// Note: isCalendarSwiping is now in touchState
this.SWIPE_THRESHOLD = 50;
this.SWIPE_TIME_THRESHOLD = 300;
this._currentMonth = this.currentDate.getMonth();
this._currentYear = this.currentDate.getFullYear();
this._currentDecade = Math.floor(this.currentDate.getFullYear() / 10) * 10;
this.monthOptions = computed(() => {
const options = generateMonthOptions(this._localeSignal(), this._currentYearSignal());
const min = this._disabledStateSignal().minDate;
const max = this._disabledStateSignal().maxDate;
const currentYear = this._currentYearSignal();
return options.map((opt) => {
let disabled = false;
if (min) {
const minYear = min.getFullYear();
const minMonth = min.getMonth();
if (currentYear < minYear || (currentYear === minYear && opt.value < minMonth)) {
disabled = true;
}
}
if (max) {
const maxYear = max.getFullYear();
const maxMonth = max.getMonth();
if (currentYear > maxYear || (currentYear === maxYear && opt.value > maxMonth)) {
disabled = true;
}
}
return { ...opt, disabled };
});
}, ...(ngDevMode ? [{ debugName: "monthOptions" }] : /* istanbul ignore next */ []));
this.yearOptions = computed(() => {
const options = generateYearOptions(this._currentYearSignal(), this.yearRange);
const min = this._disabledStateSignal().minDate;
const max = this._disabledStateSignal().maxDate;
return options.map((opt) => {
let disabled = false;
if (min && opt.value < min.getFullYear()) {
disabled = true;
}
if (max && opt.value > max.getFullYear()) {
disabled = true;
}
return { ...opt, disabled };
});
}, ...(ngDevMode ? [{ debugName: "yearOptions" }] : /* istanbul ignore next */ []));
this.decadeOptions = [];
this.yearGrid = [];
this.hourOptions = [];
this.minuteOptions = [];
this.secondOptions = [];
this.decadeGrid = [];
this.firstDayOfWeek = 0;
this.currentHour = 0;
this.currentMinute = 0;
this.currentSecond = 0;
this.currentDisplayHour = 12;
this.isPm = false;
// Time range properties (for timeRangeMode)
this.startHour = 0;
this.startMinute = 0;
this.startSecond = 0;
this.startDisplayHour = 12;
this.startIsPm = false;
this.endHour = 0;
this.endMinute = 0;
this.endSecond = 0;
this.endDisplayHour = 12;
this.endIsPm = false;
this.ampmOptions = [
{ label: 'AM', value: false },
{ label: 'PM', value: true },
];
this.timelineMonths = [];
this.timelineStartDate = new Date();
this.timelineEndDate = new Date();
this.timelineZoomLevel = 1;
this.startTimeSlider = 0;
this.endTimeSlider = 1440;
this.elementRef = inject(ElementRef);
this.cdr = inject(ChangeDetectorRef);
this.platformId = inject(PLATFORM_ID);
this.globalConfig = inject(DATEPICKER_CONFIG, { optional: true });
this.fieldSyncService = inject(FieldSyncService);
this.localeRegistry = inject(LocaleRegistryService);
this.translationRegistry = inject(TranslationRegistryService);
this.focusTrapService = inject(FocusTrapService);
this.ariaLiveService = inject(AriaLiveService);
this.hapticFeedbackService = inject(HapticFeedbackService);
this.calendarGenerationService = inject(CalendarGenerationService);
this.parsingService = inject(DatepickerParsingService);
this.touchService = inject(TouchGestureHandlerService);
this.popoverPositioningService = inject(PopoverPositioningService);
this.naturalLanguageParserService = inject(NaturalLanguageParserService);
this.ngControl = inject(NgControl, {
optional: true,
self: true,
});
this.isBrowser = isPlatformBrowser(this.platformId);
this.dateComparator = createDateComparator();
// Signal-backed template state: setting these auto-notifies change detection,
// matching the `isCalendarOpen` pattern and reducing reliance on manual markForCheck().
this._naturalLanguagePreview = signal(null, ...(ngDevMode ? [{ debugName: "_naturalLanguagePreview" }] : /* istanbul ignore next */ []));
this._showNaturalLanguagePreview = signal(false, ...(ngDevMode ? [{ debugName: "_showNaturalLanguagePreview" }] : /* istanbul ignore next */ []));
// Signal-backed typed-input buffer. When the user is actively typing, this holds
// their in-progress text; otherwise the displayed value is derived live from
// `displayValue`. This mirrors the exact condition under which the old code synced
// the field (`!isTyping && allowTyping`), without writing state during render.
this._typedInputValue = signal('', ...(ngDevMode ? [{ debugName: "_typedInputValue" }] : /* istanbul ignore next */ []));
this.isTyping = false;
this.popoverId = 'ngxsmk-popover-' + Math.random().toString(36).substring(2, 9);
this.focusTrapCleanup = null;
this._translations = null;
this._translationService = null;
this._changeDetectionScheduled = false;
this._currentMonthSignal = signal(this.currentDate.getMonth(), ...(ngDevMode ? [{ debugName: "_currentMonthSignal" }] : /* istanbul ignore next */ []));
this._currentYearSignal = signal(this.currentDate.getFullYear(), ...(ngDevMode ? [{ debugName: "_currentYearSignal" }] : /* istanbul ignore next */ []));
this._localeSignal = signal('en-US', ...(ngDevMode ? [{ debugName: "_localeSignal" }] : /* istanbul ignore next */ []));
this._holidayProviderSignal = signal(null, ...(ngDevMode ? [{ debugName: "_holidayProviderSignal" }] : /* istanbul ignore next */ []));
this._disabledStateSignal = signal({
minDate: null,
maxDate: null,
disabledDates: null,
disabledRanges: null,
}, ...(ngDevMode ? [{ debugName: "_disabledStateSignal" }] : /* istanbul ignore next */ []));
/**
* Effect that automatically triggers change detection when key signals change.
* This reduces the need for manual markForCheck() calls throughout the codebase.
*/
this._changeDetectionEffect = effect(() => {
// Track signal dependencies to trigger change detection
this._isCalendarOpen();
this._currentMonthSignal();
this._currentYearSignal();
this._localeSignal();
this.monthOptions();
this.yearOptions();
// Schedule change detection once for all signal changes
this.scheduleChangeDetection();
}, ...(ngDevMode ? [{ debugName: "_changeDetectionEffect" }] : /* istanbul ignore next */ []));
/** Day-start timestamps returned by `asyncDateFilter` for the visible range. */
this._asyncDisabledTimestamps = signal(new Set(), ...(ngDevMode ? [{ debugName: "_asyncDisabledTimestamps" }] : /* istanbul ignore next */ []));
/** Monotonic id so late-arriving asyncDateFilter responses can be discarded. */
this._asyncFilterRequestId = 0;
this._asyncDateFilterEffect = effect(() => {
// Re-fetch whenever the visible month range changes
this._currentMonthSignal();
this._currentYearSignal();
this.refreshAsyncDisabledDates();
}, ...(ngDevMode ? [{ debugName: "_asyncDateFilterEffect" }] : /* istanbul ignore next */ []));
/**
* Signal tracking which calendar month indices are currently visible in the viewport.
* Used for lazy rendering of multi-calendar layouts to improve performance.
*/
this._visibleCalendarIndicesSignal = signal(new Set([0, 1, 2, 3, 4]) // Default: render first 5 months
, ...(ngDevMode ? [{ debugName: "_visibleCalendarIndicesSignal" }] : /* istanbul ignore next */ []));
/** Bumped when `multiCalendarMonths` is regenerated so `renderedCalendars` invalidates (plain array is not a signal). */
this._multiCalendarDataRevision = signal(0, ...(ngDevMode ? [{ debugName: "_multiCalendarDataRevision" }] : /* istanbul ignore next */ []));
this.calendarAriaLabel = computed(() => {
this._currentMonthSignal();
this._currentYearSignal();
this._localeSignal();
return this.getCalendarAriaLabel();
}, ...(ngDevMode ? [{ debugName: "calendarAriaLabel" }] : /* istanbul ignore next */ []));
this.calendarLoadingMessage = computed(() => {
this._localeSignal();
return this.getCalendarLoadingMessage();
}, ...(ngDevMode ? [{ debugName: "calendarLoadingMessage" }] : /* istanbul ignore next */ []));
/**
* Computed signal for rendered calendars - only includes visible calendars + buffer.
* This dramatically reduces DOM nodes for multi-calendar layouts.
*/
this.renderedCalendars = computed(() => {
// Track month/year signal dependencies to trigger re-computation when navigating months
// This ensures the computed re-evaluates when changeMonth() or dropdown selection changes the month/year
this._currentMonthSignal();
this._currentYearSignal();
this._multiCalendarDataRevision();
const visibleIndices = this._visibleCalendarIndicesSignal();
const allMonths = this.multiCalendarMonths;
const lazyBuffer = 2; // Render 2 calendars outside viewport
if (allMonths.length <= 1) {
// No lazy loading for single calendar
return allMonths;
}
if (visibleIndices.size === 0) {
return allMonths;
}
// Find min/max visible indices
const minVisible = Math.min(...visibleIndices);
const maxVisible = Math.max(...visibleIndices);
// Expand range with buffer
const renderStart = Math.max(0, minVisible - lazyBuffer);
const renderEnd = Math.min(allMonths.length - 1, maxVisible + lazyBuffer);
// Return calendars in the render range
return allMonths.slice(renderStart, renderEnd + 1);
}, ...(ngDevMode ? [{ debugName: "renderedCalendars" }] : /* istanbul ignore next */ []));
this._cachedIsCurrentMonthMemo = null;
this._cachedIsDateDisabledMemo = null;
this._cachedIsSameDayMemo = null;
this._cachedIsHolidayMemo = null;
this._cachedGetHolidayLabelMemo = null;
this._disabledDatesTimestamps = new Set();
this._parsedDisabledRanges = [];
this.passiveTouchListeners = [];
// Bind methods for child components to preserve 'this' context
this.boundIsDateDisabled = (d) => this.isDateDisabledMemo(d);
this.boundIsSameDay = (d1, d2) => this.isSameDayMemo(d1, d2);
this.boundIsHoliday = (d) => this.isHolidayMemo(d);
this.boundIsMultipleSelected = (d) => this.isMultipleSelected(d);
this.boundIsInRange = (d) => this.isInRange(d);
this.boundIsPreviewInRange = (d) => this.isPreviewInRange(d);
/**
* 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.
*/
this.comparisonRange = input(null, ...(ngDevMode ? [{ debugName: "comparisonRange" }] : /* istanbul ignore next */ []));
this.boundIsInComparisonRange = (d) => this.isInComparisonRange(d);
this.boundGetAriaLabel = (d) => this.getAriaLabel(d);
this.boundGetDayCellCustomClasses = (d) => this.getDayCellCustomClasses(d);
this.boundGetDayCellTooltip = (d) => this.getDayCellTooltip(d);
this.boundGetDayMetadata = (d) => this.getDayMetadata(d);
this.boundFormatDayNumber = (d) => this.formatDayNumber(d);
this.boundGetMonthYearLabel = (m, y) => this.getMonthYearLabel(m, y);
this.boundGetCalendarAriaLabelForMonth = (m, y) => this.getCalendarAriaLabelForMonth(m, y);
this.boundIsTimelineMonthSelected = (d) => this.isTimelineMonthSelected(d);
this.boundFormatTimeSliderValue = (v) => this.formatTimeSliderValue(v);
this.isYearDisabled = (year) => {
if (this._minDate && year < this._minDate.getFullYear()) {
return true;
}
if (this._maxDate && year > this._maxDate.getFullYear()) {
return true;
}
return false;
};
this.isDecadeDisabled = (decade) => {
if (this._minDate && decade + 9 < this._minDate.getFullYear()) {
return true;
}
if (this._maxDate && decade > this._maxDate.getFullYear()) {
return true;
}
return false;
};
this.boundIsYearDisabled = (year) => this.isYearDisabled(year);
this.boundIsDecadeDisabled = (decade) => this.isDecadeDisabled(decade);
this.isKeyboardHelpOpen = false;
this.focusedDate = null;
this.scrollDebounceTimer = null;
this.updatePositionOnScroll = () => {
if (this.isCalendarOpen && this._shouldAppendToBody && this.isBrowser) {
this.scrollDebounceTimer ??= this.trackedRequestAnimationFrame(() => {
this.positionPopoverRelativeToInput();
this.scrollDebounceTimer = null;
});
}
};
this._touchListenersSetupTimeout = null;
if (this.ngControl) {
this.ngControl.valueAccessor = this;
}
}
get typedInputValue() {
if (!this.isTyping && this.allowTyping) {
return this.displayValue;
}
return this._typedInputValue();
}
set typedInputValue(value) {
this._typedInputValue.set(value);
}
/**
* 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.
*/
scheduleChangeDetection() {
if (this._changeDetectionScheduled) {
return;
}
this._changeDetectionScheduled = true;
Promise.resolve().then(() => {
this._changeDetectionScheduled = false;
this.cdr.markForCheck();
});
}
/**
* 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
*/
trackedSetTimeout(callback, delay) {
const timeoutId = setTimeout(() => {
this.activeTimeouts.delete(timeoutId);
callback();
}, delay);
this.activeTimeouts.add(timeoutId);
return timeoutId;
}
/**
* 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
*/
trackedRequestAnimationFrame(callback) {
const frameId = requestAnimationFrame(() => {
this.activeAnimationFrames.delete(frameId);
callback();
});
this.activeAnimationFrames.add(frameId);
return frameId;
}
/**
* 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
*/
trackedDoubleRequestAnimationFrame(callback) {
this.trackedRequestAnimationFrame(() => {
this.trackedRequestAnimationFrame(callback);
});
}
/**
* Clears all active timeouts. Used when locale or weekStart changes
* to cancel any pending operations that might be invalidated by the change.
*/
clearActiveTimeouts() {
if (this.activeTimeouts && this.activeTimeouts.size > 0) {
this.activeTimeouts.forEach((timeoutId) => clearTimeout(timeoutId));
this.activeTimeouts.clear();
}
}
/**
* 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)
*/
debouncedFieldSync(delay = 100) {
if (this.fieldSyncTimeoutId) {
clearTimeout(this.fieldSyncTimeoutId);
this.activeTimeouts.delete(this.fieldSyncTimeoutId);
}
this.fieldSyncTimeoutId = this.trackedSetTimeout(() => {
this.fieldSyncTimeoutId = null;
if (this._field) {
this.syncFieldValue(this._field);
}
}, delay);
}
/**
* 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.
*/
refreshAsyncDisabledDates() {
if (!this.asyncDateFilter || !this.isBrowser)
return;
const month = this._currentMonthSignal();
const year = this._currentYearSignal();
const visibleStart = new Date(year, month, 1);
const visibleEnd = getEndOfMonth(new Date(year, month + Math.max(this.calendarCount - 1, 0), 1));
const requestId = ++this._asyncFilterRequestId;
this.asyncDateFilterLoading.emit(true);
Promise.resolve()
.then(() => this.asyncDateFilter(visibleStart, visibleEnd))
.then((dates) => {
if (requestId !== this._asyncFilterRequestId)
return; // superseded by newer navigation
const timestamps = new Set();
for (const raw of dates ?? []) {
const parsed = this._normalizeDate(raw);
if (parsed)
timestamps.add(getStartOfDay(parsed).getTime());
}
this._asyncDisabledTimestamps.set(timestamps);
this.scheduleChangeDetection();
})
.catch((error) => {
if (requestId !== this._asyncFilterRequestId)
return;
if (isDevMode()) {
console.warn('[ngxsmk-datepicker] asyncDateFilter rejected:', error);
}
this.asyncDateFilterError.emit(error);
})
.finally(() => {
if (requestId === this._asyncFilterRequestId) {
this.asyncDateFilterLoading.emit(false);
}
});
}
// Memoized dependencies for calendar generation with equality function for better performance
// Helper to get dependencies for memoization
_memoDependencies() {
return {
month: this._currentMonthSignal(),
year: this._currentYearSignal(),
firstDayOfWeek: this.firstDayOfWeek,
holidayProvider: this._holidayProviderSignal(),
disabledState: this._disabledStateSignal(),
};
}
_syncDisabledDatesCache() {
const set = new Set();
if (this._disabledDates && this._disabledDates.length > 0) {
for (const d of this._disabledDates) {
let parsed = null;
if (typeof d === 'string') {
parsed = this.parsingService ? this.parsingService.parseDateString(d) : normalizeDate(d);
}
else if (d instanceof Date) {
parsed = getStartOfDay(d);
}
if (parsed && !Number.isNaN(parsed.getTime())) {
set.add(getStartOfDay(parsed).getTime());
}
}
}
this._disabledDatesTimestamps = set;
const ranges = [];
if (this._disabledRanges && this._disabledRanges.length > 0) {
for (const r of this._disabledRanges) {
const s = typeof r.start === 'string'
? this.parsingService
? this.parsingService.parseDateString(r.start)
: normalizeDate(r.start)
: getStartOfDay(r.start);
const e = typeof r.end === 'string'
? this.parsingService
? this.parsingService.parseDateString(r.end)
: normalizeDate(r.end)
: getStartOfDay(r.end);
if (s && e && !Number.isNaN(s.getTime()) && !Number.isNaN(e.getTime())) {
ranges.push({
startTime: getStartOfDay(s).getTime(),
endTime: getEndOfDay(e).getTime(),
});
}
}
}
this._parsedDisabledRanges = ranges;
}
_updateMemoSignals() {
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this._holidayProviderSignal.set(this.holidayProvider || null);
this._disabledStateSignal.set({
minDate: this._minDate,
maxDate: this._maxDate,
disabledDates: this.disabledDates.length > 0 ? this.disabledDates : null,
disabledRanges: this.disabledRanges.length > 0 ? this.disabledRanges : null,
});
this._syncDisabledDatesCache();
}
get isInlineMode() {
if (this.inline === true || this.inline === 'always') {
return true;
}
if (this.inline === 'auto' && this.isBrowser) {
try {
const mediaQuery = globalThis.matchMedia('(min-width: 768px)');
return mediaQuery?.matches ?? false;
}
catch {
return false;
}
}
return false;
}
clearTouchHandledFlag() {
this.touchState.dateCellTouchHandled = false;
this.dateCellTouchHandledTime = 0;
this.touchState.isDateCellTouching = false;
if (this.touchHandledTimeout) {
clearTimeout(this.touchHandledTimeout);
this.touchHandledTimeout = null;
}
}
closeMonthYearDropdowns() {
this.datepickerContent?.closeAllSelects();
}
setTouchHandledFlag(duration = 300) {
this.touchState.dateCellTouchHandled = true;
this.dateCellTouchHandledTime = Date.now();
if (this.touchHandledTimeout) {
clearTimeout(this.touchHandledTimeout);
}
this.touchHandledTimeout = this.trackedSetTimeout(() => {
this.clearTouchHandledFlag();
}, duration);
}
isMobileDevice() {
if (!this.autoDetectMobile) {
return false;
}
if (this.isBrowser) {
try {
const mediaQuery = globalThis.matchMedia('(max-width: 1024px)');
const isMobileWidth = mediaQuery?.matches ?? false;
const hasTouchSupport = 'ontouchstart' in globalThis ||
('maxTouchPoints' in navigator && navigator.maxTouchPoints > 0);
const hasPointerEvents = 'onpointerdown' in globalThis;
const isMobileUserAgent = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
return (isMobileWidth ||
(hasTouchSupport && isMobileWidth) ||
(hasPointerEvents && isMobileWidth) ||
isMobileUserAgent);
}
catch {
return ('ontouchstart' in globalThis ||
('maxTouchPoints' in navigator && navigator.maxTouchPoints > 0));
}
}
return false;
}
shouldUseNativePicker() {
if (!this.useNativePicker || this.isInlineMode || this.mode === 'multiple') {
return false;
}
if (!this.isBrowser) {
return false;
}
const isMobile = this.isMobileDevice();
if (!isMobile) {
return false;
}
try {
const testInput = document.createElement('input');
if (this.showTime || this.timeOnly) {
testInput.type = 'datetime-local';
}
else {
testInput.type = 'date';
}
return testInput.type === (this.showTime || this.timeOnly ? 'datetime-local' : 'date');
}
catch {
return false;
}
}
getNativeInputType() {
if (this.showTime || this.timeOnly) {
return 'datetime-local';
}
return 'date';
}
formatValueForNativeInput(value) {
return this.parsingService.formatValueForNativeInput(value, this.mode, this.showTime, this.timeOnly);
}
formatDateForNativeInput(date) {
return this.parsingService.formatDateForNativeInput(date, this.showTime, this.timeOnly);
}
getMinDateForNativeInput() {
if (!this._minDate) {
return null;
}
return this.formatDateForNativeInput(this._minDate);
}
getMaxDateForNativeInput() {
if (!this._maxDate) {
return null;
}
return this.formatDateForNativeInput(this._maxDate);
}
parseNativeInputValue(value) {
return this.parsingService.parseNativeInputValue(value, this.mode);
}
onNativeInputChange(event) {
const input = event.target;
const value = this.parseNativeInputValue(input.value);
if (value === null) {
this.emitValue(null);
}
else {
this.emitValue(value);
}
}
onBottomSheetTouchStart(event) {
if (!this.isMobileDevice() || this.mobileModalStyle !== 'bottom-sheet' || this.isInlineMode) {
return;
}
const touch = event.touches[0];
if (touch) {
this.bottomSheetSwipeStartY = touch.clientY;
this.bottomSheetSwipeCurrentY = touch.clientY;
this.isBottomSheetSwiping = false;
}
}
onBottomSheetTouchMove(event) {
if (!this.isMobileDevice() ||
this.mobileModalStyle !== 'bottom-sheet' ||
this.isInlineMode ||
!this.isCalendarOpen) {
return;
}
const touch = event.touches[0];
if (touch && this.bottomSheetSwipeStartY > 0) {
this.bottomSheetSwipeCurrentY = touch.clientY;
const deltaY = this.bottomSheetSwipeCurrentY - this.bottomSheetSwipeStartY;
if (deltaY > 10 && !this.isBottomSheetSwiping) {
this.isBottomSheetSwiping = true;
}
if (this.isBottomSheetSwiping && deltaY > 0) {
const popoverContainer = this.getActualPopoverContainer();
if (popoverContainer) {
popoverContainer.style.transform = `translateY(${deltaY}px)`;
const opacity = Math.max(0, 1 - deltaY / 300);
popoverContainer.style.opacity = String(opacity);
}
}
}
}
onBottomSheetTouchEnd(event) {
if (!this.isMobileDevice() ||
this.mobileModalStyle !== 'bottom-sheet' ||
this.isInlineMode ||
!this.isCalendarOpen) {
return;
}
const touch = event.changedTouches[0];
if (touch && this.isBottomSheetSwiping) {
const deltaY = this.bottomSheetSwipeCurrentY - this.bottomSheetSwipeStartY;
if (deltaY > this.bottomSheetSwipeThreshold) {
this.closeCalendarWithFocusRestore();
}
const popoverContainer = this.getActualPopoverContainer();
if (popoverContainer) {
popoverContainer.style.transform = '';
popoverContainer.style.opacity = '';
}
this.bottomSheetSwipeStartY = 0;
this.bottomSheetSwipeCurrentY = 0;
this.isBottomSheetSwiping = false;
}
}
get isCalendarVisible() {
return this.isInlineMode || this.isCalendarOpen;
}
// Pure getter: no side-effects. The typed-input mirroring is handled by the
// `typedInputValue` getter deriving from this value (see below), so this can be
// safely read during template rendering.
get displayValue() {
if (this.hooks?.formatDisplayValue) {
return this.hooks.formatDisplayValue(this._value, this.mode) ?? '';
}
if (this._dateFormatPattern && this.customDateFormatService) {
return this.formatWithCustomPattern();
}
if (this.displayFormat) {
return this.formatWithCustomFormat();
}
if (this.timeOnly) {
return this.getDisplayValueTimeOnly();
}
return this.getDisplayValueDateDefault();
}
getDisplayValueTimeOnly() {
const timeOpts = {
hour: '2-digit',
minute: '2-digit',
};
if (this.mode === 'single' && this.selectedDate) {
return formatDateWithTimezone(this.selectedDate, this.locale, timeOpts, this.timezone);
}
if (this.mode === 'range' && this.startDate) {
const start = formatDateWithTimezone(this.startDate, this.locale, timeOpts, this.timezone);
if (this.endDate) {
const end = formatDateWithTimezone(this.endDate, this.locale, timeOpts, this.timezone);
return `${start} - ${end}`;
}
return `${start}...`;
}
if (this.mode === 'multiple' && this.selectedDates.length > 0) {
return this.getTranslation('timesSelected', undefined, {
count: this.selectedDates.length,
});
}
return '';
}
getDisplayValueDateDefault() {
const options = {
year: 'numeric',
month: 'short',
day: '2-digit',
};
if (this.showTime) {
options.hour = '2-digit';
options.minute = '2-digit';
}
if (this.mode === 'single' && this.selectedDate) {
return formatDateWithTimezone(this.selectedDate, this.locale, options, this.timezone);
}
if (this.mode === 'range' && this.startDate) {
const start = formatDateWithTimezone(this.startDate, this.locale, options, this.timezone);
if (this.endDate) {
const end = formatDateWithTimezone(this.endDate, this.locale, options, this.timezone);
return `${start} - ${end}`;
}
return `${start}...`;
}
if (this.mode === 'multiple' && this.selectedDates.length > 0) {
return this.getTranslation('datesSelected', undefined, {
count: this.selectedDates.length,
});
}
return '';
}
formatWithCustomFormat() {
if (!this.displayFormat)
return '';
const fromAdapter = this.formatWithAdapter();
if (fromAdapter !== null)
return fromAdapter;
return this.formatWithParsingServiceFallback();
}
formatWithAdapter() {
const adapter = this.globalConfig?.dateAdapter;
if (!adapter || typeof adapter.format !== 'function')
return null;
const fmt = this.displayFormat;
const loc = this.locale;
if (this.mode === 'single' && this.selectedDate) {
return adapter.format(this.selectedDate, fmt, loc);
}
if (this.mode === 'range' && this.startDate) {
const start = adapter.format(this.startDate, fmt, loc);
if (this.endDate) {
return `${start} - ${adapter.format(this.endDate, fmt, loc)}`;
}
return `${start}...`;
}
if (this.mode === 'multiple' && this.selectedDates.length > 0) {
return this.getTranslation('datesSelected', undefined, {
count: this.selectedDates.length,
});
}
return null;
}
formatWithParsingServiceFallback() {
const fmt = this.displayFormat ?? 'MM/DD/YYYY';
const formatOne = (d) => this.parsingService.formatDateWithPattern(d, fmt);
if (this.mode === 'single' && this.selectedDate) {
return formatOne(this.selectedDate);
}
if (this.mode === 'range' && this.startDate) {
if (this.endDate) {
return `${formatOne(this.startDate)} - ${formatOne(this.endDate)}`;
}
return `${formatOne(this.startDate)}...`;
}
if (this.mode === 'multiple' && this.selectedDates.length > 0) {
return this.getTranslation('datesSelected', undefined, {
count: this.selectedDates.length,
});
}
return '';
}
/**
* Format dates using a custom date format pattern
* Supports YYYY, MM, DD, HH, mm, ss, etc.
*/
formatWithCustomPattern() {
if (!this._dateFormatPattern || !this.customDateFormatService)
return '';
try {
if (this.mode === 'single' && this.selectedDate) {
return this.customDateFormatService.format(this.selectedDate, this._dateFormatPattern);
}
else if (this.mode === 'range' && this.startDate) {
if (this.endDate) {
const start = this.customDateFormatService.format(this.startDate, this._dateFormatPattern);
const end = this.customDateFormatService.format(this.endDate, this._dateFormatPattern);
return `${start} - ${end}`;
}
else {
return this.customDateFormatService.format(this.startDate, this._dateFormatPattern) + '...';
}
}
else if (this.mode === 'multiple' && this.selectedDates.length > 0) {
return this.getTranslation('datesSelected', undefined, {
count: this.selectedDates.length,
});
}
else if (this.mode === 'timeRange') {
// For time range mode, show formatted start and end times
const today = this.today;
const startDate = new Date(today);
startDate.setHours(this.get24Hour(this.startDisplayHour, this.startIsPm), this.startMinute, this.startSecond, 0);
const endDate = new Date(today);
endDate.setHours(this.get24Hour(this.endDisplayHour, this.endIsPm), this.endMinute, this.endSecond, 0);
const start = this.customDateFormatService.format(startDate, this._dateFormatPattern);
const end = this.customDateFormatService.format(endDate, this._dateFormatPattern);
return `${start} - ${end}`;
}
}
catch {
// Fallback to empty string if formatting fails
return '';
}
return '';
}
get isBackArrowDisabled() {
if (!this._minDate)
return false;
const firstDayOfCurrentMonth = new Date(this.currentYear, this.currentMonth, 1);
return firstDayOfCurrentMonth <= this._minDate;
}
adjustDisplayedDateToRange() {
let yearChanged = false;
let monthChanged = false;
if (this._minDate) {
const minYear = this._minDate.getFullYear();
const minMonth = this._minDate.getMonth();
if (this._currentYear < minYear) {
this._currentYear = minYear;
this._currentYearSignal.set(minYear);
yearChanged = true;
}
if (this._currentYear === minYear && this._currentMonth < minMonth) {
this._currentMonth = minMonth;
this._currentMonthSignal.set(minMonth);
monthChanged = true;
}
}
if (this._maxDate) {
const maxYear = this._maxDate.getFullYear();
const maxMonth = this._maxDate.getMonth();
if (this._currentYear > maxYear) {
this._currentYear = maxYear;
this._currentYearSignal.set(maxYear);
yearChanged = true;
}
if (this._currentYear === maxYear && this._currentMonth > maxMonth) {
this._currentMonth = maxMonth;
this._currentMonthSignal.set(maxMonth);
monthChanged = true;
}
}
if (yearChanged || monthChanged) {
this.currentDate = new Date(this._currentYear, this._currentMonth, 1);
}
}
_invalidateMemoCache() {
this._memoDependencies();
this._cachedIsCurrentMonthMemo = null;
this._cachedIsDateDisabledMemo = null;
this._cachedIsSameDayMemo = null;
this._cachedIsHolidayMemo = null;
this._cachedGetHolidayLabelMemo = null;
}
get isCurrentMonthMemo() {
const deps = this._memoDependencies();
if (this._cachedIsCurrentMonthMemo) {
const currentMonth = this._currentMonth;
const currentYear = this._currentYear;
if (currentMonth === deps.month && currentYear === deps.year) {
return this._cachedIsCurrentMonthMemo;
}
}
const month = deps.month;
const year = deps.year;
this._cachedIsCurrentMonthMemo = (day) => {
if (!day)
return false;
return day.getMonth() === month && day.getFullYear() === year;
};
return this._cachedIsCurrentMonthMemo;
}
/**
* 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() {
const deps = this._memoDependencies();
const disabledState = deps.disabledState;
const currentDisabledState = {
minDate: this._minDate,
maxDate: this._maxDate,
disabledDates: this.disabledDates.length > 0 ? this.disabledDates : null,
disabledRanges: this.disabledRanges.length > 0 ? this.disabledRanges : null,
};
const currentMonth = this._currentMonthSignal();
const currentYear = this._currentYearSignal();
const stateChanged = disabledState.minDate !== currentDisabledState.minDate ||
disabledState.maxDate !== currentDisabledState.maxDate ||
disabledState.disabledDates !== currentDisabledState.disabledDates ||
disabledState.disabledRanges !== currentDisabledState.disabledRanges ||
deps.month !== currentMonth ||
deps.year !== currentYear;
if (this._cachedIsDateDisabledMemo && !stateChanged) {
return this._cachedIsDateDisabledMemo;
}
if (stateChanged) {
this.trackedSetTimeout(() => {
this._disabledStateSignal.set(currentDisabledState);
}, 0);
}
this._cachedIsDateDisabledMemo = (day) => {
if (!day)
return false;
return this.isDateDisabled(day);
};
return this._cachedIsDateDisabledMemo;
}
/**
* 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() {
if (this._cachedIsSameDayMemo) {
return this._cachedIsSameDayMemo;
}
this._cachedIsSameDayMemo = (d1, d2) => this.dateComparator(d1, d2);
return this._cachedIsSameDayMemo;
}
/**
* 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() {
const deps = this._memoDependencies();
const holidayProvider = deps.holidayProvider;
const currentProvider = this.holidayProvider || null;
if (this._cachedIsHolidayMemo && holidayProvider === currentProvider) {
return this._cachedIsHolidayMemo;
}
if (holidayProvider !== currentProvider) {
this._holidayProviderSignal.set(currentProvider);
}
const provider = currentProvider;
this._cachedIsHolidayMemo = (day) => {
if (!day || !provider)
return false;
const dateOnly = getStartOfDay(day);
return provider.isHoliday(dateOnly);
};
return this._cachedIsHolidayMemo;
}
get getHolidayLabelMemo() {
const deps = this._memoDependencies();
const holidayProvider = deps.holidayProvider;
const currentProvider = this.holidayProvider || null;
if (this._cachedGetHolidayLabelMemo && holidayProvider === currentProvider) {
return this._cachedGetHolidayLabelMemo;
}
if (holidayProvider !== currentProvider) {
this._holidayProviderSignal.set(currentProvider);
}
const provider = currentProvider;
const isHolidayFn = this.isHolidayMemo;
this._cachedGetHolidayLabelMemo = (day) => {
if (!day || !provider || !isHolidayFn(day))
return null;
return provider.getHolidayLabel ? provider.getHolidayLabel(getStartOfDay(day)) : 'Holiday';
};
return this._cachedGetHolidayLabelMemo;
}
/**
* 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, day) {
return day ? day.getTime().toString() : `empty-${index}`;
}
/**
* 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
*/
containsNodeViaComposedPath(path) {
const nativeElement = this.elementRef?.nativeElement;
if (nativeElement && path.includes(nativeElement)) {
return true;
}
// Check portaled popover content if it exists
if (this._shouldAppendToBody && this.portalViewRef) {
if (this.portalViewRef.rootNodes.some((node) => path.includes(node))) {
return true;
}
}
// Check inline popover Container (secondary fallback)
if (this.popoverContainer?.nativeElement) {
if (path.includes(this.popoverContainer.nativeElement)) {
return true;
}
}
return false;
}
containsNodeViaDOM(target) {
const nativeElement = this.elementRef?.nativeElement;
if (nativeElement && (nativeElement === target || nativeElement.contains(target))) {
return true;
}
// Check portaled popover content if it exists
if (this._shouldAppendToBody && this.portalViewRef) {
return this.portalViewRef.rootNodes.some((node) => node === target || (node instanceof HTMLElement && node.contains(target)));
}
// Check inline popover Container (secondary fallback)
if (this.popoverContainer?.nativeElement) {
const popover = this.popoverContainer.nativeElement;
if (popover === target || popover.contains(target)) {
return true;
}
}
return false;
}
/**
* 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, event) {
if (!this.isBrowser || !target) {
return false;
}
// Support shadow DOM by checking composedPath if event is provided
if (event && typeof event.composedPath === 'function') {
return this.containsNodeViaComposedPath(event.composedPath());
}
return this.containsNodeViaDOM(target);
}
/** Shared logic for closing calendar when user interacts outside (click or touch). */
tryCloseCalendarOnOutsideInteraction(target, event) {
if (this.containsNode(target, event))
return;
const isInsideOtherDatepicker = Array.from(NgxsmkDatepickerComponent._allInstances).some((instance) => {
if (instance === this || instance.isInlineMode)
return false;
return instance.containsNode(target, event);
});
if (isInsideOtherDatepicker || !this.isCalendarOpen)
return;
const now = Date.now();
const timeSinceToggle = this.lastToggleTime > 0 ? now - this.lastToggleTime : Infinity;
const protectionTime = this.isMobileDevice() ? 1000 : 300;
if (this._isCalendarOpening() || timeSinceToggle < protectionTime)
return;
this.isCalendarOpen = false;
this._isCalendarOpening.set(false);
}
onDocumentClick(event) {
this.handleDocumentOutsideInteraction(event);
}
onDocumentTouchStart(event) {
this.handleDocumentOutsideInteraction(event);
}
handleDocumentOutsideInteraction(event) {
if (!this.isBrowser || this.isInlineMode)
return;
const target = event.target;
if (!target)
return;
this.tryCloseCalendarOnOutsideInteraction(target, event);
}
onTouchStart(event) {
if (this.disabled || this.isInlineMode) {
return;
}
this.touchStartTime = Date.now();
this.touchStartElement = event.currentTarget;
}
onInputGroupFocus() {
if (!this._focused) {
this._focused = true;
this.stateChanges.next();
}
if (this._field && !this.disabled) {
this.syncFieldValue(this._field);
}
}
focusInput() {
if (this.datepickerInput) {
this.datepickerInput.focus();
}
else if (this.elementRef?.nativeElement) {
const inputGroup = this.elementRef?.nativeElement?.querySelector('.ngxsmk-input-group');
if (inputGroup) {
inputGroup.focus();
}
}
}
onTouchEnd(event) {
if (this.disabled || this.isInlineMode) {
this.touchStartTime = 0;
this.touchStartElement = null;
return;
}
const now = Date.now();
const timeSinceTouch = this.touchStartTime > 0 ? now - this.touchStartTime : 0;
const touch = event.changedTouches[0];
const isSameElement = touch &&
this.touchStartElement &&
(touch.target === this.touchStartElement || this.touchStartElement.contains?.(touch.target));
if (this.touchStartTime === 0 || timeSinceTouch > 800 || !isSameElement) {
this.touchStartTime = 0;
this.touchStartElement = null;
return;
}
const timeSinceToggle = this.lastToggleTime > 0 ? now - this.lastToggleTime : Infinity;
if (timeSinceToggle < 300) {
this.touchStartTime = 0;
this.touchStartElement = null;
return;
}
if (this._field) {
this.syncFieldValue(this._field);
}
event.preventDefault();
event.stopPropagation();
const wasOpen = this.isCalendarOpen;
if (wasOpen) {
this.lastToggleTime = now;
this.touchStartTime = 0;
this.touchStartElement = null;
this.applyCalendarCloseState();
}
else {
this.closeOtherCalendarInstances();
this.applyCalendarOpenStateFromTouch(now);
}
}
closeOtherCalendarInstances() {
NgxsmkDatepickerComponent._allInstances.forEach((instance) => {
if (instance !== this && instance.isCalendarOpen && !instance.isInlineMode) {
instance.isCalendarOpen = false;
instance._isCalendarOpening.set(false);
instance._startClosingState();
instance.cdr.markForCheck();
}
});
}
applyCalendarOpenStateFromTouch(now) {
this._isCalendarOpening.set(true);
this.isCalendarOpen = true;
this.lastToggleTime = now;
this.closeMonthYearDropdowns();
this.trackedSetTimeout(() => {
this.touchStartTime = 0;
this.touchStartElement = null;
}, 500);
if (this.defaultMonthOffset !== 0 && !this._value && !this._startAtDate) {
const nextMonth = new Date();
nextMonth.setMonth(nextMonth.getMonth() + this.defaultMonthOffset);
nextMonth.setDate(1);
this.currentDate = nextMonth;
this._currentMonth = nextMonth.getMonth();
this._currentYear = nextMonth.getFullYear();
this._currentDecade = Math.floor(this._currentYear / 10) * 10;
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this._invalidateMemoCache();
}
if (this.openCalendarTimeoutId) {
clearTimeout(this.openCalendarTimeoutId);
}
this.generateCalendar();
this._startOpeningState();
if (this.isBrowser) {
this.trackedDoubleRequestAnimationFrame(() => {
this.setupPassiveTouchListeners();
this.scheduleChangeDetection();
const timeoutDelay = this.isMobileDevice() ? 150 : 60;
if (this._isCalendarOpening()) {
this.trackedSetTimeout(() => {
this._isCalendarOpening.set(false);
this.setupPassiveTouchListeners();
this.scheduleChangeDetection();
}, timeoutDelay);
}
});
}
}
onPointerDown(event) {
if (this.disabled || this.isInlineMode || event.pointerType === 'mouse') {
return;
}
const target = event.target;
if (target?.closest('.ngxsmk-clear-button')) {
return;
}
this.isPointerEvent = true;
this.pointerDownTime = Date.now();
this.touchStartTime = Date.now();
this.touchStartElement = event.currentTarget;
}
onPointerUp(event) {
if (this.disabled || this.isInlineMode || !this.isPointerEvent || event.pointerType === 'mouse') {
this.isPointerEvent = false;
return;
}
const target = event.target;
if (target?.closest('.ngxsmk-clear-button')) {
this.clearPointerTouchState();
return;
}
const now = Date.now();
const timeSincePointerDown = this.pointerDownTime > 0 ? now - this.pointerDownTime : 0;
if (this.pointerDownTime === 0 || timeSincePointerDown > 600) {
this.clearPointerTouchState();
return;
}
event.preventDefault();
event.stopPropagation();
this.clearPointerTouchState();
const wasOpen = this.isCalendarOpen;
if (wasOpen) {
this.applyCalendarCloseState();
}
else {
this.applyCalendarOpenStateFromPointer(now);
}
}
clearPointerTouchState() {
this.isPointerEvent = false;
this.pointerDownTime = 0;
this.touchStartTime = 0;
this.touchStartElement = null;
}
applyCalendarCloseState() {
this.isCalendarOpen = false;
this._isCalendarOpening.set(false);
if (this.openCalendarTimeoutId) {
clearTimeout(this.openCalendarTimeoutId);
this.openCalendarTimeoutId = null;
}
this._startClosingState();
}
applyCalendarOpenStateFromPointer(now) {
this._isCalendarOpening.set(true);
this.isCalendarOpen = true;
this.lastToggleTime = now;
this.closeMonthYearDropdowns();
if (this.openCalendarTimeoutId) {
clearTimeout(this.openCalendarTimeoutId);
}
this._startOpeningState();
if (this.isBrowser) {
this.trackedDoubleRequestAnimationFrame(() => {
this.setupPassiveTouchListeners();
this.scheduleChangeDetection();
});
const timeoutDelay = this.isMobileDevice() ? 150 : 60;
this.openCalendarTimeoutId = this.trackedSetTimeout(() => {
this._isCalendarOpening.set(false);
this.setupPassiveTouchListeners();
this.openCalendarTimeoutId = null;
if (this._shouldAppendToBody && this.isBrowser) {
this.positionPopoverRelativeToInput();
this.revealBodyPopover();
}
this.cdr.markForCheck();
}, timeoutDelay);
}
}
onKeyDown(event) {
if (!this.isCalendarVisible || this.disabled || event.defaultPrevented)
return;
const target = event.target;
const isInsideCalendar = this.isElementInsideCalendar(target);
if (isInsideCalendar || event.key === 'Escape') {
const handled = this.handleKeyboardNavigation(event);
if (handled) {
event.preventDefault();
event.stopPropagation();
}
}
}
isElementInsideCalendar(target) {
if (!target)
return true;
const isInputOrControl = (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.tagName === 'SELECT') &&
!target.classList.contains('ngxsmk-day-cell');
if (isInputOrControl)
return false;
return (target.closest('.ngxsmk-days-grid') !== null ||
target.closest('.ngxsmk-popover-container') !== null ||
target.closest('.ngxsmk-calendar-container') !== null ||
target.closest('.ngxsmk-datepicker-container') !== null ||
target.classList.contains('ngxsmk-day-cell') ||
target.closest('.ngxsmk-datepicker-wrapper') !== null);
}
handleKeyboardNavigation(event) {
if (!this.enableKeyboardShortcuts)
return false;
const context = {
currentDate: this.currentDate,
selectedDate: this.selectedDate,
startDate: this.startDate,
endDate: this.endDate,
selectedDates: this.selectedDates,
mode: this.mode,
focusedDate: this.focusedDate,
isCalendarOpen: this.isCalendarOpen,
};
if (this.tryCustomShortcuts(event, context))
return true;
if (this.tryHooksHandleShortcut(event, context))
return true;
return this.handleShortcutKey(event);
}
tryCustomShortcuts(event, context) {
if (!this.customShortcuts)
return false;
const key = this.getShortcutKey(event);
if (!key || !this.customShortcuts[key])
return false;
const handled = this.customShortcuts[key](context);
if (handled) {
event.preventDefault();
event.stopPropagation();
}
return handled;
}
tryHooksHandleShortcut(event, context) {
if (!this.hooks?.handleShortcut)
return false;
const handled = this.hooks.handleShortcut(event, context);
if (handled) {
event.preventDefault();
event.stopPropagation();
}
return handled;
}
handleShortcutKey(event) {
const key = event.key;
const nav = this.handleShortcutNavigationKeys(key, event);
if (nav !== null)
return nav;
return this.handleShortcutLetterAndSpecialKeys(key, event);
}
handleShortcutNavigationKeys(key, event) {
const arrow = this.handleShortcutArrowKeys(key);
if (arrow !== null)
return arrow;
const page = this.handleShortcutPageHomeEndKeys(key, event);
if (page !== null)
return page;
if (key === 'Enter' || key === ' ') {
if (this.focusedDate)
this.onDateClick(this.focusedDate);
return true;
}
if (key === 'Escape') {
if (!this.isInlineMode)
this.closeCalendarWithFocusRestore();
return true;
}
return null;
}
handleShortcutArrowKeys(key) {
if (key === 'ArrowLeft') {
this.navigateDate(this.isRtl ? 1 : -1, 0);
return true;
}
if (key === 'ArrowRight') {
this.navigateDate(this.isRtl ? -1 : 1, 0);
return true;
}
if (key === 'ArrowUp') {
this.navigateDate(0, -1);
return true;
}
if (key === 'ArrowDown') {
this.navigateDate(0, 1);
return true;
}
return null;
}
handleShortcutPageHomeEndKeys(key, event) {
if (key === 'PageUp') {
if (event.shiftKey)
this.navigateYear(-1);
else
this.navigateMonth(-1);
return true;
}
if (key === 'PageDown') {
if (event.shiftKey)
this.navigateYear(1);
else
this.navigateMonth(1);
return true;
}
if (key === 'Home') {
this.navigateToFirstDay();
return true;
}
if (key === 'End') {
this.navigateToLastDay();
return true;
}
return null;
}
handleShortcutLetterAndSpecialKeys(key, event) {
const noMod = !event.ctrlKey && !event.metaKey;
if ((key === 't' || key === 'T') && noMod) {
this.selectToday();
return true;
}
if ((key === 'y' || key === 'Y') && noMod) {
this.selectYesterday();
return true;
}
if ((key === 'n' || key === 'N') && noMod) {
this.selectTomorrow();
return true;
}
if ((key === 'w' || key === 'W') && noMod) {
this.selectNextWeek();
return true;
}
if (key === '?' && event.shiftKey) {
this.toggleKeyboardHelp();
return true;
}
if (key === '/' && noMod && this.enableAi) {
this.datepickerContent?.focusAiInput();
return true;
}
return false;
}
toggleKeyboardHelp() {
this.isKeyboardHelpOpen = !this.isKeyboardHelpOpen;
this.scheduleChangeDetection();
}
getShortcutKey(event) {
const parts = [];
if (event.ctrlKey)
parts.push('Ctrl');
if (event.metaKey)
parts.push('Meta');
if (event.shiftKey)
parts.push('Shift');
if (event.altKey)
parts.push('Alt');
parts.push(event.key);
return parts.length > 1 ? parts.join('+') : event.key;
}
focusDateCell(date) {
this.focusedDate = date;
this.scheduleChangeDetection();
if (!this.isBrowser)
return;
this.trackedRequestAnimationFrame(() => {
const popover = this.getActualPopoverContainer() || this.elementRef?.nativeElement;
if (!popover)
return;
const dateTimestamp = date.getTime();
let cell = popover.querySelector(`.ngxsmk-day-cell[data-date="${dateTimestamp}"]`);
if (!cell) {
const cells = Array.from(popover.querySelectorAll('.ngxsmk-day-cell'));
for (const c of cells) {
const attr = c.getAttribute('data-date');
if (attr) {
const cellDate = new Date(Number(attr));
if (cellDate.getFullYear() === date.getFullYear() &&
cellDate.getMonth() === date.getMonth() &&
cellDate.getDate() === date.getDate()) {
cell = c;
break;
}
}
}
}
if (!cell) {
cell = popover.querySelector('.ngxsmk-day-cell.focused');
}
if (!cell) {
cell = popover.querySelector('.ngxsmk-day-cell:not(.empty):not(.disabled):not(.ngxsmk-other-month)');
}
if (cell && typeof cell.focus === 'function') {
try {
cell.focus();
}
catch {
// Ignore focus errors
}
}
});
}
findFirstValidDateInMonth(year, month) {
const daysInMonth = new Date(year, month + 1, 0).getDate();
for (let day = 1; day <= daysInMonth; day++) {
const candidate = new Date(year, month, day);
if (this.isDateValid(candidate)) {
return candidate;
}
}
return null;
}
findLastValidDateInMonth(year, month) {
const daysInMonth = new Date(year, month + 1, 0).getDate();
for (let day = daysInMonth; day >= 1; day--) {
const candidate = new Date(year, month, day);
if (this.isDateValid(candidate)) {
return candidate;
}
}
return null;
}
navigateMonth(delta) {
if (delta < 0 && this.isBackArrowDisabled)
return;
const baseDate = this.focusedDate || this.selectedDate || this.currentDate || new Date();
const targetMonthDate = addMonths(new Date(baseDate.getFullYear(), baseDate.getMonth(), 1), delta);
const targetYear = targetMonthDate.getFullYear();
const targetMonth = targetMonthDate.getMonth();
const lastDayOfTargetMonth = new Date(targetYear, targetMonth + 1, 0).getDate();
const clampedDay = Math.min(baseDate.getDate(), lastDayOfTargetMonth);
let targetDate = new Date(targetYear, targetMonth, clampedDay);
if (!this.isDateValid(targetDate)) {
targetDate = this.findFirstValidDateInMonth(targetYear, targetMonth) ?? targetDate;
}
this.focusedDate = targetDate;
this.currentDate = new Date(targetYear, targetMonth, 1);
this._currentMonth = targetMonth;
this._currentYear = targetYear;
this._currentDecade = Math.floor(targetYear / 10) * 10;
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this._invalidateMemoCache();
this.generateCalendar();
this.focusDateCell(targetDate);
}
navigateYear(delta) {
const baseDate = this.focusedDate || this.selectedDate || this.currentDate || new Date();
const targetYear = baseDate.getFullYear() + delta;
const targetMonth = baseDate.getMonth();
const lastDayOfTargetMonth = new Date(targetYear, targetMonth + 1, 0).getDate();
const clampedDay = Math.min(baseDate.getDate(), lastDayOfTargetMonth);
let targetDate = new Date(targetYear, targetMonth, clampedDay);
if (!this.isDateValid(targetDate)) {
targetDate = this.findFirstValidDateInMonth(targetYear, targetMonth) ?? targetDate;
}
this.focusedDate = targetDate;
this.currentDate = new Date(targetYear, targetMonth, 1);
this._currentMonth = targetMonth;
this._currentYear = targetYear;
this._currentDecade = Math.floor(targetYear / 10) * 10;
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this._invalidateMemoCache();
this.generateCalendar();
this.focusDateCell(targetDate);
}
navigateDate(days, weeks) {
const baseDate = this.focusedDate || this.selectedDate || this.currentDate || new Date();
const newDate = new Date(baseDate);
newDate.setDate(newDate.getDate() + days + weeks * 7);
if (this.isDateValid(newDate)) {
this.focusedDate = newDate;
const monthChanged = this._currentMonth !== newDate.getMonth() || this._currentYear !== newDate.getFullYear();
if (monthChanged) {
this.currentDate = new Date(newDate.getFullYear(), newDate.getMonth(), 1);
this._currentMonth = newDate.getMonth();
this._currentYear = newDate.getFullYear();
this._currentDecade = Math.floor(this._currentYear / 10) * 10;
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this._invalidateMemoCache();
}
this.generateCalendar();
this.focusDateCell(newDate);
}
}
navigateToFirstDay() {
const year = this.currentDate.getFullYear();
const month = this.currentDate.getMonth();
let firstDay = new Date(year, month, 1);
if (!this.isDateValid(firstDay)) {
firstDay = this.findFirstValidDateInMonth(year, month) ?? firstDay;
}
if (this.isDateValid(firstDay)) {
this.focusedDate = firstDay;
this.generateCalendar();
this.focusDateCell(firstDay);
}
}
navigateToLastDay() {
const year = this.currentDate.getFullYear();
const month = this.currentDate.getMonth();
let lastDay = new Date(year, month + 1, 0);
if (!this.isDateValid(lastDay)) {
lastDay = this.findLastValidDateInMonth(year, month) ?? lastDay;
}
if (this.isDateValid(lastDay)) {
this.focusedDate = lastDay;
this.generateCalendar();
this.focusDateCell(lastDay);
}
}
selectToday() {
const today = this.today;
if (this.isDateValid(today)) {
this.focusedDate = today;
this.currentDate = new Date(today.getFullYear(), today.getMonth(), 1);
this._currentMonth = today.getMonth();
this._currentYear = today.getFullYear();
this._currentDecade = Math.floor(this._currentYear / 10) * 10;
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this._invalidateMemoCache();
this.generateCalendar();
this.onDateClick(today);
this.focusDateCell(today);
}
}
selectYesterday() {
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
yesterday.setHours(0, 0, 0, 0);
if (this.isDateValid(yesterday)) {
this.focusedDate = yesterday;
this.currentDate = new Date(yesterday.getFullYear(), yesterday.getMonth(), 1);
this._currentMonth = yesterday.getMonth();
this._currentYear = yesterday.getFullYear();
this._currentDecade = Math.floor(this._currentYear / 10) * 10;
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this._invalidateMemoCache();
this.generateCalendar();
this.onDateClick(yesterday);
this.focusDateCell(yesterday);
}
}
selectTomorrow() {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(0, 0, 0, 0);
if (this.isDateValid(tomorrow)) {
this.focusedDate = tomorrow;
this.currentDate = new Date(tomorrow.getFullYear(), tomorrow.getMonth(), 1);
this._currentMonth = tomorrow.getMonth();
this._currentYear = tomorrow.getFullYear();
this._currentDecade = Math.floor(this._currentYear / 10) * 10;
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this._invalidateMemoCache();
this.generateCalendar();
this.onDateClick(tomorrow);
this.focusDateCell(tomorrow);
}
}
selectNextWeek() {
const nextWeek = new Date();
nextWeek.setDate(nextWeek.getDate() + 7);
nextWeek.setHours(0, 0, 0, 0);
if (this.isDateValid(nextWeek)) {
this.focusedDate = nextWeek;
this.currentDate = new Date(nextWeek.getFullYear(), nextWeek.getMonth(), 1);
this._currentMonth = nextWeek.getMonth();
this._currentYear = nextWeek.getFullYear();
this._currentDecade = Math.floor(this._currentYear / 10) * 10;
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this._invalidateMemoCache();
this.generateCalendar();
this.onDateClick(nextWeek);
this.focusDateCell(nextWeek);
}
}
onDateFocus(day) {
if (day) {
this.focusedDate = day;
}
}
isDateValid(date) {
if (this._minDate && date.getTime() < getStartOfDay(this._minDate).getTime())
return false;
if (this._maxDate && date.getTime() > getEndOfDay(this._maxDate).getTime())
return false;
if (this.isInvalidDate?.(date))
return false;
if (this.isDateDisabledMemo(date))
return false;
if (this.hooks?.validateDate) {
if (!this.hooks.validateDate(date, this._value, this.mode)) {
return false;
}
}
return true;
}
/** Resolves `dayMetadata` for a day, tolerating provider errors. */
getDayMetadata(day) {
if (!day)
return null;
let customMeta = null;
if (this.dayMetadata) {
try {
customMeta = this.dayMetadata(day) ?? null;
}
catch (error) {
if (isDevMode()) {
console.warn('[ngxsmk-datepicker] Error in dayMetadata provider:', error);
}
}
}
return customMeta;
}
getDayCellCustomClasses(day) {
if (!day)
return [];
const classes = [];
if (this.hooks?.getDayCellClasses) {
const isSelected = (this.mode === 'single' && this.isSameDayMemo(day, this.selectedDate)) ||
(this.mode === 'multiple' && this.isMultipleSelected(day)) ||
(this.mode === 'range' && (this.isSameDayMemo(day, this.startDate) || this.isSameDayMemo(day, this.endDate)));
const isDisabled = this.isDateDisabledMemo(day);
const isToday = this.isSameDayMemo(day, this.today);
const isHoliday = this.isHolidayMemo(day);
classes.push(...(this.hooks.getDayCellClasses(day, isSelected, isDisabled, isToday, isHoliday) || []));
}
const metaClass = this.getDayMetadata(day)?.cssClass;
if (typeof metaClass === 'string') {
classes.push(metaClass);
}
else if (Array.isArray(metaClass)) {
classes.push(...metaClass);
}
return classes;
}
getDayCellTooltip(day) {
if (!day)
return null;
const holidayLabel = this.getHolidayLabelMemo(day);
if (this.hooks?.getDayCellTooltip) {
const customTooltip = this.hooks.getDayCellTooltip(day, holidayLabel);
if (customTooltip !== null)
return customTooltip;
}
return this.getDayMetadata(day)?.tooltip ?? holidayLabel;
}
formatDayNumber(day) {
if (!day)
return '';
if (this.hooks?.formatDayNumber) {
return this.hooks.formatDayNumber(day);
}
return day.getDate().toString();
}
/**
* 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) {
if (!day)
return '';
if (this.hooks?.formatAriaLabel) {
return this.hooks.formatAriaLabel(day);
}
return day.toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
});
}
/**
* 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) {
const normalizedVal = val !== null && val !== undefined ? this._normalizeValue(val) : null;
this._value = normalizedVal;
this.initializeValue(normalizedVal);
this._invalidateMemoCache();
this._updateMemoSignals();
this.generateCalendar();
this.scheduleChangeDetection();
this.stateChanges.next();
if (this._field) {
this.fieldSyncService.updateFieldFromInternal(normalizedVal, this._field);
}
}
/**
* 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) {
this.onChange = fn;
}
/**
* 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) {
this.onTouched = fn;
}
setDisabledState(isDisabled) {
if (this.disabled !== isDisabled) {
this.disabled = isDisabled;
this.stateChanges.next();
}
}
/**
* 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
*/
emitValue(val) {
const normalizedVal = val !== null && val !== undefined ? this._normalizeValue(val) : null;
this._value = normalizedVal;
if (this._field) {
this.fieldSyncService.updateFieldFromInternal(normalizedVal, this._field);
}
this.valueChange.emit(normalizedVal);
this.onChange(normalizedVal);
this.onTouched();
if (this._field) {
this.fieldSyncService.markAsTouched(this._field);
}
if (!this.isInlineMode && val !== null && !this.timeOnly) {
if (this.mode === 'single' || (this.mode === 'range' && this.startDate && this.endDate)) {
this.isCalendarOpen = false;
}
}
this.stateChanges.next();
}
/**
* 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) {
if (this.disabled || this.isInlineMode)
return;
if (event?.target && event.target.closest('.ngxsmk-clear-button'))
return;
const now = Date.now();
if (this.lastToggleTime > 0 && now - this.lastToggleTime < 300)
return;
if (!event) {
this.applyToggleWithNoEvent(now);
return;
}
if (event.type === 'touchstart')
return;
if (event.type === 'click' && this.shouldSkipClickToggle(event))
return;
event.stopPropagation();
const wasOpen = this.isCalendarOpen;
const willOpen = !wasOpen;
if (willOpen) {
this.closeOtherCalendarInstances();
if (this._field)
this.syncFieldValue(this._field);
this.applyDefaultMonthForOpen();
this.applySmartViewModeForOpen();
this.generateCalendar();
}
this.isCalendarOpen = !wasOpen;
this.lastToggleTime = Date.now();
if (willOpen && this.isCalendarOpen) {
this._startOpeningState();
this.announceAfterOpen();
}
else {
this._startClosingState();
this.announceAfterClose();
}
}
applyToggleWithNoEvent(now) {
const wasOpen = this.isCalendarOpen;
const willOpen = !wasOpen;
this.isCalendarOpen = willOpen;
this.lastToggleTime = now;
if (willOpen) {
this.closeMonthYearDropdowns();
this.applyDefaultMonthForOpen();
this.applySmartViewModeForOpen();
this.generateCalendar();
}
if (willOpen && this.isCalendarOpen) {
this._startOpeningState();
if (this.isBrowser && document.activeElement instanceof HTMLElement) {
this.previousFocusElement = document.activeElement;
}
this.closeMonthYearDropdowns();
if (this.isBrowser) {
this.trackedDoubleRequestAnimationFrame(() => this.setupPassiveTouchListeners());
}
this.trackedSetTimeout(() => {
this.setupFocusTrap();
if (this.isBrowser)
this.setupPassiveTouchListeners();
this.closeMonthYearDropdowns();
this.announceCalendarOpened();
}, 100);
}
else {
this._startClosingState();
this.removeFocusTrap();
this.announceCalendarClosed();
}
}
shouldSkipClickToggle(event) {
const target = event.target;
if (this.allowTyping && target?.tagName === 'INPUT' && target.classList.contains('ngxsmk-display-input')) {
return true;
}
if (target?.closest('.ngxsmk-clear-button'))
return true;
const now = Date.now();
const touchDetectionWindow = this.isMobileDevice() ? 600 : 300;
if (this.touchStartElement && this.touchStartTime > 0) {
const timeSinceTouch = now - this.touchStartTime;
const sameElement = this.touchStartElement === event.target || this.touchStartElement.contains?.(event.target);
if (timeSinceTouch < touchDetectionWindow &&
sameElement &&
(this._isCalendarOpening() || (timeSinceTouch < 500 && this.isCalendarOpen))) {
return true;
}
}
if (now - this.lastToggleTime < 300)
return true;
return false;
}
applyDefaultMonthForOpen() {
if (this.defaultMonthOffset !== 0 && !this._value && !this._startAtDate) {
const nextMonth = new Date();
nextMonth.setMonth(nextMonth.getMonth() + this.defaultMonthOffset);
nextMonth.setDate(1);
this.currentDate = nextMonth;
this._currentMonth = nextMonth.getMonth();
this._currentYear = nextMonth.getFullYear();
this._currentDecade = Math.floor(this._currentYear / 10) * 10;
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this._invalidateMemoCache();
}
else if (this.mode === 'range' && this.startDate) {
this.currentDate = new Date(this.startDate);
this._currentMonth = this.startDate.getMonth();
this._currentYear = this.startDate.getFullYear();
this._currentDecade = Math.floor(this._currentYear / 10) * 10;
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this._invalidateMemoCache();
}
}
applySmartViewModeForOpen() {
if (this.mode === 'year')
this.calendarViewMode = 'decade';
else if (this.mode === 'month')
this.calendarViewMode = 'year';
}
announceAfterOpen() {
if (this.isCalendarOpen) {
this.trackedSetTimeout(() => {
this.setupFocusTrap();
if (this.isBrowser) {
this.setupPassiveTouchListeners();
this.positionPopoverRelativeToInput();
if (this._shouldAppendToBody) {
this.trackedRequestAnimationFrame(() => {
this.trackedRequestAnimationFrame(() => {
this.positionPopoverRelativeToInput();
this.revealBodyPopover();
});
});
}
}
this.announceCalendarOpened();
}, 50);
}
}
announceAfterClose() {
this.removeFocusTrap();
this.announceCalendarClosed();
}
announceCalendarOpened() {
const monthName = this.currentDate.toLocaleDateString(this.locale, {
month: 'long',
});
const year = String(this.currentDate.getFullYear());
const msg = this.getTranslation('calendarOpened', undefined, { month: monthName, year }) ||
`Calendar opened for ${monthName} ${year}`;
this.ariaLiveService.announce(msg, 'polite');
}
announceCalendarClosed() {
const msg = this.getTranslation('calendarClosed') || 'Calendar closed';
this.ariaLiveService.announce(msg, 'polite');
}
onBackdropInteract(event) {
event.stopPropagation();
// Protection against ghost clicks immediately after opening
const now = Date.now();
const timeSinceToggle = now - this.lastToggleTime;
const protectionTime = this.isMobileDevice() ? 600 : 300;
if (timeSinceToggle < protectionTime) {
return;
}
if (event instanceof KeyboardEvent) {
event.preventDefault();
}
this.closeCalendarWithFocusRestore();
this.lastToggleTime = now;
}
onPopoverEscape(event) {
event.preventDefault();
event.stopPropagation();
this.closeCalendarWithFocusRestore();
}
_startOpeningState() {
if (this.isBrowser && this._shouldAppendToBody) {
window.addEventListener('scroll', this.updatePositionOnScroll, { capture: true, passive: true });
window.addEventListener('resize', this.updatePositionOnScroll, { passive: true });
}
this._isCalendarOpening.set(true);
if (this._shouldAppendToBody) {
this.renderInBody();
if (this.isBrowser) {
this.positionPopoverRelativeToInput();
}
}
if (this.openCalendarTimeoutId) {
clearTimeout(this.openCalendarTimeoutId);
}
const timeoutDelay = this.isMobileDevice() ? 150 : 60;
this.openCalendarTimeoutId = this.trackedSetTimeout(() => {
this._isCalendarOpening.set(false);
this.openCalendarTimeoutId = null;
if (this._shouldAppendToBody && this.isBrowser) {
this.positionPopoverRelativeToInput();
this.revealBodyPopover();
}
this.cdr.markForCheck();
}, timeoutDelay);
}
_startClosingState() {
if (this.isBrowser) {
window.removeEventListener('scroll', this.updatePositionOnScroll, { capture: true });
window.removeEventListener('resize', this.updatePositionOnScroll);
}
this._isCalendarOpening.set(false);
if (this.portalViewRef) {
this.destroyBodyView();
}
if (this.openCalendarTimeoutId) {
clearTimeout(this.openCalendarTimeoutId);
this.openCalendarTimeoutId = null;
}
}
renderInBody() {
if (this.portalViewRef)
return; // Already rendered
// Create the view
this.portalViewRef = this.portalTemplate.createEmbeddedView(null);
// Attach to application logic for change detection
this.appRef.attachView(this.portalViewRef);
// Hide popover and set id before appending so it never flashes in the wrong place (e.g. outside modal)
this.portalViewRef.rootNodes.forEach((node) => {
if (node instanceof HTMLElement) {
const popover = node.classList?.contains('ngxsmk-popover-container')
? node
: node.querySelector?.('.ngxsmk-popover-container');
if (popover instanceof HTMLElement) {
popover.style.setProperty('visibility', 'hidden', 'important');
popover.id = this.popoverId;
}
}
this.document.body.appendChild(node);
});
this.hideBodyPopoverUntilPositioned();
this.lockBodyScroll();
this.cdr.markForCheck();
}
lockBodyScroll() {
if (!this.isBrowser || this.isInlineMode)
return;
if (window.matchMedia && window.matchMedia('(pointer: coarse)').matches) {
document.body.classList.add('ngxsmk-scroll-locked');
}
}
unlockBodyScroll() {
if (!this.isBrowser)
return;
document.body.classList.remove('ngxsmk-scroll-locked');
}
/** Hides the body-appended popover so loading/calendar are not visible at wrong position. */
hideBodyPopoverUntilPositioned() {
if (!this.isBrowser)
return;
const popover = this.document.getElementById(this.popoverId);
if (popover) {
popover.style.setProperty('visibility', 'hidden', 'important');
}
}
/** Shows the body-appended popover after positioning has been applied. */
revealBodyPopover() {
if (!this.isBrowser)
return;
const popover = this.document.getElementById(this.popoverId);
if (popover) {
popover.style.setProperty('visibility', 'visible', 'important');
}
}
destroyBodyView() {
if (this.portalViewRef) {
this.appRef.detachView(this.portalViewRef);
// Remove nodes (destroy does this usually if attached to VCR, but here we appended manually so we might need to remove unless detach handles it via cleanup?
// createEmbeddedView returns a view that is not attached to VCR unless we call insert.
// appRef.attachView enables CD.
// destroy() removes it from DOM if it knew where it was? No, EmbeddedViewRef.destroy() removes from DOM if it was inserted via VCR.
// Since we manually appended, we should manually remove.
this.portalViewRef.rootNodes.forEach((node) => {
if (node.parentNode && 'remove' in node) {
node.remove();
}
});
this.portalViewRef.destroy();
this.portalViewRef = null;
this.unlockBodyScroll();
}
}
closeCalendar() {
if (!this.isInlineMode) {
this.removeFocusTrap();
this.closeCalendarWithFocusRestore();
const calendarClosedMsg = this.getTranslation('calendarClosed') || 'Calendar closed';
this.ariaLiveService.announce(calendarClosedMsg, 'polite');
this.cdr.markForCheck();
}
}
shouldAutoClose() {
if (!this.autoApplyClose() || this.showTime || this.timeOnly || this.isInlineMode) {
return false;
}
if (this.mode === 'single') {
return this.selectedDate !== null;
}
else if (this.mode === 'range') {
return this.startDate !== null && this.endDate !== null;
}
return false;
}
/**
* 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) {
if (event) {
event.stopPropagation();
event.preventDefault();
}
if (this.disabled)
return;
if (this.enableHapticFeedback()) {
this.hapticFeedbackService.heavy();
}
this.clearTouchHandledFlag();
this.clearValidationError();
this.selectedDate = null;
this.selectedDates = [];
this.startDate = null;
this.endDate = null;
this.hoveredDate = null;
this.isCalendarOpen = false;
this.emitValue(null);
this.action.emit({ type: 'clear', payload: null });
let resetDate = new Date();
if (this._startAtDate) {
resetDate = new Date(this._startAtDate);
}
else if (this._minDate && getStartOfDay(this._minDate).getTime() > this.today.getTime()) {
resetDate = new Date(this._minDate);
}
this.currentDate = resetDate;
this._currentMonth = this.currentDate.getMonth();
this._currentYear = this.currentDate.getFullYear();
this._currentDecade = Math.floor(this._currentYear / 10) * 10;
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this._invalidateMemoCache();
this.generateCalendar();
}
get currentMonth() {
return this._currentMonth;
}
set currentMonth(month) {
if (this.disabled)
return;
if (this._currentMonth !== month) {
this._currentMonth = month;
this._currentMonthSignal.set(month);
// Fix: Normalize to 1st of month to prevent Date overflow (e.g., Jan 31 -> Feb would become Mar)
this.currentDate = new Date(this.currentDate.getFullYear(), month, 1);
this._invalidateMemoCache();
this.generateCalendar();
}
}
get currentYear() {
return this._currentYear;
}
set currentYear(year) {
if (this.disabled)
return;
if (this._currentYear !== year) {
this._currentYear = year;
this._currentYearSignal.set(year);
// Fix: Normalize to 1st of month to prevent Date overflow (e.g., Feb 29 -> non-leap year)
this.currentDate = new Date(year, this.currentDate.getMonth(), 1);
if (this.focusedDate) {
const lastDayOfNewMonth = new Date(year, this.currentDate.getMonth() + 1, 0).getDate();
const clampedDay = Math.min(this.focusedDate.getDate(), lastDayOfNewMonth);
let targetFocused = new Date(year, this.currentDate.getMonth(), clampedDay);
if (!this.isDateValid(targetFocused)) {
targetFocused = this.findFirstValidDateInMonth(year, this.currentDate.getMonth()) ?? targetFocused;
}
this.focusedDate = targetFocused;
}
this.adjustDisplayedDateToRange();
this._invalidateMemoCache();
this.generateCalendar();
}
}
_updateToday() {
let now = new Date();
if (this.timezone && isValidTimezone(this.timezone)) {
now = convertTimezone(now, this.timezone, '');
}
this.today = getStartOfDay(now);
this.updateRangesArray();
}
ngOnInit() {
if (!this.timezone && this.showTimezoneSelector() && this.defaultTimezone) {
this.timezone = this.defaultTimezone;
}
if (this.enableNaturalLanguage) {
this.allowTyping = true;
}
NgxsmkDatepickerComponent._allInstances.add(this);
this.applyGlobalConfig();
this.applyAnimationConfig();
this._updateMemoSignals();
if (this._locale === 'en-US' && this.isBrowser && typeof navigator !== 'undefined' && navigator.language) {
this._locale = navigator.language;
}
this.initializeTranslations();
if (this.timeOnly)
this.showTime = true;
this.updateRtlState();
this._updateToday();
this.generateLocaleData();
this.generateTimeOptions();
this.generateYearGrid();
this.generateDecadeGrid();
if (this.calendarViewMode === 'timeline')
this.generateTimeline();
if (this.calendarViewMode === 'time-slider' && this.mode === 'range' && this.showTime) {
this.initializeTimeSliders();
}
this.initializeTimeFromNowIfNeeded();
const initialValue = this.resolveInitialValue();
if (initialValue) {
this.initializeValue(initialValue);
this._value = initialValue;
}
else {
this.initializeValue(null);
}
this.generateCalendar();
if (this._field && this.isBrowser)
this.debouncedFieldSync();
}
initializeTimeFromNowIfNeeded() {
if (!(this.showTime || this.timeOnly) || this._value)
return;
const now = new Date();
this.currentHour = now.getHours();
this.currentMinute = Math.floor(now.getMinutes() / this.minuteInterval) * this.minuteInterval;
if (this.currentMinute === 60) {
this.currentMinute = 0;
this.currentHour = (this.currentHour + 1) % 24;
}
if (this.showSeconds) {
this.currentSecond = Math.min(59, Math.floor(now.getSeconds() / this.secondInterval) * this.secondInterval);
}
this.update12HourState(this.currentHour);
if (this.timeOnly && !this._value) {
const today = new Date();
const sec = this.showSeconds || this.currentSecond !== 0 ? this.currentSecond : 0;
today.setHours(this.currentHour, this.currentMinute, sec, 0);
this.selectedDate = today;
this.currentDate = new Date(today);
this._currentMonth = today.getMonth();
this._currentYear = today.getFullYear();
this._currentDecade = Math.floor(this._currentYear / 10) * 10;
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this._invalidateMemoCache();
}
}
resolveInitialValue() {
if (!this._field)
return this._value ?? null;
const fromField = this.resolveInitialValueFromField();
return fromField ?? this._value ?? null;
}
resolveInitialValueFromField() {
try {
let resolved = this._field;
if (typeof resolved === 'function' && !('set' in resolved) && !('update' in resolved)) {
try {
const res = resolved();
if (res && typeof res === 'object')
resolved = res;
}
catch {
/* ignore */
}
}
if (resolved && typeof resolved === 'object') {
const rf = resolved;
const fieldValue = typeof rf['value'] === 'function' ? rf['value']() : rf['value'];
return this._normalizeValue(fieldValue);
}
}
catch {
/* ignore */
}
return null;
}
ngAfterViewInit() {
if (this.allowTyping && this.displayValue) {
this.typedInputValue = this.displayValue;
}
if (this.isBrowser) {
this.trackedDoubleRequestAnimationFrame(() => {
this.setupPassiveTouchListeners();
this.setupInputGroupPassiveListeners();
this.setupLazyLoadingObserver();
this.trackedSetTimeout(() => {
this.setupInputGroupPassiveListeners();
}, 100);
});
if (this._field) {
this.trackedDoubleRequestAnimationFrame(() => {
this.syncFieldValue(this._field);
});
this.debouncedFieldSync(100);
}
}
}
setupInputGroupPassiveListeners() {
const nativeElement = this.elementRef?.nativeElement;
if (!nativeElement) {
this.trackedSetTimeout(() => this.setupInputGroupPassiveListeners(), 50);
return;
}
const inputGroup = nativeElement.querySelector('.ngxsmk-input-group');
if (!inputGroup) {
this.trackedSetTimeout(() => this.setupInputGroupPassiveListeners(), 50);
return;
}
if (this.touchListenersSetup.get(inputGroup)) {
return;
}
this.touchListenersSetup.set(inputGroup, true);
const touchStartHandler = (event) => {
this.onTouchStart(event);
};
inputGroup.addEventListener('touchstart', touchStartHandler, {
passive: true,
});
const touchEndHandler = (event) => {
this.onTouchEnd(event);
};
inputGroup.addEventListener('touchend', touchEndHandler, { passive: true });
this.passiveTouchListeners.push(() => {
this.touchListenersSetup.delete(inputGroup);
inputGroup.removeEventListener('touchstart', touchStartHandler);
inputGroup.removeEventListener('touchend', touchEndHandler);
});
}
/**
* 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.
*/
setupPassiveTouchListeners() {
if (!this.isBrowser)
return;
this.passiveTouchListeners.forEach((cleanup) => cleanup());
this.passiveTouchListeners = [];
const nativeElement = this.elementRef?.nativeElement;
if (!nativeElement)
return;
if (this._touchListenersSetupTimeout) {
return;
}
this._touchListenersSetupTimeout = this.trackedSetTimeout(() => {
this._touchListenersSetupTimeout = null;
if (!this.isBrowser || !nativeElement)
return;
const dateCells = nativeElement.querySelectorAll('.ngxsmk-day-cell[data-date]');
if (dateCells.length > 0) {
this.attachTouchListenersToCells(dateCells);
}
else if (this.isCalendarOpen) {
let retryCount = 0;
const maxRetries = 5;
let retryTimeoutId = null;
const retry = () => {
if (!this.isBrowser || !nativeElement || !this.isCalendarOpen) {
if (retryTimeoutId) {
this.activeTimeouts.delete(retryTimeoutId);
clearTimeout(retryTimeoutId);
}
return;
}
retryCount++;
const dateCellsRetry = nativeElement.querySelectorAll('.ngxsmk-day-cell[data-date]');
if (dateCellsRetry.length > 0) {
this.attachTouchListenersToCells(dateCellsRetry);
retryTimeoutId = null;
}
else if (retryCount < maxRetries && this.isCalendarOpen) {
retryTimeoutId = this.trackedSetTimeout(retry, 50);
}
else {
retryTimeoutId = null;
}
};
retryTimeoutId = this.trackedSetTimeout(retry, 50);
}
}, 10);
}
attachTouchListenersToCells(dateCells) {
dateCells.forEach((cellEl) => {
const cell = cellEl;
const dateTimestamp = cell.dataset['date'];
if (!dateTimestamp)
return;
const dateValue = Number.parseInt(dateTimestamp, 10);
if (Number.isNaN(dateValue))
return;
const day = new Date(dateValue);
if (!day || Number.isNaN(day.getTime()))
return;
if (this.touchListenersAttached.get(cell)) {
return;
}
this.touchListenersAttached.set(cell, true);
const touchStartHandler = (event) => {
this.onDateCellTouchStart(event, day);
};
cell.addEventListener('touchstart', touchStartHandler, { passive: true });
const touchEndHandler = (event) => {
this.onDateCellTouchEnd(event, day);
};
cell.addEventListener('touchend', touchEndHandler, { passive: true });
const touchMoveHandler = (event) => {
this.onDateCellTouchMove(event);
};
cell.addEventListener('touchmove', touchMoveHandler, { passive: true });
this.passiveTouchListeners.push(() => {
this.touchListenersAttached.delete(cell);
cell.removeEventListener('touchstart', touchStartHandler);
cell.removeEventListener('touchend', touchEndHandler);
cell.removeEventListener('touchmove', touchMoveHandler);
});
});
}
ngOnChanges(changes) {
this.validateInputs(changes);
let needsChangeDetection = false;
if (changes['timeOnly'] || changes['mode']) {
needsChangeDetection = this.handleChangesTimeAndMode(changes) || needsChangeDetection;
}
if (changes['locale'] || changes['rtl']) {
needsChangeDetection = this.handleChangesLocaleRtl(changes) || needsChangeDetection;
}
if (changes['weekStart'] ||
changes['minuteInterval'] ||
changes['holidayProvider'] ||
changes['yearRange'] ||
changes['timezone'] ||
changes['use24Hour']) {
needsChangeDetection = this.handleChangesWeekStartAndRelated(changes) || needsChangeDetection;
}
if (changes['rangePresetFactory']) {
this.updateRangesArray();
needsChangeDetection = true;
}
if (changes['asyncDateFilter']) {
if (this.asyncDateFilter) {
this.refreshAsyncDisabledDates();
}
else {
this._asyncFilterRequestId++; // invalidate in-flight requests
this._asyncDisabledTimestamps.set(new Set());
}
}
if (needsChangeDetection)
this.scheduleChangeDetection();
if (changes['field'])
this.handleChangesField(changes);
if (changes['value'])
this.handleChangesValue(changes);
this.handleChangesDisabledStates(changes);
this.handleChangesTranslations(changes);
if (changes['startAt'])
this.handleChangesStartAt();
if (changes['minDate'])
this.handleChangesMinDate();
this.handleChangesMaxDate(changes);
if (changes['calendarViewMode'])
this.handleChangesCalendarViewMode();
this.handleChangesShowTimeInlineAndLayout(changes);
}
handleChangesShowTimeInlineAndLayout(changes) {
if (changes['showTime'] || changes['showSeconds']) {
if (this.showTime) {
this.generateTimeOptions();
this.update12HourState(this.currentHour);
}
this.cdr.markForCheck();
}
if (changes['inline']) {
if (this.isInlineMode)
this.generateCalendar();
this.cdr.markForCheck();
}
if (changes['calendarLayout'] ||
changes['calendarCount'] ||
changes['showCalendarButton'] ||
changes['allowTyping'] ||
changes['align'] ||
changes['useNativePicker'] ||
changes['theme']) {
this.generateCalendar();
this.cdr.markForCheck();
}
}
handleChangesLocaleRtl(changes) {
this.updateRtlState();
if (changes['locale']) {
this.clearActiveTimeouts();
this.calendarGenerationService.clearCache();
this.initializeTranslations();
this.generateLocaleData();
this._invalidateMemoCache();
this.generateCalendar();
}
return true;
}
handleChangesWeekStartAndRelated(changes) {
if (changes['timezone']) {
this._updateToday();
}
this.applyGlobalConfig();
if (changes['weekStart'] || changes['yearRange']) {
if (changes['weekStart']) {
this.clearActiveTimeouts();
this.calendarGenerationService.clearCache();
}
this.generateLocaleData();
this._invalidateMemoCache();
this.generateCalendar();
if (changes['weekStart'])
this.clearActiveTimeouts();
}
this.applyChangesMinuteAnd24Hour(changes);
if (changes['minuteInterval']) {
this.currentMinute = Math.floor(this.currentMinute / this.minuteInterval) * this.minuteInterval;
this.timeChange();
return false;
}
if (changes['yearRange'])
this.generateDropdownOptions();
return true;
}
applyChangesMinuteAnd24Hour(changes) {
if (changes['minuteInterval'] || changes['use24Hour']) {
this.generateTimeOptions();
if (changes['use24Hour'])
this.update12HourState(this.currentHour);
}
}
handleChangesField(changes) {
const newField = changes['field']?.currentValue;
if (newField && typeof newField === 'object') {
this.syncFieldValue(newField);
if (this.isBrowser)
this.debouncedFieldSync(50);
}
}
handleChangesTimeAndMode(changes) {
if (changes['timeOnly']) {
if (this.timeOnly) {
this.showTime = true;
this.generateTimeOptions();
}
}
if (changes['mode']) {
this.initializeValue(this._value);
this.generateCalendar();
}
return !!(changes['timeOnly'] || changes['mode']);
}
handleChangesDisabledStates(changes) {
if (changes['holidayProvider'] ||
changes['disableHolidays'] ||
changes['disabledDates'] ||
changes['disabledRanges']) {
this._updateMemoSignals();
this.generateCalendar();
this.cdr.markForCheck();
}
}
handleChangesTranslations(changes) {
if (changes['translations'] || changes['translationService']) {
this.initializeTranslations();
this.scheduleChangeDetection();
}
}
handleChangesMaxDate(changes) {
if (changes['maxDate']) {
this.generateCalendar();
this.cdr.markForCheck();
}
}
handleChangesValue(changes) {
if (this._field)
return;
const newValue = changes['value']?.currentValue;
if (this.isValueEqual(newValue, this._value))
return;
this._value = newValue;
this.initializeValue(newValue);
this.generateCalendar();
this.cdr.markForCheck();
}
handleChangesStartAt() {
if (!this._value && this._startAtDate) {
this.currentDate = new Date(this._startAtDate);
this._currentMonth = this.currentDate.getMonth();
this._currentYear = this.currentDate.getFullYear();
this._currentDecade = Math.floor(this._currentYear / 10) * 10;
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this.generateCalendar();
this.cdr.markForCheck();
}
}
handleChangesMinDate() {
this.generateCalendar();
this.cdr.markForCheck();
if (!this._value && this._minDate) {
const minDateOnly = getStartOfDay(this._minDate);
const todayOnly = this.today;
if (minDateOnly.getTime() > todayOnly.getTime()) {
this.currentDate = new Date(this._minDate);
this._currentMonth = this.currentDate.getMonth();
this._currentYear = this.currentDate.getFullYear();
this._currentDecade = Math.floor(this._currentYear / 10) * 10;
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this.generateCalendar();
this.cdr.markForCheck();
}
}
}
handleChangesCalendarViewMode() {
if (this.calendarViewMode === 'year')
this.generateYearGrid();
else if (this.calendarViewMode === 'decade')
this.generateDecadeGrid();
else if (this.calendarViewMode === 'timeline')
this.generateTimeline();
else if (this.calendarViewMode === 'time-slider')
this.initializeTimeSliders();
else
this.generateCalendar();
this.cdr.markForCheck();
}
/**
* 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
*/
validateInputs(changes) {
this.validateInputsMinMaxDate(changes);
this.validateInputsTimeOnly(changes);
this.validateInputsIntervals(changes);
this.validateInputsYearRange(changes);
}
validateInputsMinMaxDate(changes) {
if (!changes['minDate'] && !changes['maxDate'])
return;
if (!this._minDate || !this._maxDate)
return;
const minStart = getStartOfDay(this._minDate);
const maxStart = getStartOfDay(this._maxDate);
if (minStart.getTime() <= maxStart.getTime())
return;
if (isDevMode()) {
console.warn('[ngxsmk-datepicker] minDate is greater than maxDate. ' +
`minDate: ${this._minDate.toISOString()}, maxDate: ${this._maxDate.toISOString()}. ` +
'Adjusting maxDate to be at least 1 day after minDate.');
}
const adjustedMaxDate = new Date(minStart);
adjustedMaxDate.setDate(adjustedMaxDate.getDate() + 1);
this._maxDate = adjustedMaxDate;
this._updateMemoSignals();
this._invalidateMemoCache();
}
validateInputsTimeOnly(changes) {
if (!changes['timeOnly'] || !this.timeOnly || this.mode === 'single')
return;
if (isDevMode()) {
console.warn('[ngxsmk-datepicker] timeOnly is only supported with mode="single". ' +
`Current mode: "${this.mode}". timeOnly will be disabled.`);
}
this.timeOnly = false;
}
validateInputsIntervals(changes) {
if (changes['minuteInterval'] && this.minuteInterval < 1) {
if (isDevMode()) {
console.warn(`[ngxsmk-datepicker] minuteInterval must be at least 1. Received: ${this.minuteInterval}. Setting to 1.`);
}
this.minuteInterval = 1;
}
if (changes['secondInterval'] && this.secondInterval < 1) {
if (isDevMode()) {
console.warn(`[ngxsmk-datepicker] secondInterval must be at least 1. Received: ${this.secondInterval}. Setting to 1.`);
}
this.secondInterval = 1;
}
}
validateInputsYearRange(changes) {
if (!changes['yearRange'] || this.yearRange >= 1)
return;
if (isDevMode()) {
console.warn(`[ngxsmk-datepicker] yearRange must be at least 1. Received: ${this.yearRange}. Setting to 1.`);
}
this.yearRange = 1;
}
initializeTimeSliders() {
if (this.mode === 'range' && this.showTime) {
if (this.startDate) {
this.startTimeSlider = this.startDate.getHours() * 60 + this.startDate.getMinutes();
}
if (this.endDate) {
this.endTimeSlider = this.endDate.getHours() * 60 + this.endDate.getMinutes();
}
else {
this.endTimeSlider = 1440;
}
}
}
get24Hour(displayHour, isPm) {
return get24Hour(displayHour, isPm);
}
update12HourState(fullHour) {
if (this.use24Hour) {
this.currentDisplayHour = fullHour;
this.isPm = false;
}
else {
const state = update12HourState(fullHour);
this.isPm = state.isPm;
this.currentDisplayHour = state.displayHour;
}
}
applyCurrentTime(date) {
if (this.use24Hour) {
this.currentHour = this.currentDisplayHour;
}
else {
this.currentHour = this.get24Hour(this.currentDisplayHour, this.isPm);
}
const newDate = new Date(date);
const sec = this.showSeconds || this.currentSecond !== 0 ? this.currentSecond : 0;
newDate.setHours(this.currentHour, this.currentMinute, sec, 0);
return newDate;
}
applyTimeIfNeeded(date) {
if (this.showTime || this.timeOnly) {
return this.applyCurrentTime(date);
}
return getStartOfDay(date);
}
/**
* 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
*/
initializeValue(value) {
const initialDate = this.applyValueToSelection(value);
const viewCenterDate = this.resolveViewCenterDate(initialDate);
if (viewCenterDate) {
this.currentDate = new Date(viewCenterDate);
this._currentMonth = viewCenterDate.getMonth();
this._currentYear = viewCenterDate.getFullYear();
this._currentDecade = Math.floor(this._currentYear / 10) * 10;
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this.currentHour = viewCenterDate.getHours();
this.currentMinute = viewCenterDate.getMinutes();
if (this.showSeconds) {
this.currentSecond = Math.min(59, Math.floor(viewCenterDate.getSeconds() / this.secondInterval) * this.secondInterval);
}
this.update12HourState(this.currentHour);
this.currentMinute = Math.floor(this.currentMinute / this.minuteInterval) * this.minuteInterval;
}
}
applyValueToSelection(value) {
this.selectedDate = null;
this.startDate = null;
this.endDate = null;
this.selectedDates = [];
if (!value)
return null;
if (this.mode === 'single' && value instanceof Date) {
this.selectedDate = this._normalizeDate(value);
return this.selectedDate;
}
if (this.mode === 'range' && typeof value === 'object' && 'start' in value && 'end' in value) {
const range = value;
this.startDate = this._normalizeDate(range.start);
this.endDate = this._normalizeDate(range.end);
return this.startDate;
}
if (this.mode === 'multiple' && Array.isArray(value)) {
this.selectedDates = value.map((d) => this._normalizeDate(d)).filter((d) => d !== null);
return this.selectedDates.length > 0 ? (this.selectedDates.at(-1) ?? null) : null;
}
return null;
}
resolveViewCenterDate(initialDate) {
let viewCenterDate = initialDate || this._startAtDate;
if (!viewCenterDate && this._minDate) {
const minDateOnly = getStartOfDay(this._minDate);
const todayOnly = this.today;
if (minDateOnly.getTime() > todayOnly.getTime()) {
viewCenterDate = this._minDate;
}
}
if ((this.isCalendarOpen || this.isInlineMode) &&
this.currentDate &&
this.mode === 'range' &&
this.startDate &&
this.endDate) {
const isStartVisible = this.isCurrentMonth(this.startDate);
const isEndVisible = this.isCurrentMonth(this.endDate);
if (isStartVisible || isEndVisible)
viewCenterDate = null;
}
return viewCenterDate;
}
_normalizeDate(date) {
return normalizeDate(date);
}
/**
* 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.
*/
_normalizeValue(val) {
if (val === null || val === undefined)
return null;
if (val instanceof Date) {
return this._normalizeDate(val);
}
if (this.isMomentObject(val)) {
const momentObj = val;
return this._normalizeDate(this.momentToDate(momentObj));
}
if (typeof val === 'object' && val !== null && 'start' in val && 'end' in val) {
return this._normalizeRangeValue(val);
}
if (Array.isArray(val)) {
return this._normalizeArrayValue(val);
}
if (typeof val === 'string' && this.displayFormat) {
const parsed = this.parsingService.parseCustomDateString(val, this.displayFormat);
return parsed;
}
if (typeof val === 'string' || (typeof val === 'object' && val !== null && 'getTime' in val)) {
const normalized = this._normalizeDate(val);
return normalized;
}
return null;
}
_normalizeRangeValue(range) {
const start = this.isMomentObject(range.start)
? this._normalizeDate(this.momentToDate(range.start))
: this._normalizeDate(range.start);
const end = this.isMomentObject(range.end)
? this._normalizeDate(this.momentToDate(range.end))
: this._normalizeDate(range.end);
if (start && end)
return { start, end };
return null;
}
_normalizeArrayValue(val) {
const dates = val
.map((d) => {
if (this.isMomentObject(d)) {
return this._normalizeDate(this.momentToDate(d));
}
return this._normalizeDate(d);
})
.filter((d) => d !== null);
return dates;
}
/**
* Check if the provided value is a Moment.js object
*/
isMomentObject(val) {
if (!val || typeof val !== 'object') {
return false;
}
const obj = val;
return (typeof obj['format'] === 'function' &&
typeof obj['toDate'] === 'function' &&
typeof obj['isMoment'] === 'function' &&
typeof obj['isMoment']() === 'boolean' &&
obj['isMoment']() === true);
}
/**
* Convert a Moment.js object to a Date, preserving timezone offset
*/
momentToDate(momentObj) {
if (typeof momentObj.utcOffset === 'function' && typeof momentObj.format === 'function') {
const offset = momentObj.utcOffset();
if (offset !== undefined && offset !== null) {
try {
const formatted = momentObj.format('YYYY-MM-DDTHH:mm:ss.SSSZ');
const date = new Date(formatted);
if (!Number.isNaN(date.getTime())) {
return date;
}
}
catch { }
}
}
return momentObj.toDate();
}
/**
* 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.
*/
isValueEqual(val1, val2) {
if (val1 === val2)
return true;
if (val1 === null || val2 === null)
return val1 === val2;
if (val1 instanceof Date && val2 instanceof Date) {
return val1.getTime() === val2.getTime();
}
if (typeof val1 === 'object' &&
typeof val2 === 'object' &&
'start' in val1 &&
'end' in val1 &&
'start' in val2 &&
'end' in val2) {
const r1 = val1;
const r2 = val2;
return r1.start.getTime() === r2.start.getTime() && r1.end.getTime() === r2.end.getTime();
}
if (Array.isArray(val1) && Array.isArray(val2)) {
if (val1.length !== val2.length)
return false;
return val1.every((d1, i) => {
const d2 = val2[i];
return d1?.getTime() === d2?.getTime();
});
}
return false;
}
/**
* 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) {
// Prevent keyboard on mobile when calendar should open instead
if (this.isMobileDevice() && !this.allowTyping && !this.isInlineMode) {
event.target.blur();
this.toggleCalendar(event);
return;
}
if (!this._focused) {
this._focused = true;
this.stateChanges.next();
}
if (!this.allowTyping)
return;
this.isTyping = true;
// Seed the typing buffer with the current formatted value so the user edits it.
this.typedInputValue = this.displayValue || '';
}
/**
* 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.
*/
sanitizeInput(input) {
if (!input || typeof input !== 'string') {
return '';
}
// Remove HTML tag delimiters
let sanitized = input.replaceAll(/[<>]/g, '');
// Remove script event handlers (onerror, onclick, onload, etc.)
sanitized = sanitized.replaceAll(/on\w+\s*=/gi, '');
// Remove dangerous protocols (javascript:, vbscript:, data:)
sanitized = sanitized.replaceAll(/\b(?:javascript|vbscript|data)\s*:/gi, '');
// Remove control characters
// eslint-disable-next-line no-control-regex
sanitized = sanitized.replaceAll(/[\u0000-\u001F\u007F]/g, '');
// Remove quotes/backticks/equals to prevent attribute injection
sanitized = sanitized.replaceAll(/[`"'=]/g, '');
// Trim whitespace
return sanitized.trim();
}
onInputChange(event) {
if (!this.allowTyping)
return;
const input = event.target;
const value = this.sanitizeInput(input.value);
const maskPattern = this.effectiveInputMaskPattern();
this.typedInputValue = maskPattern ? this.applyInputMask(value, maskPattern) : value;
if (input.value !== this.typedInputValue) {
const rawCaret = input.selectionStart ?? input.value.length;
// When typing at the end, keep the caret at the end even if the mask
// inserted separators; mid-string edits keep their original position.
const wasAtEnd = rawCaret >= input.value.length;
input.value = this.typedInputValue;
const caret = wasAtEnd ? this.typedInputValue.length : Math.min(rawCaret, this.typedInputValue.length);
this.trackedSetTimeout(() => {
input.setSelectionRange(caret, caret);
}, 0);
}
this.updateNaturalLanguagePreview(value);
this.scheduleChangeDetection();
}
onInputBlur(event) {
const relatedTarget = event.relatedTarget;
const isMovingWithinComponent = relatedTarget && this.elementRef?.nativeElement?.contains(relatedTarget);
if (!isMovingWithinComponent && this._focused) {
this._focused = false;
this.stateChanges.next();
this.onTouched();
if (this._field) {
this.fieldSyncService.markAsTouched(this._field);
}
}
if (!this.allowTyping)
return;
this.isTyping = false;
const input = event.target;
const value = this.sanitizeInput(input.value);
if (!value) {
this.clearValue();
this.typedInputValue = '';
this.clearValidationError();
this.scheduleChangeDetection();
return;
}
const parsedDate = this.parsingService.parseTypedInput(value, this.displayFormat);
if (parsedDate && this.isValidDate(parsedDate)) {
this.clearValidationError();
this.applyTypedDate(parsedDate);
this.typedInputValue = this.displayValue;
this.showNaturalLanguagePreview = false;
}
else if (this.enableNaturalLanguage) {
const resolved = this.naturalLanguageParserService.parse(value);
if (resolved) {
this.clearValidationError();
if (resolved instanceof Date) {
this.selectedDate = resolved;
this.emitValue(resolved);
this.naturalLanguageResolved.emit(resolved);
}
else {
this.startDate = resolved.start;
this.endDate = resolved.end;
this.emitValue(resolved);
this.naturalLanguageResolved.emit(resolved);
}
this.typedInputValue = this.displayValue;
this.generateCalendar();
}
else {
this.applyValidationErrorForBlur(null);
this.typedInputValue = this.displayValue;
}
this.showNaturalLanguagePreview = false;
this.scheduleChangeDetection();
}
else {
this.applyValidationErrorForBlur(parsedDate);
this.typedInputValue = this.displayValue;
this.showNaturalLanguagePreview = false;
this.scheduleChangeDetection();
}
}
applyValidationErrorForBlur(parsedDate) {
if (parsedDate) {
if (this._minDate && parsedDate < this._minDate) {
const minFormatted = this.parsingService.formatDateWithPattern(this._minDate, this.displayFormat ?? 'MM/DD/YYYY');
const msg = this.getTranslation('dateBeforeMin', undefined, { minDate: minFormatted }) ||
`Date must be on or after ${minFormatted}.`;
this.setValidationError('dateBeforeMin', msg);
}
else if (this._maxDate && parsedDate > this._maxDate) {
const maxFormatted = this.parsingService.formatDateWithPattern(this._maxDate, this.displayFormat ?? 'MM/DD/YYYY');
const msg = this.getTranslation('dateAfterMax', undefined, { maxDate: maxFormatted }) ||
`Date must be on or before ${maxFormatted}.`;
this.setValidationError('dateAfterMax', msg);
}
else {
const msg = this.getTranslation('invalidDate') || 'Invalid date.';
this.setValidationError('invalidDate', msg);
}
}
else {
const msg = this.getTranslation('invalidDateFormat') || 'Please enter a valid date.';
this.setValidationError('invalidDateFormat', msg);
}
}
onInputKeyDown(event) {
const keyboardEvent = event;
if (!this.allowTyping) {
if (keyboardEvent.key === 'Enter' || keyboardEvent.key === ' ') {
this.toggleCalendar(keyboardEvent);
if (keyboardEvent.key === ' ') {
keyboardEvent.preventDefault();
}
}
return;
}
if (keyboardEvent.key === 'Enter') {
keyboardEvent.preventDefault();
const input = keyboardEvent.target;
const value = this.sanitizeInput(input.value);
if (!value) {
this.clearValue();
this.typedInputValue = '';
this.showNaturalLanguagePreview = false;
return;
}
const parsedDate = this.parsingService.parseTypedInput(value, this.displayFormat);
if (parsedDate !== null && this.isValidDate(parsedDate)) {
this.applyTypedDate(parsedDate);
this.typedInputValue = this.displayValue;
this.showNaturalLanguagePreview = false;
input.blur();
}
else if (this.enableNaturalLanguage) {
const resolved = this.naturalLanguageParserService.parse(value);
if (resolved) {
this.clearValidationError();
if (resolved instanceof Date) {
this.selectedDate = resolved;
this.emitValue(resolved);
this.naturalLanguageResolved.emit(resolved);
}
else {
this.startDate = resolved.start;
this.endDate = resolved.end;
this.emitValue(resolved);
this.naturalLanguageResolved.emit(resolved);
}
this.typedInputValue = this.displayValue;
this.generateCalendar();
this.showNaturalLanguagePreview = false;
input.blur();
}
else {
this.typedInputValue = this.displayValue;
this.showNaturalLanguagePreview = false;
this.scheduleChangeDetection();
}
}
else {
this.typedInputValue = this.displayValue;
this.showNaturalLanguagePreview = false;
this.scheduleChangeDetection();
}
}
else if (keyboardEvent.key === 'Escape') {
this.typedInputValue = this.displayValue;
this.showNaturalLanguagePreview = false;
const input = keyboardEvent.target;
input.blur();
this.scheduleChangeDetection();
}
}
/**
* 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.
*/
effectiveInputMaskPattern() {
if (typeof this.inputMask === 'string' && this.inputMask.length > 0) {
return this.inputMask;
}
if (this.inputMask === true || this.inputMask === '') {
return this.displayFormat ?? 'MM/DD/YYYY';
}
return this.displayFormat ?? null;
}
applyInputMask(value, format) {
const digits = value.replaceAll(/[^\d]/g, '');
let masked = '';
let digitIndex = 0;
let i = 0;
while (i < format.length && digitIndex < digits.length) {
if (format.substring(i, i + 4) === 'YYYY') {
const yearDigits = digits.substring(digitIndex, digitIndex + 4);
masked += yearDigits;
digitIndex += yearDigits.length;
i += 4;
}
else if (format.substring(i, i + 2) === 'YY' ||
format.substring(i, i + 2) === 'MM' ||
format.substring(i, i + 2) === 'DD' ||
format.substring(i, i + 2) === 'HH' ||
format.substring(i, i + 2) === 'hh' ||
format.substring(i, i + 2) === 'mm' ||
format.substring(i, i + 2) === 'ss') {
const tokenDigits = digits.substring(digitIndex, digitIndex + 2);
masked += tokenDigits;
digitIndex += tokenDigits.length;
i += 2;
}
else {
masked += format[i];
i++;
}
}
return masked;
}
isValidDate(date) {
if (!date || Number.isNaN(date.getTime()))
return false;
if (this._minDate && date < this._minDate)
return false;
if (this._maxDate && date > this._maxDate)
return false;
if (this.isDateDisabledMemo(date))
return false;
if (this.isInvalidDate(date))
return false;
return true;
}
applyTypedDate(date) {
if (!date || Number.isNaN(date.getTime()))
return;
if (this.showTime || this.timeOnly) {
const now = new Date();
date.setHours(now.getHours(), now.getMinutes(), now.getSeconds(), now.getMilliseconds());
}
else {
date.setHours(0, 0, 0, 0);
}
if (this.mode === 'single') {
this.selectedDate = date;
this.currentDate = new Date(date);
this._currentMonth = date.getMonth();
this._currentYear = date.getFullYear();
this._currentMonthSignal.set(date.getMonth());
this._currentYearSignal.set(date.getFullYear());
this.emitValue(date);
this.generateCalendar();
this.action.emit({ type: 'dateSelected', payload: date });
}
else if (this.mode === 'range') {
this.startDate = date;
this.currentDate = new Date(date);
this._currentMonth = date.getMonth();
this._currentYear = date.getFullYear();
this._currentMonthSignal.set(date.getMonth());
this._currentYearSignal.set(date.getFullYear());
this.generateCalendar();
this.action.emit({ type: 'rangeStartSelected', payload: date });
}
else if (this.mode === 'multiple') {
const index = this.selectedDates.findIndex((d) => this.isSameDayMemo(d, date));
if (index >= 0) {
this.selectedDates.splice(index, 1);
}
else {
this.selectedDates.push(date);
}
this.generateCalendar();
this.action.emit({
type: 'datesSelected',
payload: [...this.selectedDates],
});
}
this.scheduleChangeDetection();
if (this.shouldAutoClose()) {
this.closeCalendar();
}
}
generateTimeOptions() {
const result = generateTimeOptions(this.minuteInterval, this.secondInterval, this.showSeconds, this.use24Hour);
this.hourOptions = result.hourOptions;
this.minuteOptions = result.minuteOptions;
if (result.secondOptions) {
this.secondOptions = result.secondOptions;
}
}
generateLocaleData() {
// monthOptions is now a computed signal - no need to regenerate
this.firstDayOfWeek = this.weekStart ?? getFirstDayOfWeek(this.locale);
this.weekDays = generateWeekDays(this.locale, this.firstDayOfWeek);
this.weekDaysFull = generateWeekDaysFull(this.locale, this.firstDayOfWeek);
}
_getDefaultPresets() {
const today = this.today || new Date();
const todayStart = getStartOfDay(today);
const todayEnd = getEndOfDay(today);
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);
const last7Days = new Date(today);
last7Days.setDate(last7Days.getDate() - 6);
const last30Days = new Date(today);
last30Days.setDate(last30Days.getDate() - 29);
const startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
const endOfMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0, 23, 59, 59, 999);
const startOfLastMonth = new Date(today.getFullYear(), today.getMonth() - 1, 1);
const endOfLastMonth = new Date(today.getFullYear(), today.getMonth(), 0, 23, 59, 59, 999);
return [
{ key: this.getTranslation('today') || 'Today', value: [todayStart, todayEnd] },
{ key: 'Yesterday', value: [getStartOfDay(yesterday), getEndOfDay(yesterday)] },
{ key: 'Last 7 Days', value: [getStartOfDay(last7Days), todayEnd] },
{ key: 'Last 30 Days', value: [getStartOfDay(last30Days), todayEnd] },
{ key: 'This Month', value: [startOfMonth, endOfMonth] },
{ key: 'Last Month', value: [startOfLastMonth, endOfLastMonth] },
];
}
updateRangesArray() {
let factoryPresets = [];
if (this.rangePresetFactory) {
try {
const dynamicPresets = this.rangePresetFactory(this.today || new Date());
factoryPresets = dynamicPresets.map((preset) => {
const calculated = preset.calculate(this.today || new Date());
return {
key: preset.name,
value: [calculated.start, calculated.end],
};
});
}
catch (err) {
console.error('[ngxsmk-datepicker] Error executing rangePresetFactory:', err);
}
}
const standardPresets = this._ranges ? Object.entries(this._ranges).map(([key, value]) => ({ key, value })) : [];
if (standardPresets.length === 0 && factoryPresets.length === 0 && this.showPresets()) {
this.rangesArray = this._getDefaultPresets();
}
else {
this.rangesArray = [...standardPresets, ...factoryPresets];
}
}
setTimezone(tz) {
if (this.timezone === tz)
return;
this.timezone = tz;
this._updateToday();
this.generateCalendar();
this.timezoneChange.emit(tz);
this.scheduleChangeDetection();
}
checkAndEmitInvalidRange(start, end) {
const disabledDatesInside = [];
const current = new Date(start);
const limitDate = new Date(start);
limitDate.setDate(limitDate.getDate() + 365);
const endChecked = end < limitDate ? end : limitDate;
while (current <= endChecked) {
if (this.isDateDisabled(current)) {
disabledDatesInside.push(new Date(current));
}
current.setDate(current.getDate() + 1);
}
if (disabledDatesInside.length > 0) {
this.invalidRange.emit({ start, end, disabledDatesInside });
return true;
}
return false;
}
updateNaturalLanguagePreview(value) {
if (!this.enableNaturalLanguage || !value) {
this.naturalLanguagePreview = null;
this.showNaturalLanguagePreview = false;
return;
}
const resolved = this.naturalLanguageParserService.parse(value);
if (resolved) {
if (resolved instanceof Date) {
this.naturalLanguagePreview = formatDateWithTimezone(resolved, this.locale, { year: 'numeric', month: 'long', day: 'numeric' }, this.timezone);
}
else {
const start = formatDateWithTimezone(resolved.start, this.locale, { year: 'numeric', month: 'long', day: 'numeric' }, this.timezone);
const end = formatDateWithTimezone(resolved.end, this.locale, { year: 'numeric', month: 'long', day: 'numeric' }, this.timezone);
this.naturalLanguagePreview = `${start} - ${end}`;
}
this.showNaturalLanguagePreview = true;
}
else {
this.naturalLanguagePreview = null;
this.showNaturalLanguagePreview = false;
}
}
async onAiPromptSubmitted(prompt) {
if (!prompt || this.disabled || this.isAiResolving)
return;
this.aiPromptSubmitted.emit(prompt);
this.isAiResolving = true;
this.scheduleChangeDetection();
try {
let resolved = null;
if (this.aiResolver) {
try {
const res = this.aiResolver(prompt);
if (isObservable(res)) {
resolved = await firstValueFrom(res);
}
else {
resolved = await res;
}
}
catch (err) {
if (isDevMode()) {
console.warn('[ngxsmk-datepicker] aiResolver error:', err);
}
}
}
if (!resolved) {
resolved = this.naturalLanguageParserService.parse(prompt);
}
if (resolved) {
if (resolved instanceof Date) {
if (this.mode === 'range') {
this.startDate = new Date(resolved);
this.endDate = new Date(resolved);
this.emitValue({ start: this.startDate, end: this.endDate });
}
else if (this.mode === 'multiple') {
if (!this.selectedDates.some((d) => this.isSameDay(d, resolved))) {
this.selectedDates.push(new Date(resolved));
this.selectedDates.sort((a, b) => a.getTime() - b.getTime());
this.emitValue([...this.selectedDates]);
}
}
else {
this.selectedDate = new Date(resolved);
this.emitValue(this.selectedDate);
}
this.currentMonth = resolved.getMonth();
this.currentYear = resolved.getFullYear();
this.currentDate = new Date(resolved);
this.focusedDate = new Date(resolved);
this.generateCalendar();
this.focusDateCell(resolved);
this.ariaLiveService.announce(`AI selected: ${resolved.toLocaleDateString(this.locale || undefined)}`, 'polite');
}
else if (resolved && typeof resolved === 'object' && 'start' in resolved && 'end' in resolved) {
this.startDate = new Date(resolved.start);
this.endDate = new Date(resolved.end);
this.emitValue({ start: this.startDate, end: this.endDate });
this.currentMonth = this.startDate.getMonth();
this.currentYear = this.startDate.getFullYear();
this.currentDate = new Date(this.startDate);
this.focusedDate = new Date(this.startDate);
this.generateCalendar();
this.focusDateCell(this.startDate);
this.ariaLiveService.announce(`AI selected range: ${this.startDate.toLocaleDateString(this.locale || undefined)} to ${this.endDate.toLocaleDateString(this.locale || undefined)}`, 'polite');
}
}
else {
this.ariaLiveService.announce(`Could not resolve date for prompt: ${prompt}`, 'polite');
}
}
finally {
this.isAiResolving = false;
this.scheduleChangeDetection();
}
}
selectRange(range) {
if (this.disabled)
return;
this.startDate = new Date(range[0]);
this.endDate = new Date(range[1]);
if (this.showTime) {
this.startDate = this.applyCurrentTime(this.startDate);
this.endDate = this.applyCurrentTime(this.endDate);
}
if (this.startDate && this.endDate) {
this.checkAndEmitInvalidRange(this.startDate, this.endDate);
this.emitValue({
start: this.startDate,
end: this.endDate,
});
}
this.currentDate = new Date(this.startDate);
this.initializeValue({ start: this.startDate, end: this.endDate });
this.generateCalendar();
this.action.emit({
type: 'rangeSelected',
payload: {
start: this.startDate,
end: this.endDate,
key: this.rangesArray.find((r) => r.value === range)?.key,
},
});
if (this.shouldAutoClose()) {
this.closeCalendar();
}
else {
this.scheduleChangeDetection();
}
}
isHoliday(date) {
if (!date || !this.holidayProvider)
return false;
const dateOnly = getStartOfDay(date);
return this.holidayProvider.isHoliday(dateOnly);
}
getHolidayLabel(date) {
if (!date || !this.holidayProvider || !this.isHoliday(date))
return null;
return this.holidayProvider.getHolidayLabel
? this.holidayProvider.getHolidayLabel(getStartOfDay(date))
: this.getTranslation('holiday');
}
/**
* 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) {
if (!date)
return false;
const dateOnly = getStartOfDay(date);
if (this._isInDisabledDates(dateOnly))
return true;
if (this._isInDisabledRanges(dateOnly))
return true;
if (this._asyncDisabledTimestamps().has(dateOnly.getTime()))
return true;
if (this.holidayProvider && this.disableHolidays && this.holidayProvider.isHoliday(dateOnly)) {
return true;
}
if (this._isOutOfMinMaxBounds(dateOnly))
return true;
return this.isInvalidDate(date);
}
_isInDisabledDates(dateOnly) {
if (this._disabledDates.length === 0)
return false;
if (this._disabledDatesTimestamps.size === 0) {
this._syncDisabledDatesCache();
}
return this._disabledDatesTimestamps.has(dateOnly.getTime());
}
_isInDisabledRanges(dateOnly) {
if (this._disabledRanges.length === 0)
return false;
if (this._parsedDisabledRanges.length === 0) {
this._syncDisabledDatesCache();
}
const dateTime = dateOnly.getTime();
for (const range of this._parsedDisabledRanges) {
if (dateTime >= range.startTime && dateTime <= range.endTime) {
return true;
}
}
return false;
}
_isOutOfMinMaxBounds(dateOnly) {
const effectiveMinDate = this._minDate || (this.globalConfig?.minDate ? this._normalizeDate(this.globalConfig.minDate) : null);
const effectiveMaxDate = this._maxDate || (this.globalConfig?.maxDate ? this._normalizeDate(this.globalConfig.maxDate) : null);
if (effectiveMinDate && dateOnly.getTime() < getStartOfDay(effectiveMinDate).getTime()) {
return true;
}
if (effectiveMaxDate && dateOnly.getTime() > getStartOfDay(effectiveMaxDate).getTime()) {
return true;
}
return false;
}
/**
* 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) {
if (!d || this.mode !== 'multiple')
return false;
const dTime = getStartOfDay(d).getTime();
return this.selectedDates.some((selected) => getStartOfDay(selected).getTime() === dTime);
}
/**
* 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() {
if (this.disabled)
return;
if (this.timeOnly && this.mode === 'single' && !this.selectedDate) {
const today = new Date();
const dateWithTime = this.applyCurrentTime(today);
this.selectedDate = dateWithTime;
this.emitValue(dateWithTime);
this.action.emit({
type: 'timeChanged',
payload: this.showSeconds
? { hour: this.currentHour, minute: this.currentMinute, second: this.currentSecond }
: { hour: this.currentHour, minute: this.currentMinute },
});
this.scheduleChangeDetection();
return;
}
if (this.mode === 'single' && this.selectedDate) {
this.selectedDate = this.applyCurrentTime(this.selectedDate);
this.emitValue(this.selectedDate);
}
else if (this.mode === 'range' && this.startDate && this.endDate) {
this.startDate = this.applyCurrentTime(this.startDate);
this.endDate = this.applyCurrentTime(this.endDate);
this.emitValue({
start: this.startDate,
end: this.endDate,
});
}
else if (this.mode === 'range' && this.startDate && !this.endDate) {
this.startDate = this.applyCurrentTime(this.startDate);
}
else if (this.mode === 'multiple') {
this.selectedDates = this.selectedDates.map((date) => {
const newDate = getStartOfDay(date);
return this.applyCurrentTime(newDate);
});
this.emitValue([...this.selectedDates]);
}
this.action.emit({
type: 'timeChanged',
payload: this.showSeconds
? { hour: this.currentHour, minute: this.currentMinute, second: this.currentSecond }
: { hour: this.currentHour, minute: this.currentMinute },
});
this.scheduleChangeDetection();
}
/**
* 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() {
if (this.disabled || !this.timeRangeMode())
return;
const today = new Date();
const startDate = new Date(today);
const endDate = new Date(today);
// Set start time
startDate.setHours(this.get24Hour(this.startDisplayHour, this.startIsPm), this.startMinute, this.startSecond, 0);
// Set end time
endDate.setHours(this.get24Hour(this.endDisplayHour, this.endIsPm), this.endMinute, this.endSecond, 0);
// Emit the time range as a range object
this.emitValue({
start: startDate,
end: endDate,
});
this.action.emit({
type: 'timeRangeChanged',
payload: {
startHour: this.get24Hour(this.startDisplayHour, this.startIsPm),
startMinute: this.startMinute,
endHour: this.get24Hour(this.endDisplayHour, this.endIsPm),
endMinute: this.endMinute,
},
});
this.scheduleChangeDetection();
}
/**
* 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) {
if (!day || this.disabled)
return;
if (this._shouldSkipDueToTouchGuard())
return;
this.touchState.isDateCellTouching = false;
this.touchState.dateCellTouchStartTime = 0;
this.touchState.dateCellTouchStartDate = null;
this.touchState.lastDateCellTouchDate = null;
if (this.isDateDisabled(day))
return;
if (this.hooks?.beforeDateSelect && !this.hooks.beforeDateSelect(day, this._value)) {
return;
}
if (this.mode === 'single') {
this._handleSingleModeClick(day);
}
else if (this.mode === 'range') {
this._handleRangeModeClick(day);
}
else if (this.mode === 'week' || this.mode === 'month' || this.mode === 'quarter' || this.mode === 'year') {
this._handlePeriodModeClick(day);
}
else if (this.mode === 'multiple') {
this._handleMultipleModeClick(day);
}
this._syncTimeAfterDateClick();
if (this.hooks?.afterDateSelect) {
this.hooks.afterDateSelect(day, this._value);
}
this.action.emit({
type: 'dateSelected',
payload: { mode: this.mode, value: this._value, date: day },
});
if (this.shouldAutoClose()) {
this.closeCalendar();
}
else {
this.scheduleChangeDetection();
}
}
/**
* 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).
*/
_shouldSkipDueToTouchGuard() {
const now = Date.now();
const timeSinceTouchHandled = this.dateCellTouchHandledTime > 0 ? now - this.dateCellTouchHandledTime : Infinity;
if (this.touchState.dateCellTouchHandled && timeSinceTouchHandled < 250 && this.touchState.isDateCellTouching) {
this.clearTouchHandledFlag();
return true;
}
if (this.touchState.dateCellTouchHandled && (timeSinceTouchHandled >= 250 || !this.touchState.isDateCellTouching)) {
this.clearTouchHandledFlag();
}
return false;
}
_navigateToMonthOfDay(day) {
if (!this.changeActiveMonthOnSelection)
return;
if (this.isCurrentMonth(day))
return;
this._currentMonth = day.getMonth();
this._currentYear = day.getFullYear();
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this.currentDate = new Date(day);
this._invalidateMemoCache();
this.generateCalendar();
this.scheduleChangeDetection();
if (this.isBrowser && this.isCalendarOpen) {
this.cdr.markForCheck();
this.trackedDoubleRequestAnimationFrame(() => {
this.trackedSetTimeout(() => {
this.setupPassiveTouchListeners();
}, 50);
});
}
}
_handleSingleModeClick(day) {
this._navigateToMonthOfDay(day);
const dateWithTime = this.applyTimeIfNeeded(day);
this.selectedDate = dateWithTime;
this.emitValue(dateWithTime);
const formattedDate = formatDateWithTimezone(dateWithTime, this.locale, { year: 'numeric', month: 'long', day: 'numeric' }, this.timezone);
const msg = this.getTranslation('dateSelected', undefined, { date: formattedDate }) || formattedDate;
this.ariaLiveService.announce(msg, 'polite');
}
_handleRangeModeClick(day) {
this._navigateToMonthOfDay(day);
const dayTime = getStartOfDay(day).getTime();
const startTime = this.startDate ? getStartOfDay(this.startDate).getTime() : null;
const endTime = this.endDate ? getStartOfDay(this.endDate).getTime() : null;
if (this.startDate && this.endDate && dayTime === startTime) {
this.endDate = null;
this.hoveredDate = null;
this._invalidateMemoCache();
this.emitValue({ start: this.startDate, end: null });
this.scheduleChangeDetection();
}
else if (this.startDate && this.endDate && dayTime === endTime) {
this.startDate = this.applyTimeIfNeeded(day);
this.endDate = null;
this.hoveredDate = null;
this._invalidateMemoCache();
this.emitValue({ start: this.startDate, end: null });
this.scheduleChangeDetection();
}
else if ((this.startDate && this.endDate && dayTime > startTime && dayTime < endTime) ||
!this.startDate ||
(this.startDate && this.endDate)) {
this.startDate = this.applyTimeIfNeeded(day);
this.endDate = null;
this.hoveredDate = null;
this._invalidateMemoCache();
this.scheduleChangeDetection();
const startFormatted = formatDateWithTimezone(this.startDate, this.locale, { year: 'numeric', month: 'long', day: 'numeric' }, this.timezone);
const msg = this.getTranslation('startDateSelected', undefined, { date: startFormatted }) ||
`Start date set to ${startFormatted}. Select end date.`;
this.ariaLiveService.announce(msg, 'polite');
}
else if (this.startDate && !this.endDate) {
this._handleRangeEndSelection(day, dayTime, startTime);
}
this.hoveredDate = null;
}
_handleRangeEndSelection(day, dayTime, startTime) {
if (dayTime < startTime) {
this.startDate = this.applyTimeIfNeeded(day);
this.endDate = null;
this.hoveredDate = null;
this._invalidateMemoCache();
this.scheduleChangeDetection();
return;
}
if (dayTime === startTime) {
if (this._tryCompleteSameDayRange()) {
this._announceRangeSelected();
}
return;
}
const potentialEndDate = this.applyTimeIfNeeded(day);
if (this.hooks?.validateRange && !this.hooks.validateRange(this.startDate, potentialEndDate)) {
this.startDate = potentialEndDate;
this.endDate = null;
this.hoveredDate = null;
this._invalidateMemoCache();
this.scheduleChangeDetection();
return;
}
this.endDate = potentialEndDate;
this.hoveredDate = null;
this._invalidateMemoCache();
this.checkAndEmitInvalidRange(this.startDate, this.endDate);
this.emitValue({ start: this.startDate, end: this.endDate });
this._announceRangeSelected();
}
/** Completes range with end equal to start when `allowSameDay` is enabled. */
_tryCompleteSameDayRange() {
if (!this.allowSameDay() || this.mode !== 'range' || !this.startDate || this.endDate) {
return false;
}
const endSame = new Date(this.startDate);
if (this.hooks?.validateRange && !this.hooks.validateRange(this.startDate, endSame)) {
return false;
}
this.endDate = endSame;
this.hoveredDate = null;
this._invalidateMemoCache();
this.checkAndEmitInvalidRange(this.startDate, this.endDate);
this.emitValue({ start: this.startDate, end: this.endDate });
return true;
}
_announceRangeSelected() {
if (!this.startDate || !this.endDate)
return;
const startFormatted = formatDateWithTimezone(this.startDate, this.locale, { year: 'numeric', month: 'long', day: 'numeric' }, this.timezone);
const endFormatted = formatDateWithTimezone(this.endDate, this.locale, { year: 'numeric', month: 'long', day: 'numeric' }, this.timezone);
const msg = this.getTranslation('rangeSelected', undefined, {
start: startFormatted,
end: endFormatted,
}) || `${startFormatted} to ${endFormatted}`;
this.ariaLiveService.announce(msg, 'polite');
}
_finalizeSameDayRangeOnClose() {
if (this._tryCompleteSameDayRange()) {
this._announceRangeSelected();
}
}
_handlePeriodModeClick(day) {
if (this.mode === 'week') {
const weekStart = getStartOfWeek(day, this.firstDayOfWeek);
const weekEnd = getEndOfWeek(day, this.firstDayOfWeek);
this.startDate = weekStart;
this.endDate = weekEnd;
this._invalidateMemoCache();
this.emitValue({ start: weekStart, end: weekEnd });
const sf = formatDateWithTimezone(weekStart, this.locale, { year: 'numeric', month: 'long', day: 'numeric' }, this.timezone);
const ef = formatDateWithTimezone(weekEnd, this.locale, { year: 'numeric', month: 'long', day: 'numeric' }, this.timezone);
this.ariaLiveService.announce(`Week selected: ${sf} to ${ef}`, 'polite');
}
else if (this.mode === 'month') {
const monthStart = new Date(day.getFullYear(), day.getMonth(), 1);
const monthEnd = new Date(day.getFullYear(), day.getMonth() + 1, 0);
monthEnd.setHours(23, 59, 59, 999);
this.startDate = monthStart;
this.endDate = monthEnd;
this._invalidateMemoCache();
this.emitValue({ start: monthStart, end: monthEnd });
const mf = formatDateWithTimezone(monthStart, this.locale, { year: 'numeric', month: 'long' }, this.timezone);
this.ariaLiveService.announce(`Month selected: ${mf}`, 'polite');
}
else if (this.mode === 'quarter') {
const quarter = Math.floor(day.getMonth() / 3);
const quarterStart = new Date(day.getFullYear(), quarter * 3, 1);
const quarterEnd = new Date(day.getFullYear(), (quarter + 1) * 3, 0);
quarterEnd.setHours(23, 59, 59, 999);
this.startDate = quarterStart;
this.endDate = quarterEnd;
this._invalidateMemoCache();
this.emitValue({ start: quarterStart, end: quarterEnd });
this.ariaLiveService.announce(`Quarter selected: Q${quarter + 1} ${day.getFullYear()}`, 'polite');
}
else if (this.mode === 'year') {
const yearStart = new Date(day.getFullYear(), 0, 1);
const yearEnd = new Date(day.getFullYear(), 11, 31);
yearEnd.setHours(23, 59, 59, 999);
this.startDate = yearStart;
this.endDate = yearEnd;
this._invalidateMemoCache();
this.emitValue({ start: yearStart, end: yearEnd });
this.ariaLiveService.announce(`Year selected: ${day.getFullYear()}`, 'polite');
}
}
_handleMultipleModeClick(day) {
this._navigateToMonthOfDay(day);
if (this.recurringPattern) {
this._applyRecurringPattern();
}
else {
const existingIndex = this.selectedDates.findIndex((d) => this.isSameDay(d, day));
if (existingIndex > -1) {
this.selectedDates.splice(existingIndex, 1);
}
else {
const dateWithTime = this.applyTimeIfNeeded(day);
this.selectedDates.push(dateWithTime);
this.selectedDates.sort((a, b) => a.getTime() - b.getTime());
}
this.emitValue([...this.selectedDates]);
}
}
_applyRecurringPattern() {
const config = {
pattern: this.recurringPattern.pattern,
startDate: this.recurringPattern.startDate,
interval: this.recurringPattern.interval || 1,
...(this.recurringPattern.endDate !== undefined && { endDate: this.recurringPattern.endDate }),
...(this.recurringPattern.dayOfWeek !== undefined && { dayOfWeek: this.recurringPattern.dayOfWeek }),
...(this.recurringPattern.dayOfMonth !== undefined && { dayOfMonth: this.recurringPattern.dayOfMonth }),
};
const recurringDates = generateRecurringDates(config);
const datesWithTime = recurringDates.map((d) => this.applyTimeIfNeeded(d));
const uniqueDates = new Map();
datesWithTime.forEach((d) => uniqueDates.set(getStartOfDay(d).getTime(), d));
this.selectedDates = Array.from(uniqueDates.values()).sort((a, b) => a.getTime() - b.getTime());
this.emitValue([...this.selectedDates]);
}
_syncTimeAfterDateClick() {
let dateToSync = null;
if (this.mode === 'single') {
dateToSync = this.selectedDate;
}
else if (['range', 'week', 'month', 'quarter', 'year'].includes(this.mode)) {
dateToSync = this.startDate;
}
else if (this.mode === 'multiple' && this.selectedDates.length > 0) {
dateToSync = this.selectedDates.at(-1) ?? null;
}
if (dateToSync) {
this.update12HourState(dateToSync.getHours());
this.currentMinute = dateToSync.getMinutes();
if (this.showSeconds) {
this.currentSecond = Math.min(59, Math.floor(dateToSync.getSeconds() / this.secondInterval) * this.secondInterval);
}
}
}
onDateHover(day) {
if (this.mode === 'range' && this.startDate && !this.endDate && day) {
this.hoveredDate = day;
this.cdr.markForCheck();
}
}
onDateCellTouchStart(event, day) {
this.clearTouchHandledFlag();
this.touchService.handleDateCellTouchStart(event, day, this.touchState, {
disabled: this.disabled,
mode: this.mode,
swipeThreshold: this.SWIPE_THRESHOLD,
swipeTimeThreshold: this.SWIPE_TIME_THRESHOLD,
}, {
isDateDisabled: (d) => this.isDateDisabled(d),
onDateClick: (_) => { },
changeMonth: (_) => { },
onStateChanged: () => this.cdr.markForCheck(),
onHoverChanged: (d) => {
this.hoveredDate = d;
this.cdr.markForCheck();
},
});
// Manual range hover update for start
if (this.touchState.isDateCellTouching && this.mode === 'range' && day) {
if (this.startDate && !this.endDate) {
const dayTime = getStartOfDay(day).getTime();
const startTime = getStartOfDay(this.startDate).getTime();
if (dayTime >= startTime) {
this.hoveredDate = day;
this.cdr.markForCheck();
}
}
else if (!this.startDate) {
this.hoveredDate = null;
}
}
}
onDateCellTouchMove(event) {
this.touchService.handleDateCellTouchMove(event, this.touchState, {
disabled: this.disabled,
mode: this.mode,
swipeThreshold: this.SWIPE_THRESHOLD,
swipeTimeThreshold: this.SWIPE_TIME_THRESHOLD,
}, {
isDateDisabled: (d) => this.isDateDisabled(d),
onDateClick: (_) => { },
changeMonth: (_) => { },
onStateChanged: () => this.cdr.detectChanges(),
onHoverChanged: (d) => {
this.hoveredDate = d;
this.cdr.detectChanges();
},
}, this.startDate);
}
onDateCellTouchEnd(event, day) {
this.touchService.handleDateCellTouchEnd(event, day, this.touchState, {
disabled: this.disabled,
mode: this.mode,
swipeThreshold: this.SWIPE_THRESHOLD,
swipeTimeThreshold: this.SWIPE_TIME_THRESHOLD,
}, {
isDateDisabled: (d) => this.isDateDisabled(d),
onDateClick: (d) => {
// We need to set handled flag for debounce
this.setTouchHandledFlag();
this.onDateClick(d);
},
changeMonth: (_) => { },
onStateChanged: () => this.cdr.markForCheck(),
onHoverChanged: (d) => {
this.hoveredDate = d;
this.cdr.markForCheck();
},
});
}
isPreviewInRange(day) {
if (this.mode !== 'range' || !this.startDate || this.endDate || !this.hoveredDate || !day)
return false;
const start = getStartOfDay(this.startDate).getTime();
const end = getStartOfDay(this.hoveredDate).getTime();
const time = getStartOfDay(day).getTime();
return time > Math.min(start, end) && time < Math.max(start, end);
}
buildCalendarMonths(baseYear, baseMonth, count) {
if (this.syncScroll()?.enabled && count > 1) {
const monthGap = this.syncScroll().monthGap || 1;
const months = [];
for (let i = 0; i < count; i++) {
const offset = i * monthGap;
let targetMonth = baseMonth + offset;
let targetYear = baseYear;
while (targetMonth >= 12) {
targetMonth -= 12;
targetYear += 1;
}
while (targetMonth < 0) {
targetMonth += 12;
targetYear -= 1;
}
const days = this.calendarGenerationService.generateMonthDays(targetYear, targetMonth, this.firstDayOfWeek, this._normalizeDate.bind(this));
months.push({ month: targetMonth, year: targetYear, days });
}
return months;
}
return this.calendarGenerationService.generateMultipleMonths(baseYear, baseMonth, count, this.firstDayOfWeek, this._normalizeDate.bind(this));
}
/**
* 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() {
if (this.isCalendarOpen || this.isInlineMode) {
const loadingMsg = this.getTranslation('calendarLoading') || 'Loading calendar...';
this.ariaLiveService.announce(loadingMsg, 'polite');
}
this.daysInMonth = [];
this.multiCalendarMonths = [];
const count = Math.max(1, Math.min(this.calendarCount || 1, 12));
const baseYear = this.currentDate.getFullYear();
const baseMonth = this.currentDate.getMonth();
this._currentMonth = baseMonth;
this._currentYear = baseYear;
this._currentDecade = Math.floor(baseYear / 10) * 10;
this._currentMonthSignal.set(baseMonth);
this._currentYearSignal.set(baseYear);
this.generateDropdownOptions();
const months = this.buildCalendarMonths(baseYear, baseMonth, count);
this.multiCalendarMonths = months;
this._multiCalendarDataRevision.update((r) => r + 1);
if (months.length > 0 && months[0]) {
this.daysInMonth = months[0].days;
}
this.preloadAdjacentMonths(baseYear, baseMonth);
this.cdr.markForCheck();
// Announce calendar ready state for screen readers
if (this.isCalendarOpen || this.isInlineMode) {
const readyMsg = this.getTranslation('calendarReady') || 'Calendar ready';
this.ariaLiveService.announce(readyMsg, 'polite');
}
this.action.emit({
type: 'calendarGenerated',
payload: {
month: baseMonth,
year: baseYear,
days: this.daysInMonth.filter((d) => d !== null),
multiCalendar: this.multiCalendarMonths.map((m) => ({
month: m.month,
year: m.year,
days: m.days.filter((d) => d !== null),
})),
},
});
if (this.isBrowser) {
this.trackedRequestAnimationFrame(() => {
this.setupPassiveTouchListeners();
});
}
}
/**
* 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)
*/
preloadAdjacentMonths(currentYear, currentMonth) {
this.calendarGenerationService.preloadAdjacentMonths(currentYear, currentMonth, this.firstDayOfWeek, this._normalizeDate.bind(this));
}
generateDropdownOptions() {
// yearOptions is now a computed signal - no need to regenerate
}
generateYearGrid() {
this.yearGrid = this.calendarGenerationService.getYearGrid(this._currentYear);
}
generateDecadeGrid() {
this.decadeGrid = this.calendarGenerationService.getDecadeGrid(this._currentDecade);
}
onYearClick(year) {
if (this.disabled || this.isYearDisabled(year))
return;
this.currentYear = year;
this.generateYearGrid();
if (this.mode === 'year') {
const yearStart = new Date(year, 0, 1);
this.onDateClick(yearStart);
return;
}
const wasInYearView = this.calendarViewMode === 'year';
if (wasInYearView) {
this.calendarViewMode = 'month';
}
this.scheduleChangeDetection();
if (wasInYearView && this.isBrowser && this.isCalendarOpen) {
this.trackedSetTimeout(() => {
this.setupPassiveTouchListeners();
}, 50);
}
}
onDecadeClick(decade) {
if (this.disabled || this.isDecadeDisabled(decade))
return;
this._currentDecade = decade;
this.currentYear = decade;
if (this.calendarViewMode === 'decade') {
this.calendarViewMode = 'year';
}
this.generateDecadeGrid();
this.generateYearGrid();
this.scheduleChangeDetection();
}
/**
* 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) {
if (this.disabled)
return;
this._currentDecade += delta * 10;
this.generateDecadeGrid();
this.cdr.markForCheck();
}
/**
* 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) {
if (this.disabled)
return;
this.currentYear = this._currentYear + delta;
this.generateYearGrid();
this.scheduleChangeDetection();
if (this.isBrowser && this.isCalendarOpen && this.calendarViewMode === 'month') {
this.trackedSetTimeout(() => {
this.setupPassiveTouchListeners();
}, 50);
}
const yearChangedMsg = this.getTranslation('yearChanged', undefined, {
year: String(this._currentYear),
}) || `Year ${this._currentYear}`;
this.ariaLiveService.announce(yearChangedMsg, 'polite');
}
onViewModeChange(mode) {
this.calendarViewMode = mode;
}
onYearSelectChange(year) {
const yearValue = typeof year === 'number' ? year : Number(year);
if (Number.isNaN(yearValue) || this.isYearDisabled(yearValue))
return;
this.currentYear = yearValue;
this.generateYearGrid();
if (this.mode === 'year') {
const yearStart = new Date(yearValue, 0, 1);
this.onDateClick(yearStart);
}
}
generateTimeline() {
if (this.mode !== 'range')
return;
const result = this.calendarGenerationService.getTimelineMonths(this.timelineZoomLevel);
this.timelineStartDate = result.timelineStartDate;
this.timelineEndDate = result.timelineEndDate;
this.timelineMonths = result.timelineMonths;
this.scheduleChangeDetection();
}
timelineZoomIn() {
if (this.timelineZoomLevel < 5) {
this.timelineZoomLevel++;
this.generateTimeline();
}
}
timelineZoomOut() {
if (this.timelineZoomLevel > 1) {
this.timelineZoomLevel--;
this.generateTimeline();
}
}
isTimelineMonthSelected(month) {
if (!this.startDate || !this.endDate)
return false;
const monthStart = new Date(month.getFullYear(), month.getMonth(), 1);
const monthEnd = new Date(month.getFullYear(), month.getMonth() + 1, 0);
const rangeStart = getStartOfDay(this.startDate);
const rangeEnd = getStartOfDay(this.endDate);
return ((monthStart >= rangeStart && monthStart <= rangeEnd) ||
(monthEnd >= rangeStart && monthEnd <= rangeEnd) ||
(monthStart <= rangeStart && monthEnd >= rangeEnd));
}
onTimelineMonthClick(month) {
if (this.disabled)
return;
const monthStart = new Date(month.getFullYear(), month.getMonth(), 1);
const monthEnd = new Date(month.getFullYear(), month.getMonth() + 1, 0);
if (this.startDate) {
if (this.endDate) {
this.startDate = monthStart;
this.endDate = monthEnd;
this.emitValue({ start: this.startDate, end: this.endDate });
}
else {
if (monthStart < this.startDate) {
this.endDate = this.startDate;
this.startDate = monthStart;
}
else {
this.endDate = monthEnd;
}
this.emitValue({ start: this.startDate, end: this.endDate });
}
}
else {
this.startDate = monthStart;
}
this.cdr.markForCheck();
}
formatTimeSliderValue(minutes) {
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
const displayHours = hours % 12 || 12;
const ampm = hours >= 12 ? 'PM' : 'AM';
return `${displayHours}:${mins.toString().padStart(2, '0')} ${ampm}`;
}
onStartTimeSliderChange(minutes) {
if (this.disabled)
return;
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
if (this.startDate) {
this.startDate.setHours(hours, mins, 0, 0);
if (this.endDate && this.startDate > this.endDate) {
this.endDate.setHours(hours, mins, 0, 0);
}
if (this.startDate && this.endDate) {
this.emitValue({ start: this.startDate, end: this.endDate });
}
}
this.cdr.markForCheck();
}
onEndTimeSliderChange(minutes) {
if (this.disabled)
return;
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
if (this.endDate) {
this.endDate.setHours(hours, mins, 0, 0);
if (this.startDate && this.endDate < this.startDate) {
this.startDate.setHours(hours, mins, 0, 0);
}
if (this.startDate && this.endDate) {
this.emitValue({ start: this.startDate, end: this.endDate });
}
}
this.cdr.markForCheck();
}
onCalendarSwipeStart(event) {
this.touchService.handleCalendarSwipeStart(event, this.touchState);
}
onCalendarSwipeMove(event) {
this.touchService.handleCalendarSwipeMove(event, this.touchState);
}
onCalendarSwipeEnd(event) {
this.touchService.handleCalendarSwipeEnd(event, this.touchState, {
disabled: this.disabled,
mode: this.mode,
swipeThreshold: this.SWIPE_THRESHOLD,
swipeTimeThreshold: this.SWIPE_TIME_THRESHOLD,
}, {
isDateDisabled: (d) => this.isDateDisabled(d),
onDateClick: (_) => { },
changeMonth: (delta) => {
if (!this.isBackArrowDisabled || delta > 0) {
this.changeMonth(delta);
}
},
changeYear: (delta) => {
this._currentYear += delta;
this._currentYearSignal.set(this._currentYear);
this.generateCalendar();
},
onStateChanged: () => this.cdr.markForCheck(),
onHoverChanged: (_) => { },
});
}
changeMonth(delta) {
if (this.disabled)
return;
if (delta < 0 && this.isBackArrowDisabled)
return;
this.clearTouchHandledFlag();
this.touchState.isDateCellTouching = false;
this.touchState.dateCellTouchStartTime = 0;
this.touchState.dateCellTouchStartDate = null;
this.touchState.lastDateCellTouchDate = null;
// Calculate from the 1st of the month to avoid skipping months when current date is 31st (e.g., Jan 31 + 1m -> Mar)
const currentMonthStart = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth(), 1);
const newDate = addMonths(currentMonthStart, delta);
this.currentDate = newDate;
this._currentMonth = newDate.getMonth();
this._currentYear = newDate.getFullYear();
this._currentMonthSignal.set(this._currentMonth);
this._currentYearSignal.set(this._currentYear);
this._invalidateMemoCache();
if (this.focusedDate) {
const lastDayOfNewMonth = new Date(this._currentYear, this._currentMonth + 1, 0).getDate();
const clampedDay = Math.min(this.focusedDate.getDate(), lastDayOfNewMonth);
let targetFocused = new Date(this._currentYear, this._currentMonth, clampedDay);
if (!this.isDateValid(targetFocused)) {
targetFocused = this.findFirstValidDateInMonth(this._currentYear, this._currentMonth) ?? targetFocused;
}
this.focusedDate = targetFocused;
}
this.generateCalendar();
if (this.isBrowser && this.isCalendarOpen) {
this.trackedRequestAnimationFrame(() => {
this.trackedRequestAnimationFrame(() => {
this.trackedSetTimeout(() => {
this.setupPassiveTouchListeners();
}, 50);
});
});
}
const monthName = newDate.toLocaleDateString(this.locale, {
month: 'long',
});
const year = newDate.getFullYear();
let monthChangedMsg;
if (this.calendars > 1) {
const endMonthDate = new Date(year, newDate.getMonth() + this.calendars - 1, 1);
const endMonthName = endMonthDate.toLocaleDateString(this.locale, { month: 'long' });
const endYear = endMonthDate.getFullYear();
monthChangedMsg = `${monthName} ${year} – ${endMonthName} ${endYear}`;
}
else {
monthChangedMsg =
this.getTranslation('monthChanged', undefined, {
month: monthName,
year: String(year),
}) || `${monthName} ${year}`;
}
this.ariaLiveService.announce(monthChangedMsg, 'polite');
this.action.emit({ type: 'monthChanged', payload: { delta: delta } });
}
isSameDay(d1, d2) {
return this.dateComparator(d1, d2);
}
isCurrentMonth(day) {
if (!day)
return false;
return day.getMonth() === this._currentMonth && day.getFullYear() === this._currentYear;
}
isInRange(d) {
if (!d || !this.startDate || !this.endDate)
return false;
const dTime = getStartOfDay(d).getTime();
const startDayTime = getStartOfDay(this.startDate).getTime();
const endDayTime = getStartOfDay(this.endDate).getTime();
const startTime = Math.min(startDayTime, endDayTime);
const endTime = Math.max(startDayTime, endDayTime);
return dTime > startTime && dTime < endTime;
}
/**
* Whether `d` falls within the optional {@link comparisonRange} (inclusive of both
* endpoints, since the comparison range has no distinct start/end cells).
*/
isInComparisonRange(d) {
const start = this.comparisonRange()?.[0];
const end = this.comparisonRange()?.[1];
if (!d || !start || !end)
return false;
const dTime = getStartOfDay(d).getTime();
const startDayTime = getStartOfDay(start).getTime();
const endDayTime = getStartOfDay(end).getTime();
const startTime = Math.min(startDayTime, endDayTime);
const endTime = Math.max(startDayTime, endDayTime);
return dTime >= startTime && dTime <= endTime;
}
applyGlobalConfig() {
if (!this.globalConfig)
return;
this.applyGlobalConfigDefaults();
this.applyGlobalConfigLocaleAndDates();
this.applyGlobalConfigMobile();
}
applyGlobalConfigDefaults() {
const g = this.globalConfig;
if (this.weekStart === null && g.weekStart !== undefined) {
this.weekStart = g.weekStart;
}
if (this.minuteInterval === 1 && g.minuteInterval !== undefined) {
this.minuteInterval = g.minuteInterval;
}
if (this.holidayProvider === null && g.holidayProvider !== undefined) {
this.holidayProvider = g.holidayProvider;
}
if (this.yearRange === 10 && g.yearRange !== undefined) {
this.yearRange = g.yearRange;
}
}
applyGlobalConfigLocaleAndDates() {
const g = this.globalConfig;
if (this._locale === 'en-US' && g.locale) {
this._locale = g.locale;
}
if (!this.timezone && g.timezone) {
this.timezone = g.timezone;
}
if (!this._minDate && g.minDate !== undefined) {
this._minDate = this._normalizeDate(g.minDate);
}
if (!this._maxDate && g.maxDate !== undefined) {
this._maxDate = this._normalizeDate(g.maxDate);
}
}
applyGlobalConfigMobile() {
const g = this.globalConfig;
if (this.autoDetectMobile === true && g.autoDetectMobile !== undefined) {
this.autoDetectMobile = g.autoDetectMobile;
}
if (this.mobileModalStyle === 'center' && g.mobileModalStyle !== undefined) {
this.mobileModalStyle = g.mobileModalStyle;
}
if (this.responsive === true && g.responsive !== undefined) {
this.responsive = g.responsive;
}
}
/**
* Apply animation configuration from global config
*/
applyAnimationConfig(config) {
if (!this.isBrowser || !this.elementRef?.nativeElement)
return;
const nativeElement = this.elementRef?.nativeElement;
if (!nativeElement)
return;
const animationConfig = config || this.globalConfig?.animations || DEFAULT_ANIMATION_CONFIG;
let prefersReducedMotion = false;
if (animationConfig.respectReducedMotion) {
try {
const mediaQuery = globalThis.matchMedia('(prefers-reduced-motion: reduce)');
prefersReducedMotion = mediaQuery?.matches ?? false;
}
catch {
prefersReducedMotion = false;
}
}
if (!animationConfig.enabled || prefersReducedMotion) {
nativeElement.style.setProperty('--datepicker-transition-duration', '0ms');
nativeElement.style.setProperty('--datepicker-transition', 'none');
return;
}
const duration = `${animationConfig.duration || DEFAULT_ANIMATION_CONFIG.duration}ms`;
const easing = animationConfig.easing || DEFAULT_ANIMATION_CONFIG.easing;
const property = animationConfig.property || DEFAULT_ANIMATION_CONFIG.property;
nativeElement.style.setProperty('--datepicker-transition-duration', duration);
nativeElement.style.setProperty('--datepicker-transition-easing', easing);
nativeElement.style.setProperty('--datepicker-transition-property', property);
nativeElement.style.setProperty('--datepicker-transition', `${property} ${duration} ${easing}`);
}
/**
* Initialize translations from service or registry
*/
initializeTranslations() {
if (this.translationService) {
this._translationService = this.translationService;
return;
}
if (this.translationRegistry && this._locale) {
const defaultTranslations = this.translationRegistry.getTranslations(this._locale);
if (this.translations) {
this._translations = { ...defaultTranslations, ...this.translations };
}
else {
this._translations = defaultTranslations;
}
}
}
/**
* 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() {
if (!this.currentDate || !this.locale)
return '';
const month = this.currentDate.toLocaleDateString(this.locale, {
month: 'long',
});
const year = this.currentDate.getFullYear();
return this.getTranslation('calendarFor', undefined, {
month,
year: String(year),
});
}
/**
* 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, year) {
if (!this.locale)
return '';
const monthName = new Date(year, month, 1).toLocaleDateString(this.locale, {
month: 'long',
});
return this.getTranslation('calendarFor', undefined, {
month: monthName,
year: String(year),
});
}
/**
* 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.
*/
setupLazyLoadingObserver() {
if (!this.isBrowser || this.calendarCount <= 1) {
return;
}
try {
// Only setup observer if not already done
if (!('IntersectionObserver' in globalThis)) {
return; // IntersectionObserver not supported
}
const multiCalendarContainer = this.elementRef?.nativeElement?.querySelector('.ngxsmk-multi-calendar-container');
if (!multiCalendarContainer) {
// Will retry in ngAfterViewInit
return;
}
// Replace any observer from a previous setup before creating a new one
this.lazyLoadingObserver?.disconnect();
// Create observer to track calendar month visibility
const observer = new IntersectionObserver((entries) => {
const visibleIndices = new Set();
entries.forEach((entry) => {
const calendarMonth = entry.target;
const indexAttr = calendarMonth.dataset['calendarIndex'];
if (indexAttr !== undefined && entry.isIntersecting) {
const index = Number.parseInt(indexAttr, 10);
if (!Number.isNaN(index)) {
visibleIndices.add(index);
}
}
});
// Update signal if there are visible calendars
if (visibleIndices.size > 0) {
this._visibleCalendarIndicesSignal.set(visibleIndices);
}
}, {
root: multiCalendarContainer,
threshold: 0.01, // Consider visible if even 1% is shown
});
// Observe all calendar month elements
const calendarMonths = multiCalendarContainer.querySelectorAll('.ngxsmk-calendar-month-multi');
calendarMonths.forEach((month) => {
observer.observe(month);
});
// Store observer for cleanup in ngOnDestroy
this.lazyLoadingObserver = observer;
}
catch (error) {
if (isDevMode()) {
console.warn('[ngxsmk-datepicker] Could not setup lazy loading observer:', error);
}
}
}
/**
* 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, year) {
if (!this.locale)
return '';
const monthName = new Date(year, month, 1).toLocaleDateString(this.locale, {
month: 'long',
});
return `${monthName} ${year}`;
}
isCurrentMonthForCalendar(day, targetMonth, targetYear) {
if (!day)
return false;
return day.getMonth() === targetMonth && day.getFullYear() === targetYear;
}
getTranslation(key, fallbackKey, params) {
if (this._translationService) {
return this._translationService.translate(key, params);
}
if (this._translations) {
// Use optional chaining and nullish coalescing for safer access
const raw = this._translations[key] ?? null;
const translation = typeof raw === 'string' ? raw : null;
let fallbackTranslation = null;
if (!translation && fallbackKey) {
const rawFb = this._translations[fallbackKey] ?? null;
fallbackTranslation = typeof rawFb === 'string' ? rawFb : null;
}
const resolved = translation ?? fallbackTranslation;
if (resolved && params) {
let result = resolved;
for (const [paramKey, paramValue] of Object.entries(params)) {
result = result.replaceAll(new RegExp(`{{${paramKey}}}`, 'g'), String(paramValue));
}
return result;
}
return resolved || key;
}
if (this.translationRegistry && this._locale) {
const registryTranslations = this.translationRegistry.getTranslations(this._locale);
const regRaw = registryTranslations?.[key];
return (typeof regRaw === 'string' ? regRaw : null) ?? key;
}
return key;
}
/**
* Closes the calendar and restores focus to the previously focused element.
* This improves accessibility by returning focus to the trigger element.
*/
closeCalendarWithFocusRestore() {
if (this.isCalendarOpen && !this.isInlineMode && !this.disabled) {
this._finalizeSameDayRangeOnClose();
}
this.removeFocusTrap();
this.isCalendarOpen = false;
this._isCalendarOpening.set(false);
this._startClosingState();
this.onTouched();
if (this._field) {
this.fieldSyncService.markAsTouched(this._field);
}
// Restore focus to the previously focused element
if (this.isBrowser && this.previousFocusElement) {
// Use setTimeout to ensure the calendar is fully closed before restoring focus
this.trackedSetTimeout(() => {
try {
if (this.previousFocusElement && document.contains(this.previousFocusElement)) {
this.previousFocusElement.focus({ preventScroll: true });
}
}
catch (error) {
// Element may no longer be in the DOM, ignore error
if (isDevMode()) {
console.warn('[ngxsmk-datepicker] Could not restore focus:', error);
}
}
this.previousFocusElement = null;
}, 0);
}
}
updateRtlState() {
if (this.isBrowser && this.elementRef?.nativeElement) {
const wrapper = this.elementRef?.nativeElement?.querySelector('.ngxsmk-datepicker-wrapper');
if (wrapper) {
if (this.isRtl) {
wrapper.setAttribute('dir', 'rtl');
}
else {
wrapper.removeAttribute('dir');
}
}
}
}
/**
* 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() {
this.removeFocusTrap();
NgxsmkDatepickerComponent._allInstances.delete(this);
// Tear down open-popover resources: window listeners added in _startOpeningState
// and the body-appended portal view attached to ApplicationRef. Without this,
// destroying the component while the calendar is open leaks both.
if (this.isBrowser) {
window.removeEventListener('scroll', this.updatePositionOnScroll, { capture: true });
window.removeEventListener('resize', this.updatePositionOnScroll);
}
this.destroyBodyView();
if (this.lazyLoadingObserver) {
this.lazyLoadingObserver.disconnect();
this.lazyLoadingObserver = null;
}
// Clean up field sync service
this.fieldSyncService.cleanup();
if (this.scrollDebounceTimer && this.isBrowser) {
cancelAnimationFrame(this.scrollDebounceTimer);
this.scrollDebounceTimer = null;
}
// Clear individual timeout IDs first
if (this.openCalendarTimeoutId) {
clearTimeout(this.openCalendarTimeoutId);
this.openCalendarTimeoutId = null;
}
if (this.touchHandledTimeout) {
clearTimeout(this.touchHandledTimeout);
this.touchHandledTimeout = null;
}
if (this._touchListenersSetupTimeout) {
clearTimeout(this._touchListenersSetupTimeout);
this._touchListenersSetupTimeout = null;
}
if (this.fieldSyncTimeoutId) {
clearTimeout(this.fieldSyncTimeoutId);
if (this.activeTimeouts) {
this.activeTimeouts.delete(this.fieldSyncTimeoutId);
}
this.fieldSyncTimeoutId = null;
}
// Clear all tracked timeouts and animation frames (single cleanup block)
if (this.activeTimeouts) {
this.activeTimeouts.forEach((timeoutId) => clearTimeout(timeoutId));
this.activeTimeouts.clear();
}
if (this.activeAnimationFrames) {
this.activeAnimationFrames.forEach((frameId) => cancelAnimationFrame(frameId));
this.activeAnimationFrames.clear();
}
// Complete the subject last to ensure all cleanup is done first
// Check if subject is already closed to avoid ObjectUnsubscribedError
if (!this.stateChanges.closed) {
this.stateChanges.complete();
}
if (this._fieldEffectRef) {
this._fieldEffectRef.destroy();
this._fieldEffectRef = null;
}
// Clean up change detection effect
if (this._changeDetectionEffect) {
this._changeDetectionEffect.destroy();
}
this._asyncFilterRequestId++; // drop in-flight asyncDateFilter responses
this._asyncDateFilterEffect.destroy();
if (this.passiveTouchListeners) {
this.passiveTouchListeners.forEach((cleanup) => cleanup());
this.passiveTouchListeners = [];
}
// Release date references so retained component instances don't pin them
this.selectedDate = null;
this.selectedDates = [];
this.startDate = null;
this.endDate = null;
this.hoveredDate = null;
this._value = null;
this.calendarGenerationService.clearCache();
}
getActualPopoverContainer() {
let popover = this.popoverContainer?.nativeElement;
if (this._shouldAppendToBody) {
if (!this.isBrowser)
return null;
const el = this.document.getElementById(this.popoverId);
if (el) {
popover = el;
}
}
if (!popover && this.elementRef?.nativeElement) {
popover = this.elementRef.nativeElement.querySelector('.ngxsmk-popover-container');
}
return popover || null;
}
setupFocusTrap() {
if (this.isInlineMode ||
!this.isBrowser ||
!this.focusTrapService ||
this.disableFocusTrap() ||
this.isIonicEnvironment()) {
return;
}
this.removeFocusTrap();
const popoverRef = this.getActualPopoverContainer();
if (popoverRef) {
this.focusTrapCleanup = this.focusTrapService.trapFocus(new ElementRef(popoverRef));
}
}
/**
* 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.
*/
positionPopoverRelativeToInput() {
if (!this.isBrowser) {
return;
}
const popover = this.getActualPopoverContainer();
if (!popover) {
return;
}
const inputGroup = this.elementRef?.nativeElement?.querySelector('.ngxsmk-input-group:not(.ngxsmk-native-input-group)') ||
this.elementRef?.nativeElement?.querySelector('.ngxsmk-input-group');
this.popoverPositioningService.positionRelativeToInput(popover, inputGroup, {
isInlineMode: this.isInlineMode,
shouldAppendToBody: this._shouldAppendToBody,
centerOnMobile: this.mobileModalStyle === 'center' && this.isMobileDevice(),
});
}
/**
* Determines if the component is operating within an Ionic environment.
* This detection disables features that may conflict with Ionic's overlay system.
*/
isIonicEnvironment() {
if (!this.isBrowser) {
return false;
}
try {
// Check for Ionic global object or key Ionic DOM elements/styles
const g = globalThis;
return (g['Ionic'] !== undefined ||
(typeof document !== 'undefined' && !!document.querySelector('ion-app')) ||
(typeof getComputedStyle !== 'undefined' &&
Boolean(getComputedStyle(document.documentElement).getPropertyValue('--ion-color-primary'))));
}
catch {
return false;
}
}
removeFocusTrap() {
if (this.focusTrapCleanup) {
this.focusTrapCleanup();
this.focusTrapCleanup = null;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NgxsmkDatepickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: NgxsmkDatepickerComponent, isStandalone: true, selector: "ngxsmk-datepicker", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: false, isRequired: false, transformFunction: null }, calendarViewMode: { classPropertyName: "calendarViewMode", publicName: "calendarViewMode", isSignal: false, isRequired: false, transformFunction: null }, isInvalidDate: { classPropertyName: "isInvalidDate", publicName: "isInvalidDate", isSignal: false, isRequired: false, transformFunction: null }, asyncDateFilter: { classPropertyName: "asyncDateFilter", publicName: "asyncDateFilter", isSignal: false, isRequired: false, transformFunction: null }, showRanges: { classPropertyName: "showRanges", publicName: "showRanges", isSignal: true, isRequired: false, transformFunction: null }, showPresets: { classPropertyName: "showPresets", publicName: "showPresets", isSignal: true, isRequired: false, transformFunction: null }, showTime: { classPropertyName: "showTime", publicName: "showTime", isSignal: false, isRequired: false, transformFunction: null }, timeOnly: { classPropertyName: "timeOnly", publicName: "timeOnly", isSignal: false, isRequired: false, transformFunction: null }, timeRangeMode: { classPropertyName: "timeRangeMode", publicName: "timeRangeMode", isSignal: true, isRequired: false, transformFunction: null }, showCalendarButton: { classPropertyName: "showCalendarButton", publicName: "showCalendarButton", isSignal: false, isRequired: false, transformFunction: null }, minuteInterval: { classPropertyName: "minuteInterval", publicName: "minuteInterval", isSignal: false, isRequired: false, transformFunction: null }, use24Hour: { classPropertyName: "use24Hour", publicName: "use24Hour", isSignal: false, isRequired: false, transformFunction: null }, secondInterval: { classPropertyName: "secondInterval", publicName: "secondInterval", isSignal: false, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: false, isRequired: false, transformFunction: null }, holidayProvider: { classPropertyName: "holidayProvider", publicName: "holidayProvider", isSignal: false, isRequired: false, transformFunction: null }, disableHolidays: { classPropertyName: "disableHolidays", publicName: "disableHolidays", isSignal: false, isRequired: false, transformFunction: null }, disabledDates: { classPropertyName: "disabledDates", publicName: "disabledDates", isSignal: false, isRequired: false, transformFunction: null }, disabledRanges: { classPropertyName: "disabledRanges", publicName: "disabledRanges", isSignal: false, isRequired: false, transformFunction: null }, recurringPattern: { classPropertyName: "recurringPattern", publicName: "recurringPattern", isSignal: false, isRequired: false, transformFunction: null }, dateTemplate: { classPropertyName: "dateTemplate", publicName: "dateTemplate", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: false, isRequired: false, transformFunction: null }, inline: { classPropertyName: "inline", publicName: "inline", isSignal: false, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: false, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: false, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: false, isRequired: false, transformFunction: null }, autocomplete: { classPropertyName: "autocomplete", publicName: "autocomplete", isSignal: false, isRequired: false, transformFunction: null }, translations: { classPropertyName: "translations", publicName: "translations", isSignal: false, isRequired: false, transformFunction: null }, translationService: { classPropertyName: "translationService", publicName: "translationService", isSignal: false, isRequired: false, transformFunction: null }, clearLabel: { classPropertyName: "clearLabel", publicName: "clearLabel", isSignal: false, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: false, isRequired: false, transformFunction: null }, prevMonthAriaLabel: { classPropertyName: "prevMonthAriaLabel", publicName: "prevMonthAriaLabel", isSignal: false, isRequired: false, transformFunction: null }, nextMonthAriaLabel: { classPropertyName: "nextMonthAriaLabel", publicName: "nextMonthAriaLabel", isSignal: false, isRequired: false, transformFunction: null }, clearAriaLabel: { classPropertyName: "clearAriaLabel", publicName: "clearAriaLabel", isSignal: false, isRequired: false, transformFunction: null }, closeAriaLabel: { classPropertyName: "closeAriaLabel", publicName: "closeAriaLabel", isSignal: false, isRequired: false, transformFunction: null }, weekStart: { classPropertyName: "weekStart", publicName: "weekStart", isSignal: false, isRequired: false, transformFunction: null }, yearRange: { classPropertyName: "yearRange", publicName: "yearRange", isSignal: false, isRequired: false, transformFunction: null }, timezone: { classPropertyName: "timezone", publicName: "timezone", isSignal: false, isRequired: false, transformFunction: null }, showOtherMonths: { classPropertyName: "showOtherMonths", publicName: "showOtherMonths", isSignal: false, isRequired: false, transformFunction: null }, hooks: { classPropertyName: "hooks", publicName: "hooks", isSignal: false, isRequired: false, transformFunction: null }, enableKeyboardShortcuts: { classPropertyName: "enableKeyboardShortcuts", publicName: "enableKeyboardShortcuts", isSignal: false, isRequired: false, transformFunction: null }, customShortcuts: { classPropertyName: "customShortcuts", publicName: "customShortcuts", isSignal: false, isRequired: false, transformFunction: null }, autoApplyClose: { classPropertyName: "autoApplyClose", publicName: "autoApplyClose", isSignal: true, isRequired: false, transformFunction: null }, allowSameDay: { classPropertyName: "allowSameDay", publicName: "allowSameDay", isSignal: true, isRequired: false, transformFunction: null }, displayFormat: { classPropertyName: "displayFormat", publicName: "displayFormat", isSignal: false, isRequired: false, transformFunction: null }, allowTyping: { classPropertyName: "allowTyping", publicName: "allowTyping", isSignal: false, isRequired: false, transformFunction: null }, inputMask: { classPropertyName: "inputMask", publicName: "inputMask", isSignal: false, isRequired: false, transformFunction: null }, enableNaturalLanguage: { classPropertyName: "enableNaturalLanguage", publicName: "enableNaturalLanguage", isSignal: false, isRequired: false, transformFunction: null }, naturalLanguagePreviewTemplate: { classPropertyName: "naturalLanguagePreviewTemplate", publicName: "naturalLanguagePreviewTemplate", isSignal: true, isRequired: false, transformFunction: null }, enableAi: { classPropertyName: "enableAi", publicName: "enableAi", isSignal: false, isRequired: false, transformFunction: null }, aiPlaceholder: { classPropertyName: "aiPlaceholder", publicName: "aiPlaceholder", isSignal: false, isRequired: false, transformFunction: null }, aiSuggestions: { classPropertyName: "aiSuggestions", publicName: "aiSuggestions", isSignal: false, isRequired: false, transformFunction: null }, showAiSuggestions: { classPropertyName: "showAiSuggestions", publicName: "showAiSuggestions", isSignal: false, isRequired: false, transformFunction: null }, aiResolver: { classPropertyName: "aiResolver", publicName: "aiResolver", isSignal: false, isRequired: false, transformFunction: null }, calendars: { classPropertyName: "calendars", publicName: "calendars", isSignal: false, isRequired: false, transformFunction: null }, rangePresetFactory: { classPropertyName: "rangePresetFactory", publicName: "rangePresetFactory", isSignal: false, isRequired: false, transformFunction: null }, showTimezoneSelector: { classPropertyName: "showTimezoneSelector", publicName: "showTimezoneSelector", isSignal: true, isRequired: false, transformFunction: null }, defaultTimezone: { classPropertyName: "defaultTimezone", publicName: "defaultTimezone", isSignal: false, isRequired: false, transformFunction: null }, calendarCount: { classPropertyName: "calendarCount", publicName: "calendarCount", isSignal: false, isRequired: false, transformFunction: null }, calendarLayout: { classPropertyName: "calendarLayout", publicName: "calendarLayout", isSignal: false, isRequired: false, transformFunction: null }, changeActiveMonthOnSelection: { classPropertyName: "changeActiveMonthOnSelection", publicName: "changeActiveMonthOnSelection", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, showWeekNumbers: { classPropertyName: "showWeekNumbers", publicName: "showWeekNumbers", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, weekNumberLabel: { classPropertyName: "weekNumberLabel", publicName: "weekNumberLabel", isSignal: false, isRequired: false, transformFunction: null }, secondaryCalendar: { classPropertyName: "secondaryCalendar", publicName: "secondaryCalendar", isSignal: false, isRequired: false, transformFunction: null }, dayMetadata: { classPropertyName: "dayMetadata", publicName: "dayMetadata", isSignal: false, isRequired: false, transformFunction: null }, calendarHeaderTemplate: { classPropertyName: "calendarHeaderTemplate", publicName: "calendarHeaderTemplate", isSignal: false, isRequired: false, transformFunction: null }, calendarFooterTemplate: { classPropertyName: "calendarFooterTemplate", publicName: "calendarFooterTemplate", isSignal: false, isRequired: false, transformFunction: null }, defaultMonthOffset: { classPropertyName: "defaultMonthOffset", publicName: "defaultMonthOffset", isSignal: false, isRequired: false, transformFunction: null }, syncScroll: { classPropertyName: "syncScroll", publicName: "syncScroll", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: false, isRequired: false, transformFunction: null }, useNativePicker: { classPropertyName: "useNativePicker", publicName: "useNativePicker", isSignal: false, isRequired: false, transformFunction: null }, enableHapticFeedback: { classPropertyName: "enableHapticFeedback", publicName: "enableHapticFeedback", isSignal: true, isRequired: false, transformFunction: null }, mobileModalStyle: { classPropertyName: "mobileModalStyle", publicName: "mobileModalStyle", isSignal: false, isRequired: false, transformFunction: null }, mobileTimePickerStyle: { classPropertyName: "mobileTimePickerStyle", publicName: "mobileTimePickerStyle", isSignal: false, isRequired: false, transformFunction: null }, enablePullToRefresh: { classPropertyName: "enablePullToRefresh", publicName: "enablePullToRefresh", isSignal: true, isRequired: false, transformFunction: null }, mobileTheme: { classPropertyName: "mobileTheme", publicName: "mobileTheme", isSignal: true, isRequired: false, transformFunction: null }, enableVoiceInput: { classPropertyName: "enableVoiceInput", publicName: "enableVoiceInput", isSignal: false, isRequired: false, transformFunction: null }, autoDetectMobile: { classPropertyName: "autoDetectMobile", publicName: "autoDetectMobile", isSignal: false, isRequired: false, transformFunction: null }, disableFocusTrap: { classPropertyName: "disableFocusTrap", publicName: "disableFocusTrap", isSignal: true, isRequired: false, transformFunction: null }, appendToBody: { classPropertyName: "appendToBody", publicName: "appendToBody", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: false, isRequired: false, transformFunction: null }, startAt: { classPropertyName: "startAt", publicName: "startAt", isSignal: false, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: false, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: false, isRequired: false, transformFunction: null }, dateFormatPattern: { classPropertyName: "dateFormatPattern", publicName: "dateFormatPattern", isSignal: false, isRequired: false, transformFunction: null }, animationConfig: { classPropertyName: "animationConfig", publicName: "animationConfig", isSignal: false, isRequired: false, transformFunction: null }, rtl: { classPropertyName: "rtl", publicName: "rtl", isSignal: false, isRequired: false, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null }, disabledState: { classPropertyName: "disabledState", publicName: "disabledState", isSignal: false, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: false, isRequired: false, transformFunction: null }, errorState: { classPropertyName: "errorState", publicName: "errorState", isSignal: false, isRequired: false, transformFunction: null }, userAriaDescribedBy: { classPropertyName: "userAriaDescribedBy", publicName: "userAriaDescribedBy", isSignal: false, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: false, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: false, isRequired: false, transformFunction: null }, ranges: { classPropertyName: "ranges", publicName: "ranges", isSignal: false, isRequired: false, transformFunction: null }, comparisonRange: { classPropertyName: "comparisonRange", publicName: "comparisonRange", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { asyncDateFilterLoading: "asyncDateFilterLoading", asyncDateFilterError: "asyncDateFilterError", naturalLanguageResolved: "naturalLanguageResolved", aiPromptSubmitted: "aiPromptSubmitted", invalidRange: "invalidRange", timezoneChange: "timezoneChange", valueChange: "valueChange", action: "action", validationError: "validationError" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:touchstart": "onDocumentTouchStart($event)", "keydown": "onKeyDown($event)" }, properties: { "class.ngxsmk-inline": "isInlineMode", "class.dark-theme": "this.isDarkMode", "class.ngxsmk-rtl": "this.rtlClass" } }, providers: [
FieldSyncService,
CalendarGenerationService,
DatepickerParsingService,
TouchGestureHandlerService,
PopoverPositioningService,
DatePipe,
], viewQueries: [{ propertyName: "portalTemplate", first: true, predicate: ["portalContent"], descendants: true, static: true }, { propertyName: "popoverContainer", first: true, predicate: ["popoverContainer"], descendants: true }, { propertyName: "datepickerInput", first: true, predicate: ["datepickerInput"], descendants: true }, { propertyName: "datepickerContent", first: true, predicate: ["datepickerContent"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
<div
class="ngxsmk-datepicker-wrapper"
[class.ngxsmk-inline-mode]="isInlineMode"
[class.ngxsmk-calendar-open]="isCalendarOpen && !isInlineMode"
[class.ngxsmk-append-to-body]="_shouldAppendToBody"
[class.ngxsmk-rtl]="isRtl"
[class.ngxsmk-native-picker]="shouldUseNativePicker()"
[class.ngxsmk-no-responsive]="!responsive"
[ngClass]="classes()?.wrapper"
>
@if (!isInlineMode) {
<div class="ngxsmk-input-wrapper-container" style="position: relative; display: inline-block; width: 100%;">
<ngxsmk-datepicker-input
#datepickerInput
[isNative]="shouldUseNativePicker()"
[disabled]="disabled"
[classes]="classes()"
[nativeInputType]="getNativeInputType()"
[formattedValue]="formatValueForNativeInput(value)"
[placeholder]="placeholder"
[id]="inputId || _uniqueId"
[name]="name"
[autocomplete]="autocomplete"
[required]="required"
[minDateNative]="getMinDateForNativeInput()"
[maxDateNative]="getMaxDateForNativeInput()"
[ariaLabel]="placeholder || getTranslation(timeOnly ? 'selectTime' : 'selectDate')"
[ariaDescribedBy]="'datepicker-help-' + _uniqueId"
[errorState]="errorState"
[clearAriaLabel]="_clearAriaLabel"
[clearLabel]="_clearLabel"
[isCalendarOpen]="isCalendarOpen"
[allowTyping]="allowTyping"
[typedInputValue]="typedInputValue"
[displayValue]="displayValue"
[showCalendarButton]="showCalendarButton"
[calendarAriaLabel]="getTranslation(timeOnly ? 'selectTime' : 'selectDate')"
[validationErrorMessage]="validationErrorMessage"
(nativeInputChange)="onNativeInputChange($event)"
(inputBlur)="onInputBlur($event)"
(clearValue)="clearValue($event)"
(toggleCalendar)="toggleCalendar($event)"
(pointerDown)="onPointerDown($event)"
(pointerUp)="onPointerUp($event)"
(inputGroupFocus)="onInputGroupFocus()"
(inputKeyDown)="onInputKeyDown($event)"
(inputChange)="onInputChange($event)"
(inputFocus)="onInputFocus($event)"
></ngxsmk-datepicker-input>
@if (showNaturalLanguagePreview && naturalLanguagePreview) {
<div
class="ngxsmk-natural-language-preview"
style="position: absolute; top: 100%; left: 0; z-index: 1000; background: var(--datepicker-background, #fff); border: 1px solid var(--datepicker-border-color, #ccc); border-radius: var(--datepicker-radius-md, 4px); padding: 8px 12px; box-shadow: var(--datepicker-shadow-md); margin-top: 4px; font-size: 14px; width: 100%; box-sizing: border-box;"
>
<ng-container
[ngTemplateOutlet]="naturalLanguagePreviewTemplate() || defaultPreviewTpl"
[ngTemplateOutletContext]="{ $implicit: naturalLanguagePreview }"
></ng-container>
</div>
}
</div>
}
<ng-template #defaultPreviewTpl let-preview>
<div class="ngxsmk-natural-language-preview-content">
Resolved: <strong>{{ preview }}</strong>
</div>
</ng-template>
<ng-template #portalContent>
<ngxsmk-datepicker-content
#datepickerContent
[isCalendarVisible]="isCalendarVisible"
[isCalendarOpen]="_isCalendarOpen()"
[isInlineMode]="isInlineMode"
[shouldAppendToBody]="_shouldAppendToBody"
[theme]="theme"
[popoverId]="popoverId"
[classes]="classes()"
[timeOnly]="timeOnly"
[showTime]="showTime"
[isMobile]="isMobileDevice()"
[mobileModalStyle]="mobileModalStyle"
[align]="align"
[ariaLabel]="calendarAriaLabel()"
[isCalendarOpening]="isCalendarOpening"
[loadingMessage]="calendarLoadingMessage()"
[showRanges]="showRanges() || showPresets()"
[rangesArray]="rangesArray"
[selectedRange]="[startDate, endDate]"
[showTimezoneSelector]="showTimezoneSelector()"
[timezoneOptions]="getTimezoneOptions()"
[currentTimezone]="timezone || defaultTimezone"
[mode]="mode"
[disabled]="disabled"
[calendarCount]="calendarCount"
[calendarLayout]="calendarLayout"
[syncScrollEnabled]="syncScroll().enabled ?? false"
[calendarMonths]="renderedCalendars()"
[weekDays]="weekDays"
[weekDaysFull]="weekDaysFull"
[showOtherMonths]="showOtherMonths"
[showWeekNumbers]="showWeekNumbers"
[weekNumberLabel]="weekNumberLabel"
[secondaryCalendar]="secondaryCalendar"
[secondaryCalendarLocale]="locale"
[selectedDate]="selectedDate"
[startDate]="startDate"
[endDate]="endDate"
[focusedDate]="focusedDate"
[today]="today"
[dateTemplate]="dateTemplate()"
[calendarViewMode]="calendarViewMode"
[monthOptions]="monthOptions()"
[currentMonth]="_currentMonthSignal()"
[yearOptions]="yearOptions()"
[currentYear]="_currentYearSignal()"
[isBackArrowDisabled]="isBackArrowDisabled"
[prevMonthAriaLabel]="_prevMonthAriaLabel"
[nextMonthAriaLabel]="_nextMonthAriaLabel"
[yearGrid]="yearGrid"
[currentDecade]="_currentDecade"
[decadeGrid]="decadeGrid"
[timelineStartDate]="timelineStartDate"
[timelineEndDate]="timelineEndDate"
[timelineMonths]="timelineMonths"
[minuteInterval]="minuteInterval"
[startTimeSlider]="startTimeSlider"
[endTimeSlider]="endTimeSlider"
[timeRangeMode]="timeRangeMode()"
[hourOptions]="hourOptions"
[minuteOptions]="minuteOptions"
[secondOptions]="secondOptions"
[ampmOptions]="ampmOptions"
[currentDisplayHour]="currentDisplayHour"
[currentMinute]="currentMinute"
[currentSecond]="currentSecond"
[isPm]="isPm"
[showSeconds]="showSeconds"
[use24Hour]="use24Hour"
[startDisplayHour]="startDisplayHour"
[startMinute]="startMinute"
[startSecond]="startSecond"
[startIsPm]="startIsPm"
[endDisplayHour]="endDisplayHour"
[endMinute]="endMinute"
[endSecond]="endSecond"
[endIsPm]="endIsPm"
[clearAriaLabel]="_clearAriaLabel"
[clearLabel]="_clearLabel"
[closeAriaLabel]="_closeAriaLabel"
[closeLabel]="_closeLabel"
[translations]="_translations"
[boundIsDateDisabled]="boundIsDateDisabled"
[boundIsYearDisabled]="boundIsYearDisabled"
[boundIsDecadeDisabled]="boundIsDecadeDisabled"
[boundGetDayMetadata]="boundGetDayMetadata"
[calendarHeaderTemplate]="calendarHeaderTemplate"
[calendarFooterTemplate]="calendarFooterTemplate"
[enableAi]="enableAi"
[aiPlaceholder]="aiPlaceholder"
[aiSuggestions]="aiSuggestions"
[showAiSuggestions]="showAiSuggestions"
[isAiResolving]="isAiResolving"
(aiPromptSubmitted)="onAiPromptSubmitted($event)"
[boundIsSameDay]="boundIsSameDay"
[boundIsHoliday]="boundIsHoliday"
[boundIsMultipleSelected]="boundIsMultipleSelected"
[boundIsInRange]="boundIsInRange"
[boundIsInComparisonRange]="boundIsInComparisonRange"
[boundIsPreviewInRange]="boundIsPreviewInRange"
[boundGetAriaLabel]="boundGetAriaLabel"
[boundGetDayCellCustomClasses]="boundGetDayCellCustomClasses"
[boundGetDayCellTooltip]="boundGetDayCellTooltip"
[boundFormatDayNumber]="boundFormatDayNumber"
[getMonthYearLabel]="boundGetMonthYearLabel"
[getCalendarAriaLabelForMonth]="boundGetCalendarAriaLabelForMonth"
[isTimelineMonthSelected]="boundIsTimelineMonthSelected"
[formatTimeSliderValue]="boundFormatTimeSliderValue"
(backdropClick)="onBackdropInteract($event)"
(escapeKey)="onPopoverEscape($event)"
(containerKeyDown)="onKeyDown($event)"
(touchStartContainer)="onBottomSheetTouchStart($event)"
(touchMoveContainer)="onBottomSheetTouchMove($event)"
(touchEndContainer)="onBottomSheetTouchEnd($event)"
(rangeSelect)="selectRange($event)"
(timezoneChange)="setTimezone($event)"
(previousMonth)="changeMonth(-1)"
(nextMonth)="changeMonth(1)"
(currentMonthChange)="currentMonth = $event"
(currentYearChange)="onYearSelectChange($event)"
(dateClick)="onDateClick($event)"
(dateHover)="onDateHover($event)"
(dateFocus)="onDateFocus($event)"
(swipeStart)="onCalendarSwipeStart($event)"
(swipeMove)="onCalendarSwipeMove($event)"
(swipeEnd)="onCalendarSwipeEnd($event)"
(touchStart)="onDateCellTouchStart($event.event, $event.day)"
(touchMove)="onDateCellTouchMove($event)"
(touchEnd)="onDateCellTouchEnd($event.event, $event.day)"
(viewModeChange)="onViewModeChange($event)"
(changeYear)="changeYear($event)"
(yearClick)="onYearClick($event)"
(changeDecade)="changeDecade($event)"
(decadeClick)="onDecadeClick($event)"
(timelineZoomOut)="timelineZoomOut()"
(timelineZoomIn)="timelineZoomIn()"
(timelineMonthClick)="onTimelineMonthClick($event)"
(startTimeSliderChange)="onStartTimeSliderChange($event)"
(endTimeSliderChange)="onEndTimeSliderChange($event)"
(currentDisplayHourChange)="currentDisplayHour = $event"
(currentMinuteChange)="currentMinute = $event"
(currentSecondChange)="currentSecond = $event"
(isPmChange)="isPm = $event"
(timeChange)="timeChange()"
(startDisplayHourChange)="startDisplayHour = $event"
(startMinuteChange)="startMinute = $event"
(startSecondChange)="startSecond = $event"
(startIsPmChange)="startIsPm = $event"
(endDisplayHourChange)="endDisplayHour = $event"
(endMinuteChange)="endMinute = $event"
(endSecondChange)="endSecond = $event"
(endIsPmChange)="endIsPm = $event"
(timeRangeChange)="timeRangeChange()"
(clearValue)="clearValue($event)"
(closeCalendar)="closeCalendarWithFocusRestore()"
></ngxsmk-datepicker-content>
</ng-template>
@if (isCalendarVisible && !_shouldAppendToBody) {
<ng-container *ngTemplateOutlet="portalContent"></ng-container>
}
@if (isKeyboardHelpOpen) {
<ngxsmk-datepicker-keyboard-help
[title]="getTranslation('keyboardShortcuts')"
[closeLabel]="getTranslation('close')"
[backdropLabel]="getTranslation('closeCalendarOverlay')"
(closeRequested)="toggleKeyboardHelp()"
/>
}
</div>
`, isInline: true, styles: ["ngxsmk-datepicker,.ngxsmk-popover-container,.ngxsmk-backdrop{--datepicker-primary-color: var(--ion-color-primary, #6d28d9);--datepicker-primary-contrast: var(--ion-color-primary-contrast, #ffffff);--datepicker-range-background: var(--ion-color-primary-tint, #f5f3ff);--datepicker-comparison-range-color: #f59e0b;--datepicker-background: var(--ion-background-color, #ffffff);--datepicker-text-color: var(--ion-text-color, #1f2937);--datepicker-subtle-text-color: var(--ion-text-color-step-400, #6b7280);--datepicker-border-color: var(--ion-item-border-color, var(--ion-border-color, #e5e7eb));--datepicker-hover-background: #f3f4f6;--datepicker-shadow-focus: 0 0 0 3px color-mix(in srgb, var(--datepicker-primary-color) 15%, transparent);--ngxsmk-color-primary: var(--datepicker-primary-color);--ngxsmk-color-on-primary: var(--datepicker-primary-contrast);--ngxsmk-color-range-bg: var(--datepicker-range-background);--ngxsmk-color-surface: var(--datepicker-background);--ngxsmk-color-surface-hover: var(--datepicker-hover-background);--ngxsmk-color-text-main: var(--datepicker-text-color);--ngxsmk-color-text-muted: var(--datepicker-subtle-text-color);--ngxsmk-color-border: var(--datepicker-border-color);--datepicker-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);--datepicker-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .06);--datepicker-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--datepicker-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, .1), 0 10px 10px -5px rgba(0, 0, 0, .04);--datepicker-font-size-xs: 10px;--datepicker-font-size-sm: 12px;--datepicker-font-size-base: 14px;--datepicker-font-size-lg: 16px;--datepicker-font-size-xl: 18px;--datepicker-line-height: 1.5;--datepicker-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;--datepicker-spacing-xs: 4px;--datepicker-spacing-sm: 8px;--datepicker-spacing-md: 12px;--datepicker-spacing-lg: 16px;--datepicker-spacing-xl: 20px;--datepicker-spacing-2xl: 24px;--datepicker-radius-sm: 6px;--datepicker-radius-md: 8px;--datepicker-radius-lg: 12px;--datepicker-radius-xl: 16px;--datepicker-border-radius: var(--datepicker-radius-lg);--datepicker-transition-duration: .15s;--datepicker-transition-easing: cubic-bezier(.4, 0, .2, 1);--datepicker-transition-property: all;--datepicker-transition: var(--datepicker-transition-property) var(--datepicker-transition-duration) var(--datepicker-transition-easing);--datepicker-z-index-base: 2147483647;--datepicker-z-index-backdrop: 2147483646}ngxsmk-datepicker.dark-theme,.ngxsmk-popover-container.dark-theme,.ngxsmk-backdrop.dark-theme{--datepicker-range-background: rgba(139, 92, 246, .15);--datepicker-background: #1f2937;--datepicker-text-color: #f3f4f6;--datepicker-subtle-text-color: #9ca3af;--datepicker-border-color: #374151;--datepicker-hover-background: #374151;--datepicker-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .3);--datepicker-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .3), 0 2px 4px -1px rgba(0, 0, 0, .2);--datepicker-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .3), 0 4px 6px -2px rgba(0, 0, 0, .2)}ngxsmk-datepicker.dark-theme *{--datepicker-range-background: rgba(139, 92, 246, .15);--datepicker-background: #1f2937;--datepicker-text-color: #f3f4f6;--datepicker-subtle-text-color: #9ca3af;--datepicker-border-color: #374151;--datepicker-hover-background: #374151}ngxsmk-datepicker.glass-theme,.ngxsmk-popover-container.glass-theme{--datepicker-background: rgba(255, 255, 255, .7);--datepicker-border-color: rgba(255, 255, 255, .3);--datepicker-shadow-lg: 0 8px 32px 0 rgba(31, 38, 135, .37);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--datepicker-border-color)}ngxsmk-datepicker.glass-theme.dark-theme,.ngxsmk-popover-container.glass-theme.dark-theme{--datepicker-background: rgba(31, 41, 55, .7);--datepicker-border-color: rgba(255, 255, 255, .1)}ngxsmk-datepicker.md3-theme{--datepicker-primary-color: #6750a4;--datepicker-radius-lg: 28px;--datepicker-font-family: \"Roboto\", sans-serif;--datepicker-shadow-md: 0px 1px 3px 1px rgba(0, 0, 0, .15), 0px 1px 2px rgba(0, 0, 0, .3)}@media(prefers-contrast:high){ngxsmk-datepicker{--datepicker-border-color: #000000;--datepicker-text-color: #000000;--datepicker-subtle-text-color: #000000;--datepicker-background: #ffffff;--datepicker-hover-background: #f0f0f0;--datepicker-primary-color: #0000ff;--datepicker-primary-contrast: #ffffff;--datepicker-range-background: #e0e0e0;--datepicker-focus-outline: #000000}ngxsmk-datepicker.dark-theme{--datepicker-border-color: #ffffff;--datepicker-text-color: #ffffff;--datepicker-subtle-text-color: #ffffff;--datepicker-background: #000000;--datepicker-hover-background: #333333;--datepicker-primary-color: #ffffff;--datepicker-primary-contrast: #000000;--datepicker-range-background: #333333;--datepicker-focus-outline: #ffffff}ngxsmk-datepicker *{border-color:var(--datepicker-border-color)!important}.ngxsmk-day-cell{border:2px solid transparent!important}.ngxsmk-day-cell:not(.disabled):not(.empty):hover{border-color:var(--datepicker-border-color)!important;background-color:var(--datepicker-hover-background)!important}.ngxsmk-day-cell.disabled{opacity:.5!important;border-color:#ccc!important}.ngxsmk-day-cell.selected,.ngxsmk-day-cell.start-date,.ngxsmk-day-cell.end-date{border:3px solid var(--datepicker-border-color)!important;background-color:var(--datepicker-primary-color)!important;color:var(--datepicker-primary-contrast)!important}.ngxsmk-day-cell.focused,.ngxsmk-day-cell:focus-visible{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-nav-button{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-nav-button.focused,.ngxsmk-nav-button:focus-visible{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-nav-button:hover:not(:disabled){background-color:var(--datepicker-hover-background)!important;border-color:var(--datepicker-border-color)!important}.ngxsmk-input-group{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-input-group:focus-within{border-color:var(--datepicker-border-color)!important;outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-clear-button,.ngxsmk-calendar-button{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-clear-button.focused,.ngxsmk-clear-button:focus-visible,.ngxsmk-calendar-button.focused,.ngxsmk-calendar-button:focus-visible{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-popover-container{border:3px solid var(--datepicker-border-color)!important;box-shadow:0 4px 8px #0000004d!important}.ngxsmk-day-cell.in-range{background-color:var(--datepicker-range-background)!important;border-color:var(--datepicker-border-color)!important}.ngxsmk-custom-select{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-custom-select.focused,.ngxsmk-custom-select:focus-within{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-time-input{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-time-input.focused,.ngxsmk-time-input:focus{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}}@media(prefers-reduced-motion:reduce){ngxsmk-datepicker{--datepicker-transition: none}ngxsmk-datepicker *,ngxsmk-datepicker *:before,ngxsmk-datepicker *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}\n", "ngxsmk-datepicker{display:block;width:100%;position:relative;font-family:var(--datepicker-font-family);user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;overflow:visible}ngxsmk-datepicker:has(.ngxsmk-calendar-open){z-index:1000!important}.ngxsmk-datepicker-wrapper.ngxsmk-calendar-open{z-index:var(--datepicker-z-index-base);position:relative;isolation:isolate}@media(max-width:1023px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode){isolation:auto!important;transform:none!important;-webkit-transform:none!important;contain:none!important;overflow:visible!important;clip-path:none!important;clip:auto!important;position:static!important;z-index:auto!important}ngxsmk-datepicker:not(:has(.ngxsmk-no-responsive)){display:block!important;width:100%!important;overflow:visible!important;position:relative!important;isolation:auto!important;transform:none!important;-webkit-transform:none!important;contain:none!important}ngxsmk-datepicker:not(:has(.ngxsmk-no-responsive)):has(.ngxsmk-calendar-open:not(.ngxsmk-inline-mode)){overflow:visible!important;isolation:auto!important;transform:none!important;-webkit-transform:none!important;contain:none!important;z-index:auto!important}}@media(min-width:1024px){.ngxsmk-backdrop:not(.ngxsmk-inline-backdrop){display:block!important;position:fixed!important;inset:0;background:transparent!important;z-index:var(--datepicker-z-index-backdrop)!important;pointer-events:auto!important}.ngxsmk-backdrop.ngxsmk-backdrop-allow-modal-scroll{pointer-events:none!important}}@media(max-width:1023px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-backdrop,.ngxsmk-backdrop:not(.ngxsmk-inline-backdrop){display:block;visibility:visible;opacity:1;position:fixed;inset:0;width:100vw;height:100vh;height:100dvh;z-index:var(--datepicker-z-index-backdrop);background:#0000008c;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent;pointer-events:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive):not(.ngxsmk-calendar-open) .ngxsmk-backdrop,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open.ngxsmk-inline-mode .ngxsmk-backdrop{display:none;visibility:hidden;opacity:0;pointer-events:none}body:has(.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode)),html:has(.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode)){overflow:hidden}.ngxsmk-backdrop.ngxsmk-backdrop-allow-modal-scroll{pointer-events:none}}.ngxsmk-input-group{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;cursor:pointer;width:100%;border:1px solid var(--datepicker-border-color);-webkit-border-radius:var(--datepicker-radius-md);-moz-border-radius:var(--datepicker-radius-md);border-radius:var(--datepicker-radius-md);background:var(--datepicker-background);-webkit-transition:var(--datepicker-transition);-moz-transition:var(--datepicker-transition);-o-transition:var(--datepicker-transition);transition:var(--datepicker-transition);position:relative;overflow:hidden;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);-ms-touch-action:manipulation;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent}.ngxsmk-input-group:focus-within{border-color:var(--datepicker-primary-color);-webkit-box-shadow:var(--datepicker-shadow-focus),var(--datepicker-shadow-md);-moz-box-shadow:var(--datepicker-shadow-focus),var(--datepicker-shadow-md);box-shadow:var(--datepicker-shadow-focus),var(--datepicker-shadow-md);outline:none}.ngxsmk-input-group:hover:not(.disabled){border-color:var(--datepicker-primary-color);-webkit-box-shadow:var(--datepicker-shadow-md);-moz-box-shadow:var(--datepicker-shadow-md);box-shadow:var(--datepicker-shadow-md)}.ngxsmk-input-group.disabled{cursor:not-allowed;opacity:.6;background:var(--datepicker-hover-background)}.ngxsmk-native-input-group{cursor:default}.ngxsmk-native-input-group .ngxsmk-native-input{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none}.ngxsmk-native-input-group .ngxsmk-native-input::-webkit-calendar-picker-indicator{cursor:pointer;opacity:1;margin-left:4px}.ngxsmk-native-input-group .ngxsmk-native-input::-webkit-inner-spin-button,.ngxsmk-native-input-group .ngxsmk-native-input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.ngxsmk-display-input{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;width:100%;padding:var(--datepicker-spacing-md) var(--datepicker-spacing-lg);font-size:var(--datepicker-font-size-base);line-height:var(--datepicker-line-height);color:var(--datepicker-text-color);background:transparent;border:none;outline:none;cursor:pointer;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;-webkit-transition:var(--datepicker-transition);-moz-transition:var(--datepicker-transition);-o-transition:var(--datepicker-transition);transition:var(--datepicker-transition);min-height:20px;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;font-weight:400;-ms-touch-action:manipulation;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent}.ngxsmk-display-input:disabled{cursor:not-allowed;opacity:.6}.ngxsmk-display-input::placeholder{color:var(--datepicker-subtle-text-color);font-weight:400}.ngxsmk-clear-button{background:none;border:none;padding:var(--datepicker-spacing-sm);margin-right:var(--datepicker-spacing-sm);cursor:pointer;color:var(--datepicker-subtle-text-color);line-height:1;-webkit-border-radius:var(--datepicker-radius-sm);-moz-border-radius:var(--datepicker-radius-sm);border-radius:var(--datepicker-radius-sm);-webkit-transition:var(--datepicker-transition);-moz-transition:var(--datepicker-transition);-o-transition:var(--datepicker-transition);transition:var(--datepicker-transition);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;will-change:background-color;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-ms-touch-action:manipulation;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent}.ngxsmk-clear-button svg{width:16px!important;height:16px!important;min-width:16px;min-height:16px;max-width:16px;max-height:16px}.ngxsmk-clear-button:hover:not(:disabled){color:var(--datepicker-text-color);background:var(--datepicker-hover-background)}.ngxsmk-clear-button:focus-visible{outline:2px solid var(--datepicker-primary-color);outline-offset:2px}.ngxsmk-calendar-button{background:none;border:none;padding:var(--datepicker-spacing-sm);margin-right:var(--datepicker-spacing-sm);cursor:pointer;color:var(--datepicker-subtle-text-color);line-height:1;-webkit-border-radius:var(--datepicker-radius-sm);-moz-border-radius:var(--datepicker-radius-sm);border-radius:var(--datepicker-radius-sm);-webkit-transition:var(--datepicker-transition);-moz-transition:var(--datepicker-transition);-o-transition:var(--datepicker-transition);transition:var(--datepicker-transition);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-ms-touch-action:manipulation;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent;flex-shrink:0}.ngxsmk-calendar-button svg{width:18px;height:18px}.ngxsmk-calendar-button:hover:not(:disabled){color:var(--datepicker-primary-color);background:var(--datepicker-hover-background)}.ngxsmk-calendar-button:focus-visible{outline:2px solid var(--datepicker-primary-color);outline-offset:2px}.ngxsmk-calendar-button:disabled{cursor:not-allowed;opacity:.6}.ngxsmk-popover-container{position:absolute;top:calc(100% + 8px);left:0;z-index:var(--datepicker-z-index-base);width:100%;min-width:100%;max-width:100%;overflow:visible;display:block;visibility:visible;opacity:1;pointer-events:auto}.ngxsmk-popover-container.ngxsmk-inline-container{position:static!important;top:0;left:0;margin:0;transform:none!important;box-shadow:none!important;animation:none!important;width:auto;min-width:0;max-width:100%;display:inline-block}.ngxsmk-popover-container.ngxsmk-popover-open{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content;max-width:-webkit-fit-content;max-width:-moz-fit-content;max-width:fit-content}.ngxsmk-popover-container.ngxsmk-popover-open .ngxsmk-calendar-container{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content}.ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open{height:auto!important;max-height:none!important;overflow:visible!important}.ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open .ngxsmk-datepicker-container,.ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open .ngxsmk-calendar-container{overflow:visible!important;max-height:none!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-time-only-popover):not(.ngxsmk-has-time-selection){max-height:calc(100dvh - 48px)!important;min-height:0!important;overflow:hidden!important;display:-webkit-box!important;display:-webkit-flex!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-orient:vertical!important;-webkit-flex-direction:column!important;-ms-flex-direction:column!important;flex-direction:column!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-time-only-popover):not(.ngxsmk-has-time-selection)>*{-webkit-box-flex:1!important;-webkit-flex:1 1 auto!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important;min-height:0!important;min-width:0!important;width:100%!important;max-width:100%!important;overflow:hidden!important;box-sizing:border-box!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-time-only-popover):not(.ngxsmk-has-time-selection) .ngxsmk-datepicker-container{display:-webkit-box!important;display:-webkit-flex!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-orient:vertical!important;-webkit-flex-direction:column!important;-ms-flex-direction:column!important;flex-direction:column!important;min-height:0!important;min-width:0!important;width:100%!important;max-width:100%!important;overflow:hidden!important;box-sizing:border-box!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-time-only-popover):not(.ngxsmk-has-time-selection) .ngxsmk-calendar-container{-webkit-box-flex:1!important;-webkit-flex:1 1 auto!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important;min-height:0!important;min-width:0!important;width:100%!important;max-width:100%!important;overflow:hidden!important;display:-webkit-box!important;display:-webkit-flex!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-orient:vertical!important;-webkit-flex-direction:column!important;-ms-flex-direction:column!important;flex-direction:column!important;box-sizing:border-box!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-time-only-popover):not(.ngxsmk-has-time-selection) .ngxsmk-multi-calendar-container{-webkit-box-flex:1!important;-webkit-flex:1 1 auto!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important;min-height:0!important;min-width:0!important;width:100%!important;max-width:100%!important;overflow:hidden!important;box-sizing:border-box!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-time-only-popover):not(.ngxsmk-has-time-selection) .ngxsmk-calendar-month{min-height:0!important;min-width:0!important;width:100%!important;max-width:100%!important;overflow:hidden!important;box-sizing:border-box!important;display:-webkit-box!important;display:-webkit-flex!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-orient:vertical!important;-webkit-flex-direction:column!important;-ms-flex-direction:column!important;flex-direction:column!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-time-only-popover):not(.ngxsmk-has-time-selection) .ngxsmk-days-grid-wrapper{-webkit-box-flex:1!important;-webkit-flex:1 1 auto!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important;min-height:0!important;min-width:0!important;width:100%!important;max-width:100%!important;overflow:hidden!important;box-sizing:border-box!important}.ngxsmk-popover-container.ngxsmk-align-left{left:0!important;right:auto!important;transform:none!important}.ngxsmk-popover-container.ngxsmk-align-right{left:auto!important;right:0!important;transform:none!important}.ngxsmk-popover-container.ngxsmk-align-center{left:50%!important;right:auto!important;transform:translate(-50%)!important;-webkit-transform:translateX(-50%)!important}@media(max-width:1023px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container){isolation:auto;contain:none;clip-path:none;clip:auto;overflow:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-popover-container:not(.ngxsmk-inline-container),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;visibility:visible;opacity:1;position:fixed;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%);transform:translate(-50%,-50%);z-index:2147483647;pointer-events:none;width:calc(100% - 12px);min-width:280px;max-width:480px;max-height:calc(100vh - 48px - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px));max-height:calc(100dvh - 48px - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px));padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px);min-height:0;overflow:hidden}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-popover-open):not(.ngxsmk-inline-container){display:none;visibility:hidden;opacity:0;pointer-events:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container){height:auto;max-height:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-popover-container:not(.ngxsmk-inline-container)>*,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container)>*{pointer-events:auto;-webkit-box-flex:1;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;min-height:0;overflow:hidden}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-datepicker-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;min-height:0;overflow:hidden}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-calendar-container{-webkit-box-flex:1;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;min-height:0;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-multi-calendar-container{-webkit-box-flex:1;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;min-height:0;overflow:hidden}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-calendar-month{min-height:0;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-days-grid-wrapper{-webkit-box-flex:1;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;min-height:0;overflow:hidden}.ngxsmk-input-and-error{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;width:100%;gap:4px}.ngxsmk-validation-error{font-size:var(--datepicker-font-size-sm, 12px);color:var(--datepicker-error-color, #b00020);margin-top:2px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-datepicker-container,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-calendar-container{pointer-events:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-day-cell{pointer-events:auto;min-width:44px;min-height:44px;margin:2px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-day-number{pointer-events:auto;font-size:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-backdrop{pointer-events:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open.ngxsmk-bottom-sheet:not(.ngxsmk-inline-container){inset:auto 0 0;transform:none;width:100%;max-width:100%;border-radius:20px 20px 0 0;margin:0;-webkit-animation:slideUpMobile .3s cubic-bezier(.16,1,.3,1);-moz-animation:slideUpMobile .3s cubic-bezier(.16,1,.3,1);animation:slideUpMobile .3s cubic-bezier(.16,1,.3,1)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open.ngxsmk-fullscreen:not(.ngxsmk-inline-container){inset:0;transform:none;width:100%;height:100%;max-height:100%;max-width:100%;border-radius:0;margin:0;-webkit-animation:fadeInScale .2s ease-out;-moz-animation:fadeInScale .2s ease-out;animation:fadeInScale .2s ease-out}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container{width:100%;max-width:100%;overflow:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container{padding:8px;width:100%;max-width:100%;box-sizing:border-box;overflow:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header>*,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects>*,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons>*{margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header{display:flex;width:100%;gap:4px;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects{-webkit-box-flex:1;-webkit-flex:1 1 0%;-ms-flex:1 1 0%;flex:1 1 0%;min-width:0;display:flex;gap:4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects>*{-webkit-box-flex:1;-webkit-flex:1 1 0%;-ms-flex:1 1 0%;flex:1 1 0%;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons{-webkit-box-flex:0;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;display:flex;gap:4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{width:44px;height:44px;min-height:44px;padding:10px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-container{height:44px;min-height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select{flex:1 1 0%;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-container{height:100%;min-height:100%;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-display{height:100%;min-height:100%;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer>*{margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button{height:44px;min-height:44px;box-sizing:border-box;padding:10px 16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid{gap:2px;margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid>*{margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-name{font-size:13px;width:100%}}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-8px,0);transform:translate3d(0,-8px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}}@-moz-keyframes fadeInDown{0%{opacity:0;-moz-transform:translate3d(0,-8px,0);transform:translate3d(0,-8px,0)}to{opacity:1;-moz-transform:translate3d(0,0,0);transform:translateZ(0)}}@keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-8px,0);-moz-transform:translate3d(0,-8px,0);-ms-transform:translate3d(0,-8px,0);transform:translate3d(0,-8px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);-moz-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translateZ(0)}}@-webkit-keyframes fadeInDownMobile{0%{opacity:0;-webkit-transform:translate3d(-50%,-8px,0);transform:translate3d(-50%,-8px,0)}to{opacity:1;-webkit-transform:translate3d(-50%,0,0);transform:translate3d(-50%,0,0)}}@-moz-keyframes fadeInDownMobile{0%{opacity:0;-moz-transform:translate3d(-50%,-8px,0);transform:translate3d(-50%,-8px,0)}to{opacity:1;-moz-transform:translate3d(-50%,0,0);transform:translate3d(-50%,0,0)}}@keyframes fadeInDownMobile{0%{opacity:0;-webkit-transform:translate3d(-50%,-8px,0);-moz-transform:translate3d(-50%,-8px,0);-ms-transform:translate3d(-50%,-8px,0);transform:translate3d(-50%,-8px,0)}to{opacity:1;-webkit-transform:translate3d(-50%,0,0);-moz-transform:translate3d(-50%,0,0);-ms-transform:translate3d(-50%,0,0);transform:translate3d(-50%,0,0)}}@-webkit-keyframes fadeInScale{0%{opacity:0;-webkit-transform:translate(-50%,-50%) scale(.95);transform:translate(-50%,-50%) scale(.95)}to{opacity:1;-webkit-transform:translate(-50%,-50%) scale(1);transform:translate(-50%,-50%) scale(1)}}@-moz-keyframes fadeInScale{0%{opacity:0;-moz-transform:translate(-50%,-50%) scale(.95);transform:translate(-50%,-50%) scale(.95)}to{opacity:1;-moz-transform:translate(-50%,-50%) scale(1);transform:translate(-50%,-50%) scale(1)}}@keyframes fadeInScale{0%{opacity:0;-webkit-transform:translate(-50%,-50%) scale(.95);-moz-transform:translate(-50%,-50%) scale(.95);-ms-transform:translate(-50%,-50%) scale(.95);transform:translate(-50%,-50%) scale(.95)}to{opacity:1;-webkit-transform:translate(-50%,-50%) scale(1);-moz-transform:translate(-50%,-50%) scale(1);-ms-transform:translate(-50%,-50%) scale(1);transform:translate(-50%,-50%) scale(1)}}@-webkit-keyframes slideUpMobile{0%{transform:translateY(100%)}to{transform:translateY(0)}}@-moz-keyframes slideUpMobile{0%{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes slideUpMobile{0%{transform:translateY(100%)}to{transform:translateY(0)}}ngxsmk-datepicker.ngxsmk-inline{display:block;width:fit-content;max-width:100%}.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode{display:block;overflow:visible}.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode .ngxsmk-popover-container.ngxsmk-inline-container{overflow:visible;position:static!important}.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode .ngxsmk-datepicker-container{-webkit-box-shadow:none!important;-moz-box-shadow:none!important;box-shadow:none!important;overflow:visible;margin-top:0!important;position:static!important;z-index:0;width:auto!important;min-width:0!important;max-width:100%!important}.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode .ngxsmk-calendar-container{width:auto!important;min-width:0!important;max-width:100%!important}.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode .ngxsmk-ranges-container,.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode .ngxsmk-ranges-container ul{width:100%;max-width:100%;min-width:0;box-sizing:border-box;overflow:visible}.ngxsmk-datepicker-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex!important;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column!important;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;min-width:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;margin:0;padding:0;position:relative;z-index:var(--datepicker-z-index-base);pointer-events:auto;background:var(--datepicker-background);-webkit-box-shadow:var(--datepicker-shadow-lg);-moz-box-shadow:var(--datepicker-shadow-lg);box-shadow:var(--datepicker-shadow-lg);border:1px solid var(--datepicker-border-color);-webkit-border-radius:var(--datepicker-border-radius);-moz-border-radius:var(--datepicker-border-radius);border-radius:var(--datepicker-border-radius)}.ngxsmk-calendar-loading{position:absolute;inset:0;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;gap:var(--datepicker-spacing-md, 12px);background:var(--datepicker-background);opacity:.95;z-index:100}.ngxsmk-calendar-loading-spinner{width:28px;height:28px;border:3px solid var(--datepicker-border-color);border-top-color:var(--datepicker-accent-color, #1976d2);-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;-webkit-animation:ngxsmk-spin .7s linear infinite;-moz-animation:ngxsmk-spin .7s linear infinite;animation:ngxsmk-spin .7s linear infinite}.ngxsmk-calendar-loading-text{font-size:var(--datepicker-font-size-sm, 14px);color:var(--datepicker-text-secondary, #666)}@-webkit-keyframes ngxsmk-spin{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes ngxsmk-spin{to{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes ngxsmk-spin{to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);transform:rotate(360deg)}}.ngxsmk-calendar-container{font-size:var(--datepicker-font-size-base);line-height:var(--datepicker-line-height);padding:var(--datepicker-spacing-xs);width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content}.ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:auto;max-width:100%;padding:var(--datepicker-spacing-xs);box-sizing:border-box}.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal,.ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal){width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:auto!important;max-width:100%!important}.ngxsmk-calendar-container.ngxsmk-time-only-mode{padding:var(--datepicker-spacing-xl)!important;min-width:320px;overflow:visible!important}.ngxsmk-calendar-container.ngxsmk-time-only-mode .ngxsmk-time-selection{overflow:visible!important;align-items:baseline;position:relative;z-index:1;margin-top:0;padding-top:0;border-top:none}.ngxsmk-calendar-container.ngxsmk-time-only-mode .ngxsmk-time-selection ngxsmk-custom-select{position:relative;z-index:10}.ngxsmk-calendar-container.ngxsmk-time-only-mode .ngxsmk-time-selection ngxsmk-custom-select[data-open=true]{z-index:10000000!important}.ngxsmk-ranges-container{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content;max-width:100%;padding:var(--datepicker-spacing-lg);background:var(--datepicker-hover-background);border-radius:var(--datepicker-radius-lg);border:1px solid var(--datepicker-border-color);box-sizing:border-box;flex-shrink:0;position:relative;z-index:var(--datepicker-z-index-base)}.ngxsmk-ranges-container ul{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;gap:var(--datepicker-spacing-sm);list-style:none;padding:0;margin:calc(var(--datepicker-spacing-sm) / -2);width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:auto;max-width:100%;overflow:visible}.ngxsmk-ranges-container ul>*{margin:calc(var(--datepicker-spacing-sm) / 2)}.ngxsmk-ranges-container li{padding:var(--datepicker-spacing-sm) var(--datepicker-spacing-md);font-size:var(--datepicker-font-size-sm);line-height:var(--datepicker-line-height);border:1px solid var(--datepicker-border-color);-webkit-border-radius:var(--datepicker-radius-md);-moz-border-radius:var(--datepicker-radius-md);border-radius:var(--datepicker-radius-md);cursor:pointer;-webkit-transition:var(--datepicker-transition);-moz-transition:var(--datepicker-transition);-o-transition:var(--datepicker-transition);transition:var(--datepicker-transition);-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;background:var(--datepicker-background);font-weight:500;-ms-touch-action:manipulation;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent}.ngxsmk-ranges-container li:hover:not(.disabled){background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);border-color:var(--datepicker-primary-color);-webkit-transform:translate3d(0,-1px,0);-moz-transform:translate3d(0,-1px,0);-ms-transform:translate3d(0,-1px,0);transform:translate3d(0,-1px,0);-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);will-change:transform}.ngxsmk-ranges-container li.disabled{cursor:not-allowed;opacity:.5;background-color:transparent!important;color:var(--datepicker-subtle-text-color)}.ngxsmk-ranges-container li.ngxsmk-preset-active{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);border-color:var(--datepicker-primary-color);font-weight:700}.ngxsmk-header{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;margin-bottom:var(--datepicker-spacing-lg);position:relative;z-index:2;gap:var(--datepicker-spacing-md);padding-bottom:var(--datepicker-spacing-md);border-bottom:1px solid var(--datepicker-border-color);margin-left:calc(var(--datepicker-spacing-md) / -2);margin-right:calc(var(--datepicker-spacing-md) / -2)}.ngxsmk-header>*{margin-left:calc(var(--datepicker-spacing-md) / 2);margin-right:calc(var(--datepicker-spacing-md) / 2)}.ngxsmk-month-year-selects{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;gap:var(--datepicker-spacing-sm);-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;margin:calc(var(--datepicker-spacing-sm) / -2)}.ngxsmk-month-year-selects>*{margin:calc(var(--datepicker-spacing-sm) / 2);cursor:pointer}.ngxsmk-month-year-selects ngxsmk-custom-select{cursor:pointer;flex:1;min-width:0;height:40px;min-height:40px}.ngxsmk-month-year-selects .ngxsmk-select-container,.ngxsmk-month-year-selects .ngxsmk-select-display{height:100%;min-height:100%;box-sizing:border-box}.ngxsmk-nav-buttons{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;gap:var(--datepicker-spacing-xs);margin:calc(var(--datepicker-spacing-xs) / -2)}.ngxsmk-nav-buttons>*{margin:calc(var(--datepicker-spacing-xs) / 2)}.ngxsmk-nav-button{padding:var(--datepicker-spacing-sm);border:1px solid transparent;-webkit-border-radius:var(--datepicker-border-radius);-moz-border-radius:var(--datepicker-border-radius);border-radius:var(--datepicker-border-radius);background:#00000008;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-webkit-align-items:center;will-change:transform;backface-visibility:hidden;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;color:var(--datepicker-text-color);-webkit-transition:var(--datepicker-transition);-moz-transition:var(--datepicker-transition);-o-transition:var(--datepicker-transition);transition:var(--datepicker-transition);min-width:36px;width:40px;height:40px;min-height:40px;box-sizing:border-box;-ms-touch-action:manipulation;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent}.ngxsmk-nav-button:hover:not(:disabled){background-color:var(--datepicker-hover-background);border-color:#0000000d;-webkit-transform:scale3d(1.05,1.05,1);-moz-transform:scale3d(1.05,1.05,1);-ms-transform:scale3d(1.05,1.05,1);transform:scale3d(1.05,1.05,1);will-change:transform}.ngxsmk-nav-button:active:not(:disabled){-webkit-transform:scale3d(.95,.95,1);-moz-transform:scale3d(.95,.95,1);-ms-transform:scale3d(.95,.95,1);transform:scale3d(.95,.95,1)}.ngxsmk-nav-button:disabled{cursor:not-allowed;opacity:.4}.ngxsmk-nav-button:focus-visible{outline:2px solid var(--datepicker-primary-color);outline-offset:2px}.ngxsmk-nav-button svg{width:18px;height:18px;stroke-width:32}.ngxsmk-days-grid-wrapper{margin-top:var(--datepicker-spacing-md);overflow:visible;position:relative;touch-action:pan-y;-webkit-overflow-scrolling:touch}.ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid-wrapper{width:100%;max-width:100%}.ngxsmk-days-grid{display:grid;grid-template-columns:repeat(7,1fr);-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;gap:var(--datepicker-spacing-xs);width:100%;position:relative;isolation:isolate;-ms-touch-action:manipulation;touch-action:manipulation;margin:calc(var(--datepicker-spacing-xs) / -2);contain:layout style;will-change:contents;transition:opacity .15s cubic-bezier(.4,0,.2,1)}.ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid{width:100%;max-width:100%}.ngxsmk-days-grid>*{margin:calc(var(--datepicker-spacing-xs) / 2)}.ngxsmk-days-grid.ngxsmk-with-week-numbers{grid-template-columns:minmax(1.5em,auto) repeat(7,1fr)}.ngxsmk-week-number,.ngxsmk-week-number-header{font-size:var(--datepicker-font-size-sm);color:var(--datepicker-subtle-text-color);display:flex;align-items:center;justify-content:center;-webkit-user-select:none;user-select:none}.ngxsmk-day-cell:has(.ngxsmk-day-secondary),.ngxsmk-day-cell:has(.ngxsmk-day-meta-label){-webkit-box-orient:vertical;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.ngxsmk-day-meta-label{font-size:.6em;line-height:1;color:var(--datepicker-subtle-text-color);margin-top:1px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ngxsmk-day-indicator{position:absolute;top:3px;right:3px;width:6px;height:6px;border-radius:50%;pointer-events:none}.ngxsmk-day-secondary{font-size:.62em;line-height:1;color:var(--datepicker-subtle-text-color);margin-top:1px}.ngxsmk-day-name{font-size:var(--datepicker-font-size-sm);padding:var(--datepicker-spacing-md) 0;color:var(--datepicker-subtle-text-color);font-weight:600;line-height:var(--datepicker-line-height);text-transform:uppercase;letter-spacing:.5px;width:100%;box-sizing:border-box}.ngxsmk-day-cell{width:40px;height:40px;min-width:40px;max-width:40px;min-height:40px;max-height:40px;position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;cursor:pointer;-webkit-border-radius:var(--datepicker-radius-sm);-moz-border-radius:var(--datepicker-radius-sm);border-radius:var(--datepicker-radius-sm);-webkit-transition:background-color .15s cubic-bezier(.4,0,.2,1);-moz-transition:background-color .15s cubic-bezier(.4,0,.2,1);-o-transition:background-color .15s cubic-bezier(.4,0,.2,1);transition:background-color .15s cubic-bezier(.4,0,.2,1);background-color:transparent;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;overflow:visible;z-index:1;margin:0 auto;contain:layout style;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;backface-visibility:hidden;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:auto;-webkit-touch-callout:none}@media(max-width:1024px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-cell{width:100%;height:auto;aspect-ratio:1/1;min-width:0;max-width:none;min-height:0;max-height:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-number{width:100%;height:100%;min-width:0;max-width:none;min-height:0;max-height:none;font-size:14px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{min-width:40px;min-height:40px;height:40px;padding:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-container{height:40px;min-height:40px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-display{min-height:100%;height:100%;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer{min-height:40px;padding:8px 12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-input-group{min-width:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input{min-height:44px;line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input::placeholder{line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input:not(:placeholder-shown){line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button{padding:var(--datepicker-spacing-xs);margin-right:var(--datepicker-spacing-xs)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-button{display:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button svg{width:16px;height:16px;min-width:16px;min-height:16px;max-width:16px;max-height:16px}}.ngxsmk-day-number{width:36px;height:36px;min-width:36px;max-width:36px;min-height:36px;max-height:36px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;color:var(--datepicker-text-color);font-size:var(--datepicker-font-size-base);line-height:var(--datepicker-line-height);position:relative;z-index:2;font-weight:500;-webkit-transition:background-color .15s cubic-bezier(.4,0,.2,1),color .15s cubic-bezier(.4,0,.2,1),-webkit-transform .15s cubic-bezier(.4,0,.2,1),border-color .15s cubic-bezier(.4,0,.2,1),-webkit-box-shadow .15s cubic-bezier(.4,0,.2,1);-moz-transition:background-color .15s cubic-bezier(.4,0,.2,1),color .15s cubic-bezier(.4,0,.2,1),-moz-transform .15s cubic-bezier(.4,0,.2,1),border-color .15s cubic-bezier(.4,0,.2,1),-moz-box-shadow .15s cubic-bezier(.4,0,.2,1);-o-transition:background-color .15s cubic-bezier(.4,0,.2,1),color .15s cubic-bezier(.4,0,.2,1),-o-transform .15s cubic-bezier(.4,0,.2,1),border-color .15s cubic-bezier(.4,0,.2,1),box-shadow .15s cubic-bezier(.4,0,.2,1);transition:background-color .15s cubic-bezier(.4,0,.2,1),color .15s cubic-bezier(.4,0,.2,1),transform .15s cubic-bezier(.4,0,.2,1),border-color .15s cubic-bezier(.4,0,.2,1),box-shadow .15s cubic-bezier(.4,0,.2,1);-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;contain:layout style;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0)}.ngxsmk-day-cell:not(.disabled):not(.empty):hover .ngxsmk-day-number{background-color:var(--datepicker-hover-background);color:var(--datepicker-primary-color);-webkit-transform:scale3d(1.1,1.1,1);-moz-transform:scale3d(1.1,1.1,1);-ms-transform:scale3d(1.1,1.1,1);transform:scale3d(1.1,1.1,1);will-change:transform}.ngxsmk-day-cell.focused{z-index:10}.ngxsmk-day-cell.focused .ngxsmk-day-number{outline:2px solid var(--datepicker-primary-color);outline-offset:2px;background-color:var(--datepicker-hover-background);z-index:11}.ngxsmk-day-cell.selected:not(.start-date):not(.end-date):not(.in-range):not(.preview-range){z-index:3}.ngxsmk-day-cell.selected:not(.start-date):not(.end-date):not(.in-range):not(.preview-range) .ngxsmk-day-number{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);position:relative;z-index:4}.ngxsmk-day-cell.start-date:not(.in-range):not(.preview-range):not(.end-date),.ngxsmk-day-cell.end-date:not(.in-range):not(.preview-range):not(.start-date){z-index:3}.ngxsmk-day-cell.start-date:not(.in-range):not(.preview-range):not(.end-date) .ngxsmk-day-number,.ngxsmk-day-cell.end-date:not(.in-range):not(.preview-range):not(.start-date) .ngxsmk-day-number{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);position:relative;z-index:4;border:none}.ngxsmk-day-cell.multiple-selected .ngxsmk-day-number{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;border:2px solid var(--datepicker-primary-contrast);-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);position:relative;z-index:2}.ngxsmk-day-cell.in-range,.ngxsmk-day-cell.preview-range{background-color:var(--datepicker-range-background);z-index:1}.ngxsmk-day-cell.in-comparison-range{position:relative}.ngxsmk-day-cell.in-comparison-range:after{content:\"\";position:absolute;right:2px;bottom:2px;left:2px;height:2px;border-radius:1px;background-color:var(--datepicker-comparison-range-color, #f59e0b);pointer-events:none;z-index:3}.ngxsmk-day-cell.start-date.in-range,.ngxsmk-day-cell.end-date.in-range,.ngxsmk-day-cell.start-date.preview-range,.ngxsmk-day-cell.end-date.preview-range{background-color:var(--datepicker-range-background);z-index:2}.ngxsmk-day-cell.start-date.in-range .ngxsmk-day-number,.ngxsmk-day-cell.end-date.in-range .ngxsmk-day-number,.ngxsmk-day-cell.start-date.preview-range .ngxsmk-day-number,.ngxsmk-day-cell.end-date.preview-range .ngxsmk-day-number{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);position:relative;z-index:7}.ngxsmk-day-cell.start-date.end-date:not(.in-range) .ngxsmk-day-number{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);position:relative;z-index:2}.ngxsmk-day-cell.start-date.end-date.in-range{background-color:var(--datepicker-range-background)}.ngxsmk-day-cell.holiday:not(.selected):not(.start-date):not(.end-date) .ngxsmk-day-number{color:#f97316;font-weight:600;position:relative}.ngxsmk-day-cell.holiday:not(.selected):not(.start-date):not(.end-date) .ngxsmk-day-number:after{content:\"\";position:absolute;bottom:4px;left:50%;transform:translate(-50%);width:4px;height:4px;background-color:#f97316;border-radius:50%}.ngxsmk-day-cell.start-date.end-date.in-range .ngxsmk-day-number{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);position:relative;z-index:2}.ngxsmk-day-cell.disabled{background-color:transparent!important;color:var(--datepicker-subtle-text-color);cursor:not-allowed;pointer-events:none;opacity:.4}.ngxsmk-day-cell.disabled.in-range,.ngxsmk-day-cell.disabled.preview-range{background-color:transparent!important}.ngxsmk-day-cell.disabled.in-range .ngxsmk-day-number,.ngxsmk-day-cell.disabled.preview-range .ngxsmk-day-number{text-decoration:line-through;text-decoration-color:var(--datepicker-error-color, #ef4444);text-decoration-thickness:2px}.ngxsmk-day-cell.empty{opacity:.3;cursor:default}.ngxsmk-day-cell.empty .ngxsmk-day-number{color:var(--datepicker-subtle-text-color)}.ngxsmk-day-cell.today:not(.selected):not(.start-date):not(.end-date):not(.in-range):not(.preview-range):not(.multiple-selected){z-index:5}.ngxsmk-day-cell.today:not(.selected):not(.start-date):not(.end-date):not(.in-range):not(.preview-range):not(.multiple-selected) .ngxsmk-day-number{border:2px solid var(--datepicker-primary-color);font-weight:600;background-color:transparent;color:var(--datepicker-primary-color);z-index:6;box-sizing:border-box}.ngxsmk-day-cell.today.selected:not(.start-date):not(.end-date):not(.in-range):not(.preview-range):not(.multiple-selected) .ngxsmk-day-number{border:none;background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);z-index:4}.ngxsmk-day-cell.today.start-date:not(.in-range):not(.preview-range):not(.end-date) .ngxsmk-day-number,.ngxsmk-day-cell.today.end-date:not(.in-range):not(.preview-range):not(.start-date) .ngxsmk-day-number{border:none;background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);z-index:4}.ngxsmk-day-cell.today.in-range:not(.start-date):not(.end-date) .ngxsmk-day-number,.ngxsmk-day-cell.today.preview-range:not(.start-date):not(.end-date) .ngxsmk-day-number{background-color:transparent;border:2px solid var(--datepicker-primary-color);color:var(--datepicker-primary-color);font-weight:600;box-sizing:border-box}.ngxsmk-day-cell.holiday .ngxsmk-day-number{color:var(--datepicker-primary-color);position:relative}.ngxsmk-day-cell.holiday .ngxsmk-day-number:after{content:\"\";position:absolute;bottom:2px;left:50%;-webkit-transform:translate3d(-50%,0,0);-moz-transform:translate3d(-50%,0,0);-ms-transform:translate3d(-50%,0,0);transform:translate3d(-50%,0,0);width:4px;height:4px;background:var(--datepicker-primary-color);-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%}.ngxsmk-time-selection{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;gap:var(--datepicker-spacing-sm);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;margin-top:var(--datepicker-spacing-lg);padding-top:var(--datepicker-spacing-lg);border-top:1px solid var(--datepicker-border-color);width:100%;overflow:visible!important;position:relative;z-index:1;min-width:0;margin-left:calc(var(--datepicker-spacing-sm) / -2);margin-right:calc(var(--datepicker-spacing-sm) / -2)}.ngxsmk-time-selection>*{margin-left:calc(var(--datepicker-spacing-sm) / 2);margin-right:calc(var(--datepicker-spacing-sm) / 2);-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-time-selection ngxsmk-custom-select{--custom-select-width: 75px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;position:relative;z-index:10;cursor:pointer}.ngxsmk-time-selection ngxsmk-custom-select[data-open=true]{z-index:10000000!important;position:relative}.ngxsmk-time-selection ngxsmk-custom-select[data-open=true] .ngxsmk-options-panel{z-index:10000001!important;position:absolute!important}.ngxsmk-popover-container.ngxsmk-has-time-selection,.ngxsmk-popover-container.ngxsmk-has-time-selection .ngxsmk-datepicker-container,.ngxsmk-popover-container.ngxsmk-has-time-selection .ngxsmk-calendar-container{overflow:visible!important}.ngxsmk-time-label{font-size:var(--datepicker-font-size-base);line-height:var(--datepicker-line-height);color:var(--datepicker-text-color);font-weight:500;margin-right:var(--datepicker-spacing-xs);white-space:nowrap;flex-shrink:0}.ngxsmk-time-separator{font-weight:600;color:var(--datepicker-text-color);font-size:var(--datepicker-font-size-lg);white-space:nowrap;flex-shrink:0}.ngxsmk-timezone-selection{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;gap:var(--datepicker-spacing-sm);margin-top:var(--datepicker-spacing-md);padding-top:var(--datepicker-spacing-md);border-top:1px solid var(--datepicker-border-color);width:100%}.ngxsmk-timezone-label{font-size:var(--datepicker-font-size-base);color:var(--datepicker-text-color);font-weight:500;white-space:nowrap}.ngxsmk-timezone-selection ngxsmk-custom-select{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.ngxsmk-time-range-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:var(--datepicker-spacing-md);width:100%;margin-top:var(--datepicker-spacing-lg);padding-top:var(--datepicker-spacing-lg);border-top:1px solid var(--datepicker-border-color)}.ngxsmk-time-range-start,.ngxsmk-time-range-end{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;gap:var(--datepicker-spacing-sm)}.ngxsmk-time-range-label{font-size:var(--datepicker-font-size-base);line-height:var(--datepicker-line-height);color:var(--datepicker-text-color);font-weight:500;white-space:nowrap;min-width:40px}.ngxsmk-footer{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end;gap:var(--datepicker-spacing-sm);margin-top:var(--datepicker-spacing-sm);padding-top:var(--datepicker-spacing-sm);border-top:1px solid var(--datepicker-border-color);margin-left:calc(var(--datepicker-spacing-sm) / -2);margin-right:calc(var(--datepicker-spacing-sm) / -2)}.ngxsmk-footer>*{margin-left:calc(var(--datepicker-spacing-sm) / 2);margin-right:calc(var(--datepicker-spacing-sm) / 2)}.ngxsmk-clear-button-footer,.ngxsmk-close-button{padding:var(--datepicker-spacing-xs) var(--datepicker-spacing-md);-webkit-border-radius:var(--datepicker-radius-md);-moz-border-radius:var(--datepicker-radius-md);border-radius:var(--datepicker-radius-md);font-size:var(--datepicker-font-size-base);line-height:var(--datepicker-line-height);cursor:pointer;-webkit-transition:var(--datepicker-transition);-moz-transition:var(--datepicker-transition);-o-transition:var(--datepicker-transition);transition:var(--datepicker-transition);border:1px solid var(--datepicker-border-color);font-weight:500;min-height:40px;-ms-touch-action:manipulation;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent}.ngxsmk-clear-button-footer{background:var(--datepicker-background);color:var(--datepicker-text-color)}.ngxsmk-clear-button-footer:hover:not(:disabled){background-color:var(--datepicker-hover-background);border-color:var(--datepicker-border-color)}.ngxsmk-close-button{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);border-color:var(--datepicker-primary-color)}.ngxsmk-close-button:hover:not(:disabled){background-color:var(--datepicker-primary-color);opacity:.9;-webkit-transform:translate3d(0,-1px,0);-moz-transform:translate3d(0,-1px,0);-ms-transform:translate3d(0,-1px,0);transform:translate3d(0,-1px,0);-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);will-change:transform}.ngxsmk-close-button:active:not(:disabled){-webkit-transform:translate3d(0,0,0);-moz-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translateZ(0)}.ngxsmk-clear-button-footer:focus-visible,.ngxsmk-close-button:focus-visible{outline:2px solid var(--datepicker-primary-color);outline-offset:2px}@media(min-width:320px)and (max-width:1023px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-bottom-sheet):not(.ngxsmk-fullscreen){position:fixed;inset:50% auto auto 50%;-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%);transform:translate(-50%,-50%);width:-webkit-fit-content;width:-moz-fit-content;width:100%;min-width:280px;max-width:min(100vw - 32px,500px);max-height:calc(100vh - 64px);max-height:calc(100dvh - 64px);z-index:2147483647;margin:0;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch;display:block;visibility:visible;opacity:1;will-change:transform,opacity}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open.ngxsmk-bottom-sheet:not(.ngxsmk-inline-container){position:fixed;inset:auto 0 0;-webkit-transform:translateY(0);-moz-transform:translateY(0);-ms-transform:translateY(0);-o-transform:translateY(0);transform:translateY(0);width:100%;max-width:100%;max-height:min(90dvh,90dvh - env(safe-area-inset-bottom,0px));padding-bottom:env(safe-area-inset-bottom,0px);border-radius:16px 16px 0 0;margin:0;z-index:2147483647;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch;display:block;visibility:visible;opacity:1;will-change:transform,opacity;-webkit-transition:transform .3s cubic-bezier(.4,0,.2,1),opacity .3s cubic-bezier(.4,0,.2,1);-moz-transition:transform .3s cubic-bezier(.4,0,.2,1),opacity .3s cubic-bezier(.4,0,.2,1);-o-transition:transform .3s cubic-bezier(.4,0,.2,1),opacity .3s cubic-bezier(.4,0,.2,1);transition:transform .3s cubic-bezier(.4,0,.2,1),opacity .3s cubic-bezier(.4,0,.2,1)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open.ngxsmk-fullscreen:not(.ngxsmk-inline-container){position:fixed;inset:0;width:100%;max-width:100%;max-height:100vh;max-height:100dvh;padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px);height:calc(100vh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px));height:calc(100dvh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px));border-radius:0;margin:0;z-index:2147483647;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch;display:block;visibility:visible;opacity:1;will-change:transform,opacity}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-has-multi-calendar){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:280px;max-width:min(100vw - 32px,500px);height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;max-height:calc(100vh - 64px)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:280px;max-width:min(100vw - 32px,800px);overflow-x:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-has-time-selection.ngxsmk-popover-open:not(.ngxsmk-inline-container){overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container){overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-has-time-selection.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-datepicker-container{overflow:visible;overflow-y:visible;max-height:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-datepicker-container{overflow:visible;overflow-y:visible;max-height:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-calendar-container{overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container)>*{pointer-events:auto}.ngxsmk-datepicker-wrapper.ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-popover-container:not(.ngxsmk-inline-container){display:block;visibility:visible;opacity:1;pointer-events:none}.ngxsmk-datepicker-wrapper.ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-popover-container:not(.ngxsmk-inline-container)>*{pointer-events:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-input-group{min-width:100%;width:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input{padding:10px 12px;min-height:44px;line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input::placeholder{line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input:not(:placeholder-shown){line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button{padding:8px;margin-right:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-button{display:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button svg{width:16px;height:16px;min-width:16px;min-height:16px;max-width:16px;max-height:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-cell{width:100%;height:auto;aspect-ratio:1/1;min-width:0;max-width:48px;min-height:0;max-height:48px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-number{width:85%;height:85%;min-width:30px;max-width:38px;min-height:30px;max-height:38px;font-size:var(--datepicker-font-size-base);display:flex;align-items:center;justify-content:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-name{font-size:11px;font-weight:700;padding:8px 0;width:100%;text-align:center;color:var(--datepicker-subtle-text-color);display:flex;align-items:center;justify-content:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container{padding:0;width:100%;min-width:0;max-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:var(--datepicker-spacing-xs) auto 0;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:var(--datepicker-spacing-xs) 0 0 0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid{gap:4px;padding:0;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;width:100%;max-width:100%;margin:0 auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid{width:100%;max-width:100%;margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header>*,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects>*,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons>*{margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header{display:flex;padding:4px;margin-bottom:var(--datepicker-spacing-sm);width:100%;max-width:100%;margin-left:0;margin-right:0;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;gap:2px;box-sizing:border-box;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects{-webkit-box-flex:1;-webkit-flex:1 1 0%;-ms-flex:1 1 0%;flex:1 1 0%;width:auto;gap:4px;min-width:0;display:flex}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects>*{-webkit-box-flex:1;-webkit-flex:1 1 0%;-ms-flex:1 1 0%;flex:1 1 0%;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons{-webkit-box-flex:0;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;display:flex;gap:2px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-container{height:36px;min-height:36px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{width:36px;height:36px;padding:6px;min-height:36px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button svg{width:20px;height:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-container,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-display{height:100%;min-height:100%;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-select-display{padding:0 6px;font-size:13px;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-arrow-icon{width:12px;height:12px;margin-left:2px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection{margin-top:var(--datepicker-spacing-sm);padding-top:var(--datepicker-spacing-sm);width:100%;max-width:100%;margin-left:auto;margin-right:auto;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;justify-content:center;gap:2px;overflow:visible;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding-left:0;padding-right:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection>*{-webkit-flex-shrink:1;-ms-flex-negative:1;flex-shrink:1;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select{height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-separator{font-size:20px;padding:0 4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-label{font-size:12px;font-weight:600;color:var(--datepicker-text-color);white-space:nowrap;margin-right:2px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer>*{margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer{margin-top:var(--datepicker-spacing-sm);padding-top:var(--datepicker-spacing-sm);width:100%;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;justify-content:center;gap:12px 8px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button{-webkit-box-flex:1;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;min-width:0;height:36px;min-height:36px;padding:6px 12px;font-size:14px;font-weight:600;border-radius:12px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container{width:100%;min-width:0;max-width:100%;overflow:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container ul{flex-wrap:wrap;justify-content:center;gap:8px 6px;overflow:visible;width:100%;min-width:0;max-width:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container li{flex-shrink:0;white-space:nowrap;min-width:fit-content}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container{width:100%;min-width:0;max-width:100%;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;position:relative;z-index:var(--datepicker-z-index-base)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container:has(.ngxsmk-calendar-container.ngxsmk-has-multi-calendar){height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}}@media(min-width:600px)and (max-width:767px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input{font-size:15px;padding:12px 16px;min-height:44px;line-height:1.5}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button{padding:10px;margin-right:8px;min-width:44px;min-height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button svg{width:20px;height:20px;min-width:20px;min-height:20px;max-width:20px;max-height:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:360px;max-width:min(90vw,550px);max-height:calc(100dvh - 40px);position:fixed;inset:50% auto auto 50%;-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%);transform:translate(-50%,-50%);border-radius:20px;padding:0;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch;z-index:2147483647}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-has-time-selection.ngxsmk-popover-open:not(.ngxsmk-inline-container),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container){overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-has-time-selection.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-datepicker-container,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-datepicker-container,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-calendar-container{overflow:visible;overflow-y:visible;max-height:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container)>*{pointer-events:auto}.ngxsmk-datepicker-wrapper.ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-popover-container:not(.ngxsmk-inline-container),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){display:block;visibility:visible;opacity:1;pointer-events:none}.ngxsmk-datepicker-wrapper.ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-popover-container:not(.ngxsmk-inline-container)>*,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container)>*{pointer-events:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-popover-open):not(.ngxsmk-inline-container){display:none;visibility:hidden;opacity:0;pointer-events:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container{padding:0;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container{padding:20px;max-width:100%;width:100%;border-radius:20px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:16px;overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-has-time-selection .ngxsmk-calendar-container,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container.ngxsmk-time-only-mode{overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container{padding:16px;width:100%;max-width:100%;margin-bottom:0;border-radius:0;border-bottom:1px solid var(--datepicker-border-color);background:var(--datepicker-background);order:-1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container ul{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;overflow-x:visible;overflow-y:visible;padding-bottom:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container li{padding:12px 18px;line-height:1.4;min-height:44px;white-space:nowrap;text-align:center;border-radius:12px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header{width:100%;padding:16px 12px;margin-bottom:16px;gap:16px;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;border-bottom:1px solid var(--datepicker-border-color);-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects{gap:12px;font-size:15px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select{height:44px;min-height:44px;font-size:15px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons{gap:10px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{width:44px;height:44px;min-width:44px;min-height:44px;padding:12px;border-radius:12px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button svg{width:22px;height:22px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:0;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid{gap:6px;width:100%;max-width:100%;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-name{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-size:13px;font-weight:600;padding:12px 0;text-align:center;color:var(--datepicker-subtle-text-color)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-cell{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;aspect-ratio:1;min-height:44px;max-height:none;margin:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-number{width:100%;height:100%;min-width:0;max-width:100%;min-height:44px;max-height:none;font-size:15px;font-weight:500;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection{gap:12px;padding:20px 12px;margin-top:16px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;overflow-x:visible;overflow-y:hidden}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-label{font-size:15px;font-weight:500;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select{--custom-select-width: 80px !important;min-width:80px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-separator{font-size:22px;font-weight:600;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;padding:0 8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer{gap:12px;padding:20px 12px 16px;margin-top:16px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:end;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button{padding:14px 24px;font-size:15px;font-weight:500;min-height:44px;-webkit-box-flex:0;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;min-width:140px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container{width:100%;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;gap:24px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-auto{flex-direction:row;flex-wrap:wrap;justify-content:center;gap:24px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-vertical{flex-direction:column;gap:24px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal{flex-direction:row;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;gap:20px;padding:0 12px;scroll-snap-type:x mandatory;flex-wrap:nowrap}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{scroll-snap-align:start;min-width:320px;max-width:340px;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container:not(.ngxsmk-calendar-vertical) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{min-width:calc(50% - 12px);max-width:calc(50% - 12px);width:calc(50% - 12px);flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month-header{font-size:16px;font-weight:600;padding:16px 12px;margin-bottom:16px;text-align:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){padding:20px;max-width:100%;width:100%;overflow-x:visible;overflow-y:visible;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal){max-width:min(90vw,800px);overflow-x:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-grid,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-grid{gap:12px;padding:16px 12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-cell,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-cell{min-height:44px;padding:14px 18px;font-size:15px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-view{padding:16px 12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-header{padding:16px 12px;margin-bottom:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-controls{gap:14px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-out,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-in{min-width:44px;min-height:44px;padding:12px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-range{font-size:15px;padding:14px 18px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-container{padding:16px 12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-month{min-height:80px;padding:14px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-view{padding:16px 12px;gap:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-header{padding:16px 12px;gap:14px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-label{font-size:15px;font-weight:500}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-value{font-size:18px;font-weight:600}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-container{padding:16px 12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider{width:100%;height:44px}}@media(min-width:1024px){.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){position:absolute!important;top:calc(100% + 8px)!important;left:0!important;transform:none!important;-webkit-transform:none!important;-moz-transform:none!important;-ms-transform:none!important;-o-transform:none!important;width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:280px!important;max-width:min(90vw,500px)!important;max-height:none!important;z-index:2147483647!important;margin:0!important;overflow:visible!important;overflow-y:visible!important;-webkit-overflow-scrolling:auto!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-has-multi-calendar),.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:auto!important;max-width:min(95vw,1400px)!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal),.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal){width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:auto!important;max-width:min(95vw,1400px)!important}.ngxsmk-datepicker-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content;max-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.ngxsmk-datepicker-wrapper .ngxsmk-calendar-container{padding:var(--datepicker-spacing-md);width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content;max-width:100%;margin:0 auto}.ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:auto;max-width:100%;margin:0;padding:var(--datepicker-spacing-md, 16px);box-sizing:border-box}.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal,.ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal){width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:auto!important;max-width:100%!important}.ngxsmk-days-grid-wrapper{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;margin:var(--datepicker-spacing-sm) auto 0}.ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:var(--datepicker-spacing-sm) 0 0 0}.ngxsmk-days-grid{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;margin:0 auto}.ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid{width:100%;max-width:100%;margin:0}.ngxsmk-ranges-container{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content;padding:var(--datepicker-spacing-lg);border-right:none;border-bottom:1px solid var(--datepicker-border-color);border-radius:var(--datepicker-radius-lg) var(--datepicker-radius-lg) 0 0;background:var(--datepicker-hover-background)}.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode .ngxsmk-ranges-container{width:100%!important;max-width:100%!important;min-width:0!important}.ngxsmk-ranges-container ul{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:start;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;gap:var(--datepicker-spacing-xs);width:100%;min-width:100%;max-width:100%;overflow:visible}.ngxsmk-ranges-container li{padding:var(--datepicker-spacing-sm) var(--datepicker-spacing-md);margin-bottom:0;border:none;font-size:var(--datepicker-font-size-sm);width:auto;text-align:center;-webkit-box-flex:0;-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto}.ngxsmk-day-cell{width:40px;height:40px;min-width:40px;max-width:40px;min-height:40px;max-height:40px}.ngxsmk-day-number{width:36px;height:36px;min-width:36px;max-width:36px;min-height:36px;max-height:36px;font-size:var(--datepicker-font-size-base)}.ngxsmk-header{width:100%;margin-left:auto;margin-right:auto}.ngxsmk-datepicker-container .ngxsmk-calendar-container{padding-left:var(--datepicker-spacing-sm);padding-right:var(--datepicker-spacing-sm)}.ngxsmk-time-selection{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;margin-left:auto;margin-right:auto}.ngxsmk-day-name{width:auto!important;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content}}@media(min-width:1024px){.ngxsmk-popover-container:not(.ngxsmk-inline-container){width:auto;min-width:600px;max-width:800px}.ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:auto!important;max-width:min(95vw,1400px)!important}.ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal){width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:auto!important;max-width:min(95vw,1400px)!important}.ngxsmk-datepicker-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;width:100%;min-width:0;max-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.ngxsmk-datepicker-container:has(.ngxsmk-calendar-container.ngxsmk-has-multi-calendar){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;min-width:0}.ngxsmk-datepicker-wrapper .ngxsmk-calendar-container{padding:var(--datepicker-spacing-2xl);width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content}.ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:auto;max-width:100%;padding:var(--datepicker-spacing-md, 16px);box-sizing:border-box}.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal,.ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal){width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:auto!important;max-width:100%!important}.ngxsmk-ranges-container{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content;padding:var(--datepicker-spacing-xl);border-right:1px solid var(--datepicker-border-color);-webkit-border-radius:var(--datepicker-radius-lg) 0 0 var(--datepicker-radius-lg);-moz-border-radius:var(--datepicker-radius-lg) 0 0 var(--datepicker-radius-lg);border-radius:var(--datepicker-radius-lg) 0 0 var(--datepicker-radius-lg);background:var(--datepicker-hover-background)}.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode .ngxsmk-ranges-container{width:100%!important;max-width:100%!important;min-width:0!important;border-right:none!important;border-bottom:1px solid var(--datepicker-border-color)!important;border-radius:var(--datepicker-radius-lg) var(--datepicker-radius-lg) 0 0!important}.ngxsmk-ranges-container ul{-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:start;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;gap:var(--datepicker-spacing-xs);width:100%;min-width:100%;max-width:100%;overflow:visible}.ngxsmk-ranges-container li{padding:var(--datepicker-spacing-md) var(--datepicker-spacing-lg);margin-bottom:0;border:none;font-size:var(--datepicker-font-size-base);width:100%;text-align:left}.ngxsmk-day-cell{width:44px;height:44px;min-width:44px;max-width:44px;min-height:44px;max-height:44px}.ngxsmk-day-number{width:40px;height:40px;min-width:40px;max-width:40px;min-height:40px;max-height:40px;font-size:var(--datepicker-font-size-base)}}@media(hover:none)and (pointer:coarse){.ngxsmk-nav-button:hover:not(:disabled){background-color:transparent;transform:none}.ngxsmk-day-cell:not(.disabled):not(.empty):hover .ngxsmk-day-number{background-color:transparent;color:var(--datepicker-text-color);transform:none}.ngxsmk-clear-button:hover:not(:disabled){color:var(--datepicker-subtle-text-color);background:transparent}.ngxsmk-ranges-container li:hover:not(.disabled){background-color:var(--datepicker-background);color:var(--datepicker-text-color);border-color:var(--datepicker-border-color);transform:none;box-shadow:none}.ngxsmk-close-button:hover:not(:disabled){transform:none;box-shadow:none}}@media print{.ngxsmk-datepicker-wrapper{display:none}}.ngxsmk-day-cell:focus-visible{outline:none}.ngxsmk-day-cell:focus-visible .ngxsmk-day-number{outline:2px solid var(--datepicker-primary-color);outline-offset:2px;background-color:var(--datepicker-hover-background)}.ngxsmk-ranges-container::-webkit-scrollbar{width:6px}.ngxsmk-ranges-container::-webkit-scrollbar-track{background:var(--datepicker-hover-background);border-radius:var(--datepicker-radius-sm)}.ngxsmk-ranges-container::-webkit-scrollbar-thumb{background:var(--datepicker-subtle-text-color);border-radius:var(--datepicker-radius-sm)}.ngxsmk-ranges-container::-webkit-scrollbar-thumb:hover{background:var(--datepicker-text-color)}.ngxsmk-year-grid-container,.ngxsmk-decade-grid-container{overflow-y:auto;overflow-x:hidden;position:relative;will-change:transform;scroll-behavior:smooth}.ngxsmk-year-grid-container::-webkit-scrollbar,.ngxsmk-decade-grid-container::-webkit-scrollbar{width:8px}.ngxsmk-year-grid-container::-webkit-scrollbar-track,.ngxsmk-decade-grid-container::-webkit-scrollbar-track{background:var(--datepicker-bg-color)}.ngxsmk-year-grid-container::-webkit-scrollbar-thumb,.ngxsmk-decade-grid-container::-webkit-scrollbar-thumb{background:var(--datepicker-subtle-text-color);border-radius:var(--datepicker-radius-sm)}.ngxsmk-year-grid-container::-webkit-scrollbar-thumb:hover,.ngxsmk-decade-grid-container::-webkit-scrollbar-thumb:hover{background:var(--datepicker-text-color)}.ngxsmk-year-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--datepicker-spacing-sm);padding:var(--datepicker-spacing-md)}.ngxsmk-decade-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--datepicker-spacing-sm);padding:var(--datepicker-spacing-md)}.ngxsmk-year-cell,.ngxsmk-decade-cell{padding:var(--datepicker-spacing-md);border:1px solid var(--datepicker-border-color);border-radius:var(--datepicker-border-radius);background:var(--datepicker-background);color:var(--datepicker-text-color);font-size:var(--datepicker-font-size-base);font-weight:500;cursor:pointer;transition:var(--datepicker-transition);text-align:center}.ngxsmk-decade-cell{padding:var(--datepicker-spacing-lg)}.ngxsmk-year-cell:hover:not(:disabled),.ngxsmk-decade-cell:hover:not(:disabled){background:var(--datepicker-hover-background);border-color:var(--datepicker-primary-color)}.ngxsmk-year-cell.selected,.ngxsmk-decade-cell.selected{background:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);border-color:var(--datepicker-primary-color)}.ngxsmk-year-cell.today{border-color:var(--datepicker-primary-color);font-weight:600}.ngxsmk-year-cell:disabled,.ngxsmk-decade-cell:disabled{opacity:.5;cursor:not-allowed}.ngxsmk-year-display,.ngxsmk-decade-display{flex:1;display:flex;align-items:center;justify-content:center;font-size:var(--datepicker-font-size-base);font-weight:600;color:var(--datepicker-text-color)}.ngxsmk-view-toggle{background:transparent;border:none;color:var(--datepicker-text-color);font-size:var(--datepicker-font-size-base);font-weight:600;cursor:pointer;padding:var(--datepicker-spacing-sm) var(--datepicker-spacing-md);border-radius:var(--datepicker-border-radius);transition:var(--datepicker-transition)}.ngxsmk-view-toggle:hover:not(:disabled){background:var(--datepicker-hover-background)}.ngxsmk-view-toggle:disabled{opacity:.5;cursor:not-allowed}.ngxsmk-timeline-view{padding:var(--datepicker-spacing-md)}.ngxsmk-timeline-header{margin-bottom:var(--datepicker-spacing-md)}.ngxsmk-timeline-controls{display:flex;align-items:center;justify-content:space-between;gap:var(--datepicker-spacing-md)}.ngxsmk-timeline-zoom-in,.ngxsmk-timeline-zoom-out{width:32px;height:32px;border:1px solid var(--datepicker-border-color);border-radius:var(--datepicker-border-radius);background:var(--datepicker-background);color:var(--datepicker-text-color);font-size:var(--datepicker-font-size-lg);font-weight:600;cursor:pointer;transition:var(--datepicker-transition);display:flex;align-items:center;justify-content:center}.ngxsmk-timeline-zoom-in:hover:not(:disabled),.ngxsmk-timeline-zoom-out:hover:not(:disabled){background:var(--datepicker-hover-background);border-color:var(--datepicker-primary-color)}.ngxsmk-timeline-zoom-in:disabled,.ngxsmk-timeline-zoom-out:disabled{opacity:.5;cursor:not-allowed}.ngxsmk-timeline-range{font-size:var(--datepicker-font-size-sm);color:var(--datepicker-subtle-text-color);font-weight:500}.ngxsmk-timeline-container{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;overscroll-behavior:contain}.ngxsmk-timeline-track{display:flex;gap:var(--datepicker-spacing-xs);min-width:max-content;padding:var(--datepicker-spacing-sm) 0}.ngxsmk-timeline-month{min-width:80px;padding:var(--datepicker-spacing-md);border:1px solid var(--datepicker-border-color);border-radius:var(--datepicker-border-radius);background:var(--datepicker-background);color:var(--datepicker-text-color);cursor:pointer;transition:var(--datepicker-transition);text-align:center;display:flex;flex-direction:column;gap:var(--datepicker-spacing-xs)}.ngxsmk-timeline-month:hover:not(.selected){background:var(--datepicker-hover-background);border-color:var(--datepicker-primary-color)}.ngxsmk-timeline-month.selected{background:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);border-color:var(--datepicker-primary-color)}.ngxsmk-timeline-month-label{font-size:var(--datepicker-font-size-sm);font-weight:600;text-transform:uppercase}.ngxsmk-timeline-month-year{font-size:var(--datepicker-font-size-xs);opacity:.8}.ngxsmk-time-slider-view{padding:var(--datepicker-spacing-lg)}.ngxsmk-time-slider-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--datepicker-spacing-sm)}.ngxsmk-time-slider-label{font-size:var(--datepicker-font-size-sm);font-weight:600;color:var(--datepicker-text-color)}.ngxsmk-time-slider-value{font-size:var(--datepicker-font-size-base);font-weight:600;color:var(--datepicker-primary-color);font-variant-numeric:tabular-nums}.ngxsmk-time-slider-container{margin-bottom:var(--datepicker-spacing-lg)}.ngxsmk-time-slider{width:100%;height:8px;border-radius:4px;background:var(--datepicker-hover-background);outline:none;-webkit-appearance:none;appearance:none}.ngxsmk-time-slider::-webkit-slider-thumb{-webkit-appearance:none;width:20px;height:20px;border-radius:50%;background:var(--datepicker-primary-color);cursor:pointer;transition:var(--datepicker-transition)}.ngxsmk-time-slider::-webkit-slider-thumb:hover{transform:scale(1.1);box-shadow:var(--datepicker-shadow-md)}.ngxsmk-time-slider::-moz-range-thumb{width:20px;height:20px;border-radius:50%;background:var(--datepicker-primary-color);cursor:pointer;border:none;transition:var(--datepicker-transition)}.ngxsmk-time-slider::-moz-range-thumb:hover{transform:scale(1.1);box-shadow:var(--datepicker-shadow-md)}.ngxsmk-time-slider:disabled{opacity:.5;cursor:not-allowed}.ngxsmk-time-slider:disabled::-webkit-slider-thumb,.ngxsmk-time-slider:disabled::-moz-range-thumb{cursor:not-allowed}.ngxsmk-datepicker-wrapper[dir=rtl],.ngxsmk-datepicker-wrapper.ngxsmk-rtl{direction:rtl}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-input-group,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-input-group{flex-direction:row-reverse}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-clear-button,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-clear-button,.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-calendar-button,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-calendar-button{margin-right:0;margin-left:var(--datepicker-spacing-sm)}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-popover-container,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-popover-container{left:auto;right:0}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-header,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-header,.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-nav-buttons,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-nav-buttons{flex-direction:row-reverse}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-nav-button:first-child svg,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-nav-button:first-child svg{transform:scaleX(-1)}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-nav-button:last-child svg,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-nav-button:last-child svg{transform:scaleX(-1)}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-days-grid,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-days-grid{direction:rtl}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-time-selection,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-time-selection,.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-footer,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-footer{flex-direction:row-reverse}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-ranges-container ul,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-ranges-container ul{direction:rtl;text-align:right}@media(max-width:1023px){.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-popover-container:not(.ngxsmk-inline-container),.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-popover-container:not(.ngxsmk-inline-container){left:auto;right:auto}}.ngxsmk-multi-calendar-container{display:flex;flex-direction:column;gap:var(--datepicker-spacing-xl, 32px);width:100%;align-items:stretch}.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar{flex-direction:row;flex-wrap:wrap;justify-content:flex-start;align-items:flex-start;width:100%;gap:var(--datepicker-spacing-xl, 32px)}.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal,.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal{flex-direction:row!important;flex-wrap:nowrap!important;width:100%!important;overflow-x:auto;-webkit-overflow-scrolling:touch;gap:var(--datepicker-spacing-xl, 32px)!important}.ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical,.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-vertical{flex-direction:column!important;flex-wrap:nowrap!important;width:100%!important;overflow-y:auto;-webkit-overflow-scrolling:touch;gap:var(--datepicker-spacing-xl, 32px)!important}.ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical .ngxsmk-calendar-month.ngxsmk-calendar-month-multi,.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-vertical .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{width:100%!important;max-width:100%!important;min-width:100%!important;margin:0!important}.ngxsmk-multi-calendar-container.ngxsmk-calendar-auto,.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-auto{flex-direction:row;flex-wrap:wrap;width:100%}.ngxsmk-calendar-month{width:100%;flex-shrink:0;display:flex;flex-direction:column}.ngxsmk-calendar-month.ngxsmk-calendar-month-multi{flex:0 0 auto;min-width:280px;width:auto;display:flex;flex-direction:column;margin:0;padding:0;box-sizing:border-box}.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi,.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{flex:0 0 auto!important;margin:0!important;box-sizing:border-box!important}.ngxsmk-calendar-month .ngxsmk-days-grid-wrapper,.ngxsmk-calendar-month .ngxsmk-days-grid{width:100%;max-width:100%}.ngxsmk-calendar-month-header{padding:var(--datepicker-spacing-sm, 8px) var(--datepicker-spacing-md, 12px);text-align:center;font-weight:600;font-size:var(--datepicker-font-size-base, 14px);color:var(--datepicker-text-color, #1f2937);border-bottom:1px solid var(--datepicker-border-color, #e5e7eb);margin-bottom:var(--datepicker-spacing-sm, 8px)}.ngxsmk-calendar-month-title{display:block}@media(min-width:768px)and (max-width:1023px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input{font-size:15px;padding:12px 16px;min-height:44px;line-height:1.5}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button{padding:10px;margin-right:8px;min-width:44px;min-height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button svg{width:20px;height:20px;min-width:20px;min-height:20px;max-width:20px;max-height:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:400px;max-width:min(90vw,600px);max-height:calc(100dvh - 40px);position:fixed;inset:50% auto auto 50%;-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%);transform:translate(-50%,-50%);padding:0;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch;z-index:2147483647}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-has-multi-calendar){min-width:400px;max-width:min(95vw,900px)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal){min-width:400px;max-width:min(95vw,1000px);overflow-x:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-has-time-selection.ngxsmk-popover-open:not(.ngxsmk-inline-container),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container){overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container{padding:0;width:100%;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container{padding:24px;max-width:100%;width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:20px;overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container{padding:20px;width:100%;max-width:100%;margin-bottom:0;border-radius:0;border-bottom:1px solid var(--datepicker-border-color);background:var(--datepicker-background);order:-1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container ul{display:grid;gap:12px;overflow-x:visible;overflow-y:visible;padding-bottom:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container li{padding:14px 20px;line-height:1.4;min-height:44px;white-space:nowrap;text-align:center;border-radius:12px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header{padding:20px 16px;margin-bottom:20px;gap:20px;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;border-bottom:1px solid var(--datepicker-border-color);-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects{gap:14px;font-size:16px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select{height:44px;min-height:44px;font-size:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons{gap:12px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{width:44px;height:44px;min-width:44px;min-height:44px;padding:12px;border-radius:12px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button svg{width:24px;height:24px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:0;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid{gap:8px;width:100%;max-width:100%;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-name{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-weight:600;padding:14px 0;text-align:center;color:var(--datepicker-subtle-text-color)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-cell{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;aspect-ratio:1;min-height:48px;max-height:none;margin:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-number{width:100%;height:100%;min-width:0;max-width:100%;min-height:48px;max-height:none;font-size:16px;font-weight:500;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection{gap:14px;padding:24px 16px;margin-top:20px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;overflow-x:visible;overflow-y:hidden}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-label{font-size:16px;font-weight:500;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select{--custom-select-width: 90px !important;min-width:90px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-separator{font-size:24px;font-weight:600;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;padding:0 10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer{gap:14px;padding:24px 16px 20px;margin-top:20px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:end;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button{padding:16px 28px;font-size:16px;font-weight:500;min-height:44px;-webkit-box-flex:0;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;min-width:160px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container{width:100%;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;gap:28px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-auto{flex-direction:row;flex-wrap:wrap;gap:28px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-vertical{flex-direction:column;gap:28px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal{flex-direction:row;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;gap:24px;padding:0 16px;scroll-snap-type:x mandatory;flex-wrap:nowrap}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{scroll-snap-align:start;min-width:360px;max-width:380px;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container:not(.ngxsmk-calendar-vertical) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{min-width:calc(50% - 14px);max-width:calc(50% - 14px);width:calc(50% - 14px);flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid{width:100%;max-width:100%;margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month-header{font-size:17px;font-weight:600;padding:18px 16px;margin-bottom:18px;text-align:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal){max-width:min(95vw,1000px);overflow-x:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){padding:24px;max-width:100%;width:100%;overflow-x:visible;overflow-y:visible;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}.ngxsmk-datepicker-wrapper .ngxsmk-multi-calendar-container{height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}}@media(min-width:768px)and (max-width:1023px)and (orientation:landscape){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){max-width:min(95vw,800px)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-auto{flex-direction:row;flex-wrap:nowrap}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{min-width:calc(33.333% - 19px);max-width:calc(33.333% - 19px);width:calc(33.333% - 19px)}}@media(min-width:768px)and (max-width:1023px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-grid,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-grid{gap:14px;padding:20px 16px;grid-template-columns:repeat(5,1fr)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-cell,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-cell{min-height:44px;padding:16px 20px;font-size:16px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-view{padding:20px 16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-header{padding:20px 16px;margin-bottom:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-controls{gap:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-out,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-in{min-width:44px;min-height:44px;padding:12px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-range{font-size:16px;padding:16px 20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-container{padding:20px 16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-month{min-height:90px;padding:16px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-view{padding:20px 16px;gap:24px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-header{padding:20px 16px;gap:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-label{font-size:16px;font-weight:500}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-value{font-size:20px;font-weight:600}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-container{padding:20px 16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider{width:100%;height:44px}}@media(min-width:769px)and (max-width:1024px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{min-width:260px;max-width:300px}}@media(min-width:480px)and (max-width:599px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input{padding:10px 14px;min-height:44px;line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input::placeholder{line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input:not(:placeholder-shown){line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button{padding:10px;margin-right:8px;min-width:44px;min-height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button svg{width:18px;height:18px;min-width:18px;min-height:18px;max-width:18px;max-height:18px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:320px;max-width:calc(100vw - 24px);max-height:calc(100dvh - 40px);position:fixed;inset:50% auto auto 50%;-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%);transform:translate(-50%,-50%);border-radius:16px;padding:0;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container{padding:0;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container{padding:16px;max-width:100%;width:100%;border-radius:16px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container{padding:12px;width:100%;max-width:100%;margin-bottom:0;border-radius:0;border-bottom:1px solid var(--datepicker-border-color);background:var(--datepicker-background);order:-1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container ul{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;overflow-x:visible;overflow-y:visible;padding-bottom:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container li{padding:12px 16px;font-size:13px;line-height:1.4;min-height:44px;white-space:nowrap;text-align:center;border-radius:10px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header{align-items:center;padding:12px 8px;margin-bottom:12px;gap:12px;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;border-bottom:1px solid var(--datepicker-border-color);-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects{gap:10px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select{height:44px;min-height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons{gap:8px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{width:44px;height:44px;min-width:44px;min-height:44px;padding:10px;border-radius:10px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button svg{width:20px;height:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:0;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid{gap:5px;width:100%;max-width:100%;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-name{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-size:12px;font-weight:600;padding:10px 0;text-align:center;color:var(--datepicker-subtle-text-color)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-cell{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;aspect-ratio:1;min-height:44px;max-height:none;margin:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-number{width:100%;height:100%;min-width:0;max-width:100%;min-height:44px;max-height:none;font-weight:500;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection{gap:10px;align-items:baseline;padding:16px 8px;margin-top:12px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;overflow-x:visible;overflow-y:hidden}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-label{font-weight:500;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select{--custom-select-width: 75px !important;min-width:75px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-separator{font-size:20px;font-weight:600;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;padding:0 6px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer{gap:12px;padding:16px 8px 12px;margin-top:12px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:end;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button{padding:12px 20px;font-weight:500;min-height:44px;-webkit-box-flex:0;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;min-width:120px;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container{width:100%;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-vertical{flex-direction:column;gap:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal{flex-direction:row;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;gap:16px;padding:0 8px;scroll-snap-type:x mandatory}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{scroll-snap-align:start;min-width:300px;max-width:320px;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{min-width:100%;max-width:100%;width:100%;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month-header{font-size:15px;font-weight:600;padding:12px 8px;margin-bottom:12px;text-align:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){padding:16px;max-width:100%;width:100%;overflow-x:visible;overflow-y:visible;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal){max-width:100vw;overflow-x:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-grid,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-grid{gap:10px;padding:12px 8px;grid-template-columns:repeat(3,1fr)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-cell,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-cell{min-height:44px;padding:12px 16px;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-view{padding:12px 8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-header{padding:12px 8px;margin-bottom:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-controls{gap:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-out,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-in{min-width:44px;min-height:44px;padding:10px;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-range{padding:12px 16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-container{padding:12px 8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-month{min-height:70px;padding:12px;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-view{padding:12px 8px;gap:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-header{padding:12px 8px;gap:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-label{font-weight:500}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-value{font-size:16px;font-weight:600}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-container{padding:12px 8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider{width:100%;height:44px}}@media(min-width:375px)and (max-width:479px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input{padding:10px 12px;min-height:44px;line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input::placeholder{line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input:not(:placeholder-shown){line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button{padding:8px;margin-right:8px;min-width:44px;min-height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button svg{width:18px;height:18px;min-width:18px;min-height:18px;max-width:18px;max-height:18px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:300px;max-width:calc(100vw - 24px);max-height:calc(100dvh - 48px);position:fixed;inset:50% auto auto 50%;-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%);transform:translate(-50%,-50%);border-radius:16px;padding:0;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container{padding:0;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container{padding:12px;max-width:100%;width:100%;border-radius:16px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container{padding:10px;width:100%;max-width:100%;margin-bottom:0;border-radius:0;border-bottom:1px solid var(--datepicker-border-color);background:var(--datepicker-background);order:-1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container ul{flex-wrap:wrap;justify-content:flex-start;gap:8px;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;padding-bottom:4px;scroll-snap-type:x mandatory}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container li{padding:10px 16px;font-size:13px;line-height:1.4;flex-shrink:0;white-space:nowrap;min-height:44px;scroll-snap-align:start;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header{padding:10px 6px;margin-bottom:10px;gap:10px;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;border-bottom:1px solid var(--datepicker-border-color);-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects{gap:8px;font-size:13px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select{height:44px;min-height:44px;font-size:13px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons{gap:6px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{width:44px;height:44px;min-width:44px;min-height:44px;padding:10px;border-radius:10px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button svg{width:20px;height:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:0;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid{gap:4px;width:100%;max-width:100%;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-name{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-size:11px;font-weight:600;padding:8px 0;text-align:center;color:var(--datepicker-subtle-text-color)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-cell{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;aspect-ratio:1;min-height:44px;max-height:none;margin:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-number{width:100%;height:100%;min-width:0;max-width:100%;min-height:44px;max-height:none;font-weight:500;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection{gap:8px;margin-top:10px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-label{font-size:13px;font-weight:500;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select{--custom-select-width: 70px !important;min-width:70px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-separator{font-size:18px;font-weight:600;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;padding:0 4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer{gap:10px;padding:14px 6px 10px;margin-top:10px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:stretch;-webkit-justify-content:stretch;-ms-flex-pack:stretch;justify-content:stretch}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button{padding:12px 18px;font-weight:500;min-height:44px;-webkit-box-flex:1;-webkit-flex:1 1 calc(50% - 5px);-ms-flex:1 1 calc(50% - 5px);flex:1 1 calc(50% - 5px);min-width:0;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container{width:100%;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:18px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-vertical{flex-direction:column;gap:18px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal{flex-direction:row;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;gap:14px;padding:0 6px;scroll-snap-type:x mandatory}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{scroll-snap-align:start;min-width:280px;max-width:300px;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{min-width:100%;max-width:100%;width:100%;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month-header{font-weight:600;padding:10px 6px;margin-bottom:10px;text-align:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){padding:12px;max-width:100%;width:100%;overflow-x:visible;overflow-y:visible;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal){max-width:100vw;overflow-x:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-grid,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-grid{gap:8px;padding:10px 6px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-cell,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-cell{min-height:44px;padding:12px 16px;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-view{padding:10px 6px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-header{padding:10px 6px;margin-bottom:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-controls{gap:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-out,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-in{min-width:44px;min-height:44px;padding:10px;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-range{font-size:13px;padding:10px 14px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-container{padding:10px 6px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-month{min-height:64px;padding:10px;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-view{padding:10px 6px;gap:14px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-header{padding:10px 6px;gap:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-label{font-size:13px;font-weight:500}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-value{font-size:15px;font-weight:600}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-container{padding:10px 6px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider{width:100%;height:44px}}@media(max-width:374px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-input-group{min-width:100%;width:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input{font-size:13px;padding:8px 10px;min-height:44px;line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input::placeholder{font-size:13px;line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input:not(:placeholder-shown){font-size:13px;line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button{padding:8px;margin-right:6px;min-width:44px;min-height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button svg{width:16px;height:16px;min-width:16px;min-height:16px;max-width:16px;max-height:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){width:calc(100% - 20px);min-width:280px;max-width:100%;max-height:calc(100dvh - 20px);position:fixed;inset:0;margin:auto;-webkit-transform:none;-moz-transform:none;-ms-transform:none;-o-transform:none;transform:none;border-radius:12px;padding:0;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container{padding:0;width:100%;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;overflow:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container{padding:6px 4px;max-width:100%;width:100%;border-radius:0;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:2px;overflow:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container{padding:8px;width:100%;max-width:100%;margin-bottom:0;border-radius:0;border-bottom:1px solid var(--datepicker-border-color);background:var(--datepicker-background);order:-1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container ul{flex-wrap:nowrap;justify-content:flex-start;gap:6px;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;padding-bottom:4px;scroll-snap-type:x mandatory;-webkit-box-pack:start;-webkit-justify-content:flex-start;-ms-flex-pack:start}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container li{padding:10px 14px;font-size:12px;line-height:1.4;flex-shrink:0;white-space:nowrap;min-height:44px;scroll-snap-align:start;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header{padding:4px 6px 10px;margin-bottom:6px;gap:4px;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;border-bottom:1px solid var(--datepicker-border-color);-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;align-items:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects{gap:6px;font-size:12px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select{height:40px;min-height:40px;font-size:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons{gap:4px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{width:40px;height:40px;min-width:40px;min-height:40px;padding:8px;border-radius:8px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button svg{width:18px;height:18px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:0;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid{gap:3px;width:100%;max-width:100%;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-name{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-size:10px;font-weight:600;padding:6px 0;text-align:center;color:var(--datepicker-subtle-text-color)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-cell{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;aspect-ratio:1;min-height:40px;max-height:none;margin:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-number{width:100%;height:100%;min-width:0;max-width:100%;min-height:40px;max-height:none;font-size:13px;font-weight:500;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection{display:flex;gap:8px;padding:12px 4px;margin-top:8px;border-top:1px solid var(--datepicker-border-color);flex-wrap:nowrap;justify-content:center;align-items:center;overflow:visible;position:relative;z-index:100}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-label{font-size:13px;font-weight:600;flex-shrink:0;margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select{--custom-select-width: 70px;min-width:70px;flex-shrink:0;position:relative;z-index:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection:has(ngxsmk-custom-select[data-open=true]){z-index:1000}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select[data-open=true]{z-index:10000}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select[data-open=true] .ngxsmk-options-panel{z-index:10001;top:auto;bottom:calc(100% + 4px);transform-origin:bottom center;animation:fadeInUp .12s cubic-bezier(.4,0,.2,1)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-separator{font-size:18px;font-weight:700;flex-shrink:0;padding:0 4px;line-height:1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer{gap:8px;padding:12px 4px 8px;margin-top:0;border-top:1px solid var(--datepicker-border-color);flex-wrap:nowrap;justify-content:stretch;position:relative;z-index:1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button{padding:12px 16px;font-size:13px;font-weight:500;min-height:44px;-webkit-box-flex:1;-webkit-flex:1 1 calc(50% - 4px);-ms-flex:1 1 calc(50% - 4px);flex:1 1 calc(50% - 4px);min-width:0;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container{width:100%;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-vertical{flex-direction:column;gap:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal{flex-direction:row;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;gap:12px;padding:0 4px;scroll-snap-type:x mandatory}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{scroll-snap-align:start;min-width:260px;max-width:280px;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{min-width:100%;max-width:100%;width:100%;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month-header{font-size:13px;font-weight:600;padding:8px 4px;margin-bottom:8px;text-align:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){padding:8px;max-width:100%;width:100%;overflow-x:visible;overflow-y:visible;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal){max-width:100vw;overflow-x:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-grid,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-grid{gap:6px;padding:8px 4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-cell,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-cell{min-height:44px;padding:10px 12px;font-size:13px;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-view{padding:8px 4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-header{padding:8px 4px;margin-bottom:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-controls{gap:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-out,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-in{min-width:44px;min-height:44px;padding:8px;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-range{font-size:12px;padding:8px 12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-container{padding:8px 4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-month{min-height:60px;padding:8px;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-view{padding:8px 4px;gap:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-header{padding:8px 4px;gap:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-label{font-size:12px;font-weight:500}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-value{font-weight:600}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-container{padding:8px 4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider{width:100%;height:44px}}@media(max-width:991px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection{position:relative;z-index:100;overflow:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select[data-open=true] .ngxsmk-options-panel{z-index:10001;top:auto;bottom:calc(100% + 4px);transform-origin:bottom center;animation:fadeInUp .12s cubic-bezier(.4,0,.2,1)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection:has(ngxsmk-custom-select[data-open=true]){z-index:1000}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer{position:relative;z-index:1}}.ngxsmk-datepicker-inner-content{display:flex!important;flex-direction:row!important;width:100%;flex:1 1 auto}@media(max-width:1023px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-inner-content{flex-direction:column}}.md3-theme .ngxsmk-day-number{border-radius:12px;transition:all .2s cubic-bezier(0,0,.2,1)}.md3-theme .ngxsmk-day-cell.selected .ngxsmk-day-number,.md3-theme .ngxsmk-day-cell.start-date .ngxsmk-day-number,.md3-theme .ngxsmk-day-cell.end-date .ngxsmk-day-number{border-radius:12px;box-shadow:0 4px 8px #0000001a;transform:translateY(-1px)}@keyframes fadeInUp{0%{opacity:0;transform:translate3d(0,4px,0)}to{opacity:1;transform:translateZ(0)}}@media(min-width:992px){.ngxsmk-datepicker-container{display:grid!important;grid-template-areas:\"header header\" \"ranges calendar\" \"time time\" \"footer footer\";grid-template-columns:auto auto;width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important}.ngxsmk-range-duration-header{grid-area:header}.ngxsmk-ranges-container{grid-area:ranges}.ngxsmk-calendar-container{grid-area:calendar}.ngxsmk-footer{grid-area:footer}ngxsmk-time-selection{grid-area:time}}.ngxsmk-multi-calendar-container.ngxsmk-sync-scroll-enabled{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:var(--datepicker-calendar-gap, 16px);align-items:start}.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal.ngxsmk-sync-scroll-enabled{display:flex;flex-direction:row;flex-wrap:nowrap;gap:var(--datepicker-calendar-gap, 16px);align-items:flex-start;overflow-x:auto;overflow-y:hidden;scroll-behavior:smooth;position:relative}.ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical.ngxsmk-sync-scroll-enabled{display:flex;flex-direction:column;gap:var(--datepicker-calendar-gap, 16px);align-items:stretch;overflow-y:auto;overflow-x:hidden;scroll-behavior:smooth}.ngxsmk-calendar-horizontal.ngxsmk-sync-scroll-enabled .ngxsmk-calendar-month{min-width:320px;width:100%;flex-basis:100%}.ngxsmk-multi-calendar.ngxsmk-sync-scroll-enabled{overflow:hidden}.ngxsmk-multi-calendar-container.ngxsmk-sync-scroll-enabled:focus-within{outline:2px solid var(--datepicker-focus-color, #4a90e2);outline-offset:2px;border-radius:4px}@media(max-width:768px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal.ngxsmk-sync-scroll-enabled{flex-direction:column;overflow-x:hidden;overflow-y:auto}.ngxsmk-calendar-horizontal.ngxsmk-sync-scroll-enabled .ngxsmk-calendar-month{min-width:100%;width:100%}}:root{--ngxsmk-space-xs: .25rem;--ngxsmk-space-sm: .5rem;--ngxsmk-space-md: .75rem;--ngxsmk-space-lg: 1rem;--ngxsmk-space-xl: 1.5rem;--ngxsmk-space-2xl: 2rem;--ngxsmk-touch-target: 44px;--ngxsmk-cell-size-desktop: 40px;--ngxsmk-font-size-xs: .625rem;--ngxsmk-font-size-sm: .75rem;--ngxsmk-font-size-base: 1rem;--ngxsmk-font-size-lg: 1.125rem;--ngxsmk-font-size-xl: 1.25rem;--ngxsmk-line-height: 1.5;--ngxsmk-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;--ngxsmk-color-primary: var(--ion-color-primary, #6d28d9);--ngxsmk-color-on-primary: var(--ion-color-primary-contrast, #ffffff);--ngxsmk-color-range-bg: var(--ion-color-primary-tint, #f5f3ff);--ngxsmk-color-surface: var(--ion-background-color, #ffffff);--ngxsmk-color-surface-hover: #f3f4f6;--ngxsmk-color-text-main: var(--ion-text-color, #1f2937);--ngxsmk-color-text-muted: var(--ion-text-color-step-400, #6b7280);--ngxsmk-color-border: var(--ion-item-border-color, var(--ion-border-color, #e5e7eb));--ngxsmk-radius-sm: 6px;--ngxsmk-radius-md: 8px;--ngxsmk-radius-lg: 12px;--ngxsmk-radius-xl: 16px;--ngxsmk-radius-popup: 16px;--ngxsmk-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);--ngxsmk-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .06);--ngxsmk-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--ngxsmk-transition-duration: .15s;--ngxsmk-transition-easing: cubic-bezier(.4, 0, .2, 1);--ngxsmk-transition: all var(--ngxsmk-transition-duration) var(--ngxsmk-transition-easing);--ngxsmk-z-backdrop: 2147483646;--ngxsmk-z-popover: 2147483647;--datepicker-primary-color: var(--ngxsmk-color-primary);--datepicker-primary-contrast: var(--ngxsmk-color-on-primary);--datepicker-range-background: var(--ngxsmk-color-range-bg);--datepicker-background: var(--ngxsmk-color-surface);--datepicker-hover-background: var(--ngxsmk-color-surface-hover);--datepicker-text-color: var(--ngxsmk-color-text-main);--datepicker-subtle-text-color: var(--ngxsmk-color-text-muted);--datepicker-border-color: var(--ngxsmk-color-border);--datepicker-radius-sm: var(--ngxsmk-radius-sm);--datepicker-radius-md: var(--ngxsmk-radius-md);--datepicker-radius-lg: var(--ngxsmk-radius-lg);--datepicker-border-radius: var(--ngxsmk-radius-lg);--datepicker-font-size-base: var(--ngxsmk-font-size-base);--datepicker-font-size-lg: var(--ngxsmk-font-size-lg);--datepicker-line-height: var(--ngxsmk-line-height);--datepicker-spacing-xs: var(--ngxsmk-space-xs);--datepicker-spacing-sm: var(--ngxsmk-space-sm);--datepicker-spacing-md: var(--ngxsmk-space-md);--datepicker-spacing-lg: var(--ngxsmk-space-lg);--datepicker-transition: var(--ngxsmk-transition)}ngxsmk-datepicker{display:block;width:100%;position:relative;font-family:var(--ngxsmk-font-family);user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;overflow:visible}ngxsmk-datepicker *,ngxsmk-datepicker *:before,ngxsmk-datepicker *:after{box-sizing:border-box;-webkit-tap-highlight-color:transparent}ngxsmk-datepicker:has(.ngxsmk-calendar-open){z-index:var(--ngxsmk-z-popover)!important}.ngxsmk-datepicker-wrapper{position:relative;width:100%;z-index:1;overflow:visible}.ngxsmk-datepicker-wrapper.ngxsmk-calendar-open{z-index:var(--ngxsmk-z-popover);position:relative;isolation:isolate}.ngxsmk-datepicker-container,.ngxsmk-calendar-container{border-radius:var(--ngxsmk-radius-lg)!important;overflow:hidden!important}.ngxsmk-datepicker-container .ngxsmk-calendar-container{border-radius:inherit!important}.ngxsmk-backdrop{display:none}@media(min-width:1025px){.ngxsmk-backdrop:not(.ngxsmk-inline-backdrop){display:block!important;position:fixed!important;inset:0;background:transparent!important;z-index:var(--ngxsmk-z-backdrop)!important;pointer-events:auto!important}.ngxsmk-backdrop:not(.ngxsmk-inline-backdrop).ngxsmk-backdrop-allow-modal-scroll{pointer-events:none!important}}@media(max-width:1024px){.ngxsmk-datepicker-wrapper.ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-backdrop,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-backdrop:not(.ngxsmk-inline-backdrop){display:block;visibility:visible;opacity:1;position:fixed;inset:0;background:#0006;z-index:var(--ngxsmk-z-backdrop);pointer-events:auto;transition:opacity .3s ease-out}}@media(prefers-reduced-motion:reduce){ngxsmk-datepicker,.ngxsmk-datepicker,.ngxsmk-datepicker-panel,.ngxsmk-datepicker-overlay,.ngxsmk-datepicker-dialog,.ngxsmk-datepicker-dropdown,.ngxsmk-datepicker-container,.ngxsmk-presets-drawer{--ngxsmk-transition: none !important;--ngxsmk-transition-duration: .01ms !important}ngxsmk-datepicker *,ngxsmk-datepicker *:before,ngxsmk-datepicker *:after,.ngxsmk-datepicker-panel *,.ngxsmk-datepicker-panel *:before,.ngxsmk-datepicker-panel *:after,.ngxsmk-datepicker-overlay *,.ngxsmk-datepicker-dialog *,.ngxsmk-datepicker-dropdown *,.ngxsmk-presets-drawer *{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}ngxsmk-datepicker.dark-theme,.ngxsmk-popover-container.dark-theme,.ngxsmk-backdrop.dark-theme{--ngxsmk-color-range-bg: rgba(139, 92, 246, .15);--ngxsmk-color-surface: #1f2937;--ngxsmk-color-text-main: #f3f4f6;--ngxsmk-color-text-muted: #9ca3af;--ngxsmk-color-border: #374151;--ngxsmk-color-surface-hover: #374151;--ngxsmk-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .3);--ngxsmk-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .3), 0 2px 4px -1px rgba(0, 0, 0, .2);--ngxsmk-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .3), 0 4px 6px -2px rgba(0, 0, 0, .2)}ngxsmk-datepicker.glass-theme,.ngxsmk-popover-container.glass-theme{--ngxsmk-color-surface: rgba(255, 255, 255, .7);--ngxsmk-color-border: rgba(255, 255, 255, .3);--ngxsmk-shadow-lg: 0 8px 32px 0 rgba(31, 38, 135, .37);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--ngxsmk-color-border)}ngxsmk-datepicker.glass-theme.dark-theme,.ngxsmk-popover-container.glass-theme.dark-theme{--ngxsmk-color-surface: rgba(31, 41, 55, .7);--ngxsmk-color-border: rgba(255, 255, 255, .1)}ngxsmk-datepicker.md3-theme{--ngxsmk-color-primary: #6750a4;--ngxsmk-radius-lg: 28px;--ngxsmk-font-family: \"Roboto\", sans-serif;--ngxsmk-shadow-md: 0px 1px 3px 1px rgba(0, 0, 0, .15), 0px 1px 2px rgba(0, 0, 0, .3)}.ngxsmk-input-group{display:flex;align-items:center;position:relative;width:100%;border:1px solid var(--ngxsmk-color-border);border-radius:var(--ngxsmk-radius-md);background:var(--ngxsmk-color-surface);transition:var(--ngxsmk-transition);min-height:44px}.ngxsmk-input-group:focus-within{border-color:var(--ngxsmk-color-primary);box-shadow:var(--datepicker-shadow-focus)}.ngxsmk-input-group.disabled{opacity:.6;background:var(--ngxsmk-color-surface-hover);cursor:not-allowed}.ngxsmk-display-input{flex-grow:1;width:100%;padding:var(--ngxsmk-space-md) var(--ngxsmk-space-lg);font-size:var(--ngxsmk-font-size-base);line-height:var(--ngxsmk-line-height);color:var(--ngxsmk-color-text-main);background:transparent;border:none;outline:none;cursor:pointer;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.ngxsmk-display-input:disabled{cursor:not-allowed}.ngxsmk-calendar-button,.ngxsmk-clear-button{display:flex;align-items:center;justify-content:center;width:var(--ngxsmk-touch-target);height:var(--ngxsmk-touch-target);background:transparent;border:none;cursor:pointer;color:var(--ngxsmk-color-text-muted);transition:var(--ngxsmk-transition);border-radius:var(--ngxsmk-radius-md)}.ngxsmk-calendar-button svg,.ngxsmk-clear-button svg{width:20px;height:20px;fill:currentColor}@media(hover:hover){.ngxsmk-calendar-button:hover:not(:disabled),.ngxsmk-clear-button:hover:not(:disabled){color:var(--ngxsmk-color-primary);background:var(--ngxsmk-color-surface-hover)}}.ngxsmk-calendar-button:focus-visible,.ngxsmk-clear-button:focus-visible{outline:2px solid var(--ngxsmk-color-primary);outline-offset:-2px}.ngxsmk-calendar-button:disabled,.ngxsmk-clear-button:disabled{cursor:not-allowed;opacity:.5}.ngxsmk-calendar-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:var(--ngxsmk-space-xs);padding:0 var(--ngxsmk-space-md) var(--ngxsmk-space-md);width:100%}.ngxsmk-weekdays{display:grid;grid-template-columns:repeat(7,1fr);gap:var(--ngxsmk-space-xs);padding:var(--ngxsmk-space-md);border-bottom:1px solid var(--ngxsmk-color-border);margin-bottom:var(--ngxsmk-space-sm)}.ngxsmk-weekdays .ngxsmk-weekday{text-align:center;font-size:var(--ngxsmk-font-size-xs);font-weight:600;color:var(--ngxsmk-color-text-muted);text-transform:uppercase;letter-spacing:.05em}.ngxsmk-day-cell{min-width:var(--ngxsmk-touch-target);min-height:var(--ngxsmk-touch-target);width:100%;aspect-ratio:1/1;display:flex;align-items:center;justify-content:center}@media(min-width:1025px){.ngxsmk-day-cell{width:var(--ngxsmk-cell-size-desktop);height:var(--ngxsmk-cell-size-desktop);min-width:0;min-height:0}}.ngxsmk-month-grid,.ngxsmk-year-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--ngxsmk-space-sm);padding:var(--ngxsmk-space-md)}.ngxsmk-month-grid .ngxsmk-grid-cell,.ngxsmk-year-grid .ngxsmk-grid-cell{min-height:var(--ngxsmk-touch-target);padding:var(--ngxsmk-space-md) var(--ngxsmk-space-sm);display:flex;align-items:center;justify-content:center;border-radius:var(--ngxsmk-radius-sm);font-size:var(--ngxsmk-font-size-base);color:var(--ngxsmk-color-text-main);background:transparent;border:1px solid transparent;cursor:pointer;transition:var(--ngxsmk-transition)}@media(hover:hover){.ngxsmk-month-grid .ngxsmk-grid-cell:hover:not(.disabled),.ngxsmk-year-grid .ngxsmk-grid-cell:hover:not(.disabled){background:var(--ngxsmk-color-surface-hover);border-color:var(--ngxsmk-color-border)}}.ngxsmk-month-grid .ngxsmk-grid-cell.selected,.ngxsmk-year-grid .ngxsmk-grid-cell.selected{background:var(--datepicker-primary-color, var(--ngxsmk-color-primary));color:var(--datepicker-primary-contrast, var(--ngxsmk-color-on-primary));font-weight:600;border-color:var(--datepicker-primary-color, var(--ngxsmk-color-primary))}.ngxsmk-month-grid .ngxsmk-grid-cell.disabled,.ngxsmk-year-grid .ngxsmk-grid-cell.disabled{color:var(--ngxsmk-color-text-muted);opacity:.5;cursor:not-allowed}@media(min-width:768px){.ngxsmk-month-grid .ngxsmk-grid-cell,.ngxsmk-year-grid .ngxsmk-grid-cell{padding:var(--ngxsmk-space-lg) var(--ngxsmk-space-sm)}}.ngxsmk-year-grid{grid-template-columns:repeat(4,1fr)}.ngxsmk-header{display:flex;align-items:center;justify-content:space-between;padding:var(--ngxsmk-space-sm) var(--ngxsmk-space-md) var(--ngxsmk-space-xs);width:100%;gap:var(--ngxsmk-space-sm)}@media(min-width:1025px){.ngxsmk-header{padding:var(--ngxsmk-space-sm) var(--ngxsmk-space-md)}}.ngxsmk-month-year-selects{display:flex;align-items:center;gap:var(--ngxsmk-space-xs);flex:1;min-width:0}.ngxsmk-month-year-selects .month-select{flex:1.5}.ngxsmk-month-year-selects .year-select{flex:1}.ngxsmk-nav-buttons{display:flex;align-items:center;gap:var(--ngxsmk-space-xs)}ngxsmk-custom-select{position:relative;display:flex;flex:1;min-width:0;-webkit-user-select:none;user-select:none;box-sizing:border-box;z-index:1}ngxsmk-custom-select[data-open=true]{z-index:10000}.ngxsmk-select-container{cursor:pointer;position:relative;display:flex;width:100%;height:100%;min-height:100%;box-sizing:border-box;outline:none}.ngxsmk-select-container.is-open{z-index:10000}.ngxsmk-select-display{display:flex;align-items:center;justify-content:space-between;flex:1;width:100%;height:100%;min-height:var(--ngxsmk-touch-target);padding:0 var(--ngxsmk-space-md);background:var(--ngxsmk-color-surface);border:1px solid var(--ngxsmk-color-border);border-radius:var(--ngxsmk-radius-sm);font-size:var(--ngxsmk-font-size-base);font-family:inherit;color:var(--ngxsmk-color-text-main);cursor:pointer;transition:var(--ngxsmk-transition);appearance:none;-webkit-appearance:none;gap:var(--ngxsmk-space-sm)}.ngxsmk-select-display:focus-within{outline:none;border-color:var(--datepicker-primary-color, var(--ngxsmk-color-primary));box-shadow:var(--datepicker-shadow-focus)}.ngxsmk-select-display:disabled{background-color:var(--ngxsmk-color-surface-hover);cursor:not-allowed;opacity:.6}@media(hover:hover){.ngxsmk-select-display:hover:not(:disabled){background:var(--ngxsmk-color-surface-hover);border-color:var(--ngxsmk-color-text-muted)}}@media(min-width:1025px){.ngxsmk-select-display{min-height:36px;font-size:var(--ngxsmk-font-size-sm)}}.ngxsmk-arrow-icon{width:14px;height:14px;transition:transform var(--ngxsmk-transition-duration) ease;flex-shrink:0;color:var(--ngxsmk-color-text-muted)}.is-open .ngxsmk-arrow-icon{transform:rotate(180deg)}.ngxsmk-options-panel{position:absolute;top:calc(100% + 4px);left:0;width:100%;background:var(--ngxsmk-color-surface);border:1px solid var(--ngxsmk-color-border);border-radius:var(--ngxsmk-radius-md);box-shadow:var(--ngxsmk-shadow-lg);max-height:200px;overflow-y:auto;z-index:10001;scrollbar-width:none}.ngxsmk-options-panel::-webkit-scrollbar{display:none}.ngxsmk-options-panel ul{list-style:none;padding:var(--ngxsmk-space-xs);margin:0}.ngxsmk-options-panel li{padding:var(--ngxsmk-space-sm) var(--ngxsmk-space-md);border-radius:var(--ngxsmk-radius-sm);cursor:pointer;transition:background-color .12s ease;font-size:var(--ngxsmk-font-size-sm);margin:2px 0;color:var(--ngxsmk-color-text-main)!important;opacity:1!important;visibility:visible!important}.ngxsmk-options-panel li:hover:not(.selected){background-color:var(--ngxsmk-color-surface-hover)}.ngxsmk-options-panel li.selected{background-color:var(--datepicker-primary-color, var(--ngxsmk-color-primary));color:var(--datepicker-primary-contrast, var(--ngxsmk-color-on-primary))!important;font-weight:600}@media(max-width:768px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-options-panel li{padding:var(--ngxsmk-space-md) var(--ngxsmk-space-lg);min-height:var(--ngxsmk-touch-target);display:flex;align-items:center}}.ngxsmk-header-title{font-size:var(--ngxsmk-font-size-lg);font-weight:600;color:var(--ngxsmk-color-text-main);text-align:center;flex-grow:1}.ngxsmk-nav-button{cursor:pointer;display:flex;align-items:center;justify-content:center;width:var(--ngxsmk-touch-target);height:var(--ngxsmk-touch-target);background:transparent;border:none;border-radius:var(--ngxsmk-radius-sm);color:var(--ngxsmk-color-text-main);transition:var(--ngxsmk-transition)}.ngxsmk-nav-button svg{width:20px;height:20px}.ngxsmk-nav-button:hover:not(:disabled){background:var(--ngxsmk-color-surface-hover)}.ngxsmk-nav-button:disabled{opacity:.3;cursor:not-allowed}@media(min-width:1025px){.ngxsmk-nav-button{width:32px;height:32px}}:root{--ngxsmk-space-xs: .25rem;--ngxsmk-space-sm: .5rem;--ngxsmk-space-md: .75rem;--ngxsmk-space-lg: 1rem;--ngxsmk-space-xl: 1.5rem;--ngxsmk-space-2xl: 2rem;--ngxsmk-touch-target: 44px;--ngxsmk-cell-size-desktop: 40px;--ngxsmk-font-size-xs: .625rem;--ngxsmk-font-size-sm: .75rem;--ngxsmk-font-size-base: 1rem;--ngxsmk-font-size-lg: 1.125rem;--ngxsmk-font-size-xl: 1.25rem;--ngxsmk-line-height: 1.5;--ngxsmk-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;--ngxsmk-color-primary: var(--datepicker-primary-color);--ngxsmk-color-on-primary: var(--ion-color-primary-contrast, #ffffff);--ngxsmk-color-range-bg: var(--ion-color-primary-tint, #f5f3ff);--ngxsmk-color-surface: var(--ion-background-color, #ffffff);--ngxsmk-color-surface-hover: #f3f4f6;--ngxsmk-color-text-main: var(--ion-text-color, #1f2937);--ngxsmk-color-text-muted: var(--ion-text-color-step-400, #6b7280);--ngxsmk-color-border: var(--ion-item-border-color, var(--ion-border-color, #e5e7eb));--ngxsmk-radius-sm: 6px;--ngxsmk-radius-md: 8px;--ngxsmk-radius-lg: 12px;--ngxsmk-radius-xl: 16px;--ngxsmk-radius-popup: 16px;--ngxsmk-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);--ngxsmk-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .06);--ngxsmk-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--ngxsmk-transition-duration: .15s;--ngxsmk-transition-easing: cubic-bezier(.4, 0, .2, 1);--ngxsmk-transition: all var(--ngxsmk-transition-duration) var(--ngxsmk-transition-easing);--ngxsmk-z-backdrop: 2147483646;--ngxsmk-z-popover: 2147483647;--datepicker-primary-color: var(--ngxsmk-color-primary);--datepicker-primary-contrast: var(--ngxsmk-color-on-primary);--datepicker-range-background: var(--ngxsmk-color-range-bg);--datepicker-background: var(--ngxsmk-color-surface);--datepicker-hover-background: var(--ngxsmk-color-surface-hover);--datepicker-text-color: var(--ngxsmk-color-text-main);--datepicker-subtle-text-color: var(--ngxsmk-color-text-muted);--datepicker-border-color: var(--ngxsmk-color-border);--datepicker-radius-sm: var(--ngxsmk-radius-sm);--datepicker-radius-md: var(--ngxsmk-radius-md);--datepicker-radius-lg: var(--ngxsmk-radius-lg);--datepicker-border-radius: var(--ngxsmk-radius-lg);--datepicker-font-size-base: var(--ngxsmk-font-size-base);--datepicker-font-size-lg: var(--ngxsmk-font-size-lg);--datepicker-line-height: var(--ngxsmk-line-height);--datepicker-spacing-xs: var(--ngxsmk-space-xs);--datepicker-spacing-sm: var(--ngxsmk-space-sm);--datepicker-spacing-md: var(--ngxsmk-space-md);--datepicker-spacing-lg: var(--ngxsmk-space-lg);--datepicker-transition: var(--ngxsmk-transition)}.ngxsmk-popover-container{position:fixed;bottom:0;left:0;width:100%;max-height:90dvb;background:var(--ngxsmk-color-surface);border-radius:var(--ngxsmk-radius-popup) var(--ngxsmk-radius-popup) 0 0;z-index:var(--ngxsmk-z-popover);padding-top:max(0,env(safe-area-inset-top));padding-right:max(0,env(safe-area-inset-right));padding-bottom:max(1rem,env(safe-area-inset-bottom));padding-left:max(0,env(safe-area-inset-left));box-shadow:var(--ngxsmk-shadow-xl);border:unset!important;overflow-y:auto;overscroll-behavior:contain;display:flex;flex-direction:column}.ngxsmk-popover-container.ngxsmk-popover-open{visibility:visible;opacity:1}.ngxsmk-popover-container.ngxsmk-inline-container{position:static!important;width:100%;margin:0;transform:none!important;box-shadow:none!important;border:none;background:transparent;padding:0;max-height:none}@media(min-width:768px){.ngxsmk-popover-container{top:50%;left:50%;bottom:auto;transform:translate(-50%,-50%);width:clamp(320px,80vw,500px);border-radius:var(--ngxsmk-radius-popup);border:unset!important;max-height:calc(100vh - 64px);padding:0}}@media(min-width:1025px){.ngxsmk-popover-container{position:absolute;transform:none;width:max-content;min-width:360px;max-height:none;border-radius:var(--ngxsmk-radius-md);box-shadow:var(--ngxsmk-shadow-lg);border:unset!important}}.ngxsmk-calendar-container{display:flex;flex-direction:column;width:100%}@media(min-width:1025px){.ngxsmk-calendar-container.ngxsmk-has-multi-calendar{flex-direction:column;align-items:stretch;min-width:600px}.ngxsmk-calendar-container.ngxsmk-has-multi-calendar>ngxsmk-calendar-header,.ngxsmk-calendar-container.ngxsmk-has-multi-calendar>.ngxsmk-multi-calendar-container{width:100%;max-width:100%;min-width:0}}body.ngxsmk-scroll-locked{overflow:hidden!important;touch-action:none}.ngxsmk-day-cell.ngxsmk-other-month{opacity:.45;color:var(--datepicker-subtle-text-color)}.ngxsmk-ai-container{display:flex;flex-direction:column;gap:4px;margin:var(--datepicker-spacing-xs, 6px) var(--datepicker-spacing-sm, 10px) var(--datepicker-spacing-xs, 6px)}.ngxsmk-ai-bar{display:flex;align-items:center;gap:var(--datepicker-spacing-xs, 6px);padding:4px 8px;background:var(--datepicker-bg-color, #ffffff);border:1px solid var(--datepicker-border-color, #e2e8f0);border-radius:var(--datepicker-border-radius, 8px);box-shadow:0 1px 3px #0000000a;transition:border-color .2s ease,box-shadow .2s ease}.ngxsmk-popover-container.dark-theme .ngxsmk-ai-bar,.dark-theme .ngxsmk-ai-bar{background:var(--datepicker-bg-color, #1e293b);border-color:var(--datepicker-border-color, #334155);box-shadow:0 1px 3px #00000040}.ngxsmk-ai-bar:focus-within{border-color:var(--datepicker-primary-color, #3b82f6);box-shadow:0 0 0 2px #3b82f633}.ngxsmk-ai-icon{display:flex;align-items:center;justify-content:center;color:var(--datepicker-primary-color, #3b82f6);flex-shrink:0}.ngxsmk-ai-input{flex:1;min-width:0;border:none;background:transparent;outline:none;font-size:var(--datepicker-font-size-sm, 13px);font-family:inherit;color:var(--datepicker-text-color, #1e293b);padding:4px 2px}.ngxsmk-popover-container.dark-theme .ngxsmk-ai-input,.dark-theme .ngxsmk-ai-input{color:var(--datepicker-text-color, #f8fafc)}.ngxsmk-ai-input::placeholder{color:var(--datepicker-subtle-text-color, #94a3b8)}.ngxsmk-ai-submit{display:flex;align-items:center;justify-content:center;width:26px;height:26px;padding:0;background:var(--datepicker-primary-color, #3b82f6);color:#fff;border:none;border-radius:calc(var(--datepicker-border-radius, 8px) - 2px);cursor:pointer;transition:opacity .15s ease,transform .1s ease;flex-shrink:0}.ngxsmk-ai-submit:hover:not(:disabled){opacity:.9;transform:scale(1.03)}.ngxsmk-ai-submit:active:not(:disabled){transform:scale(.97)}.ngxsmk-ai-submit:disabled{opacity:.4;cursor:not-allowed}.ngxsmk-ai-spinner{width:12px;height:12px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:ngxsmk-ai-spin .6s linear infinite}@keyframes ngxsmk-ai-spin{to{transform:rotate(360deg)}}.ngxsmk-ai-chips{display:flex;flex-wrap:wrap;gap:4px;padding:0 2px}.ngxsmk-ai-chip{display:inline-flex;align-items:center;padding:2px 8px;font-size:11px;font-family:inherit;background:#3b82f614;color:var(--datepicker-primary-color, #3b82f6);border:1px solid rgba(59,130,246,.2);border-radius:12px;cursor:pointer;white-space:nowrap;transition:background-color .15s ease,border-color .15s ease}.ngxsmk-popover-container.dark-theme .ngxsmk-ai-chip,.dark-theme .ngxsmk-ai-chip{background:#3b82f626;border-color:#3b82f64d;color:#93c5fd}.ngxsmk-ai-chip:hover:not(:disabled){background:#3b82f62e;border-color:var(--datepicker-primary-color, #3b82f6)}.ngxsmk-ai-chip:disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: NgxsmkDatepickerInputComponent, selector: "ngxsmk-datepicker-input", inputs: ["isNative", "disabled", "classes", "nativeInputType", "formattedValue", "placeholder", "id", "name", "autocomplete", "required", "minDateNative", "maxDateNative", "ariaLabel", "ariaDescribedBy", "errorState", "clearAriaLabel", "clearLabel", "isCalendarOpen", "allowTyping", "typedInputValue", "displayValue", "showCalendarButton", "calendarAriaLabel", "validationErrorMessage"], outputs: ["nativeInputChange", "inputBlur", "clearValue", "toggleCalendar", "pointerDown", "pointerUp", "inputGroupFocus", "inputKeyDown", "inputChange", "inputFocus"] }, { kind: "component", type: NgxsmkDatepickerContentComponent, selector: "ngxsmk-datepicker-content", inputs: ["isCalendarVisible", "isCalendarOpen", "isInlineMode", "shouldAppendToBody", "theme", "popoverId", "classes", "timeOnly", "showTime", "isMobile", "mobileModalStyle", "align", "ariaLabel", "isCalendarOpening", "loadingMessage", "showRanges", "rangesArray", "mode", "disabled", "calendarCount", "calendarLayout", "syncScrollEnabled", "calendarMonths", "weekDays", "weekDaysFull", "showOtherMonths", "showWeekNumbers", "weekNumberLabel", "secondaryCalendar", "secondaryCalendarLocale", "selectedDate", "startDate", "endDate", "focusedDate", "today", "dateTemplate", "calendarViewMode", "monthOptions", "currentMonth", "yearOptions", "currentYear", "isBackArrowDisabled", "prevMonthAriaLabel", "nextMonthAriaLabel", "yearGrid", "currentDecade", "decadeGrid", "timelineStartDate", "timelineEndDate", "timelineMonths", "minuteInterval", "startTimeSlider", "endTimeSlider", "timeRangeMode", "hourOptions", "minuteOptions", "secondOptions", "ampmOptions", "currentDisplayHour", "currentMinute", "currentSecond", "isPm", "showSeconds", "use24Hour", "startDisplayHour", "startMinute", "startSecond", "startIsPm", "endDisplayHour", "endMinute", "endSecond", "endIsPm", "clearAriaLabel", "clearLabel", "closeAriaLabel", "closeLabel", "translations", "selectedRange", "showTimezoneSelector", "timezoneOptions", "currentTimezone", "boundIsDateDisabled", "boundIsYearDisabled", "boundIsDecadeDisabled", "boundGetDayMetadata", "calendarHeaderTemplate", "calendarFooterTemplate", "boundIsSameDay", "boundIsHoliday", "boundIsMultipleSelected", "boundIsInRange", "boundIsInComparisonRange", "boundIsPreviewInRange", "boundGetAriaLabel", "boundGetDayCellCustomClasses", "boundGetDayCellTooltip", "boundFormatDayNumber", "getMonthYearLabel", "getCalendarAriaLabelForMonth", "isTimelineMonthSelected", "formatTimeSliderValue", "enableAi", "aiPlaceholder", "aiSuggestions", "showAiSuggestions", "isAiResolving"], outputs: ["timezoneChange", "backdropClick", "touchStartContainer", "touchMoveContainer", "touchEndContainer", "rangeSelect", "previousMonth", "nextMonth", "currentMonthChange", "currentYearChange", "dateClick", "dateHover", "dateFocus", "swipeStart", "swipeMove", "swipeEnd", "touchStart", "touchMove", "touchEnd", "viewModeChange", "changeYear", "yearClick", "changeDecade", "decadeClick", "timelineZoomOut", "timelineZoomIn", "timelineMonthClick", "startTimeSliderChange", "endTimeSliderChange", "currentDisplayHourChange", "currentMinuteChange", "currentSecondChange", "isPmChange", "timeChange", "startDisplayHourChange", "startMinuteChange", "startSecondChange", "startIsPmChange", "endDisplayHourChange", "endMinuteChange", "endSecondChange", "endIsPmChange", "timeRangeChange", "clearValue", "closeCalendar", "escapeKey", "containerKeyDown", "aiPromptSubmitted"] }, { kind: "component", type: NgxsmkDatepickerKeyboardHelpComponent, selector: "ngxsmk-datepicker-keyboard-help", inputs: ["title", "closeLabel", "backdropLabel"], outputs: ["closeRequested"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NgxsmkDatepickerComponent, decorators: [{
type: Component,
args: [{ selector: 'ngxsmk-datepicker', standalone: true, imports: [
NgClass,
NgTemplateOutlet,
NgxsmkDatepickerInputComponent,
NgxsmkDatepickerContentComponent,
NgxsmkDatepickerKeyboardHelpComponent,
], providers: [
FieldSyncService,
CalendarGenerationService,
DatepickerParsingService,
TouchGestureHandlerService,
PopoverPositioningService,
DatePipe,
], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
'[class.ngxsmk-inline]': 'isInlineMode',
}, template: `
<div
class="ngxsmk-datepicker-wrapper"
[class.ngxsmk-inline-mode]="isInlineMode"
[class.ngxsmk-calendar-open]="isCalendarOpen && !isInlineMode"
[class.ngxsmk-append-to-body]="_shouldAppendToBody"
[class.ngxsmk-rtl]="isRtl"
[class.ngxsmk-native-picker]="shouldUseNativePicker()"
[class.ngxsmk-no-responsive]="!responsive"
[ngClass]="classes()?.wrapper"
>
@if (!isInlineMode) {
<div class="ngxsmk-input-wrapper-container" style="position: relative; display: inline-block; width: 100%;">
<ngxsmk-datepicker-input
#datepickerInput
[isNative]="shouldUseNativePicker()"
[disabled]="disabled"
[classes]="classes()"
[nativeInputType]="getNativeInputType()"
[formattedValue]="formatValueForNativeInput(value)"
[placeholder]="placeholder"
[id]="inputId || _uniqueId"
[name]="name"
[autocomplete]="autocomplete"
[required]="required"
[minDateNative]="getMinDateForNativeInput()"
[maxDateNative]="getMaxDateForNativeInput()"
[ariaLabel]="placeholder || getTranslation(timeOnly ? 'selectTime' : 'selectDate')"
[ariaDescribedBy]="'datepicker-help-' + _uniqueId"
[errorState]="errorState"
[clearAriaLabel]="_clearAriaLabel"
[clearLabel]="_clearLabel"
[isCalendarOpen]="isCalendarOpen"
[allowTyping]="allowTyping"
[typedInputValue]="typedInputValue"
[displayValue]="displayValue"
[showCalendarButton]="showCalendarButton"
[calendarAriaLabel]="getTranslation(timeOnly ? 'selectTime' : 'selectDate')"
[validationErrorMessage]="validationErrorMessage"
(nativeInputChange)="onNativeInputChange($event)"
(inputBlur)="onInputBlur($event)"
(clearValue)="clearValue($event)"
(toggleCalendar)="toggleCalendar($event)"
(pointerDown)="onPointerDown($event)"
(pointerUp)="onPointerUp($event)"
(inputGroupFocus)="onInputGroupFocus()"
(inputKeyDown)="onInputKeyDown($event)"
(inputChange)="onInputChange($event)"
(inputFocus)="onInputFocus($event)"
></ngxsmk-datepicker-input>
@if (showNaturalLanguagePreview && naturalLanguagePreview) {
<div
class="ngxsmk-natural-language-preview"
style="position: absolute; top: 100%; left: 0; z-index: 1000; background: var(--datepicker-background, #fff); border: 1px solid var(--datepicker-border-color, #ccc); border-radius: var(--datepicker-radius-md, 4px); padding: 8px 12px; box-shadow: var(--datepicker-shadow-md); margin-top: 4px; font-size: 14px; width: 100%; box-sizing: border-box;"
>
<ng-container
[ngTemplateOutlet]="naturalLanguagePreviewTemplate() || defaultPreviewTpl"
[ngTemplateOutletContext]="{ $implicit: naturalLanguagePreview }"
></ng-container>
</div>
}
</div>
}
<ng-template #defaultPreviewTpl let-preview>
<div class="ngxsmk-natural-language-preview-content">
Resolved: <strong>{{ preview }}</strong>
</div>
</ng-template>
<ng-template #portalContent>
<ngxsmk-datepicker-content
#datepickerContent
[isCalendarVisible]="isCalendarVisible"
[isCalendarOpen]="_isCalendarOpen()"
[isInlineMode]="isInlineMode"
[shouldAppendToBody]="_shouldAppendToBody"
[theme]="theme"
[popoverId]="popoverId"
[classes]="classes()"
[timeOnly]="timeOnly"
[showTime]="showTime"
[isMobile]="isMobileDevice()"
[mobileModalStyle]="mobileModalStyle"
[align]="align"
[ariaLabel]="calendarAriaLabel()"
[isCalendarOpening]="isCalendarOpening"
[loadingMessage]="calendarLoadingMessage()"
[showRanges]="showRanges() || showPresets()"
[rangesArray]="rangesArray"
[selectedRange]="[startDate, endDate]"
[showTimezoneSelector]="showTimezoneSelector()"
[timezoneOptions]="getTimezoneOptions()"
[currentTimezone]="timezone || defaultTimezone"
[mode]="mode"
[disabled]="disabled"
[calendarCount]="calendarCount"
[calendarLayout]="calendarLayout"
[syncScrollEnabled]="syncScroll().enabled ?? false"
[calendarMonths]="renderedCalendars()"
[weekDays]="weekDays"
[weekDaysFull]="weekDaysFull"
[showOtherMonths]="showOtherMonths"
[showWeekNumbers]="showWeekNumbers"
[weekNumberLabel]="weekNumberLabel"
[secondaryCalendar]="secondaryCalendar"
[secondaryCalendarLocale]="locale"
[selectedDate]="selectedDate"
[startDate]="startDate"
[endDate]="endDate"
[focusedDate]="focusedDate"
[today]="today"
[dateTemplate]="dateTemplate()"
[calendarViewMode]="calendarViewMode"
[monthOptions]="monthOptions()"
[currentMonth]="_currentMonthSignal()"
[yearOptions]="yearOptions()"
[currentYear]="_currentYearSignal()"
[isBackArrowDisabled]="isBackArrowDisabled"
[prevMonthAriaLabel]="_prevMonthAriaLabel"
[nextMonthAriaLabel]="_nextMonthAriaLabel"
[yearGrid]="yearGrid"
[currentDecade]="_currentDecade"
[decadeGrid]="decadeGrid"
[timelineStartDate]="timelineStartDate"
[timelineEndDate]="timelineEndDate"
[timelineMonths]="timelineMonths"
[minuteInterval]="minuteInterval"
[startTimeSlider]="startTimeSlider"
[endTimeSlider]="endTimeSlider"
[timeRangeMode]="timeRangeMode()"
[hourOptions]="hourOptions"
[minuteOptions]="minuteOptions"
[secondOptions]="secondOptions"
[ampmOptions]="ampmOptions"
[currentDisplayHour]="currentDisplayHour"
[currentMinute]="currentMinute"
[currentSecond]="currentSecond"
[isPm]="isPm"
[showSeconds]="showSeconds"
[use24Hour]="use24Hour"
[startDisplayHour]="startDisplayHour"
[startMinute]="startMinute"
[startSecond]="startSecond"
[startIsPm]="startIsPm"
[endDisplayHour]="endDisplayHour"
[endMinute]="endMinute"
[endSecond]="endSecond"
[endIsPm]="endIsPm"
[clearAriaLabel]="_clearAriaLabel"
[clearLabel]="_clearLabel"
[closeAriaLabel]="_closeAriaLabel"
[closeLabel]="_closeLabel"
[translations]="_translations"
[boundIsDateDisabled]="boundIsDateDisabled"
[boundIsYearDisabled]="boundIsYearDisabled"
[boundIsDecadeDisabled]="boundIsDecadeDisabled"
[boundGetDayMetadata]="boundGetDayMetadata"
[calendarHeaderTemplate]="calendarHeaderTemplate"
[calendarFooterTemplate]="calendarFooterTemplate"
[enableAi]="enableAi"
[aiPlaceholder]="aiPlaceholder"
[aiSuggestions]="aiSuggestions"
[showAiSuggestions]="showAiSuggestions"
[isAiResolving]="isAiResolving"
(aiPromptSubmitted)="onAiPromptSubmitted($event)"
[boundIsSameDay]="boundIsSameDay"
[boundIsHoliday]="boundIsHoliday"
[boundIsMultipleSelected]="boundIsMultipleSelected"
[boundIsInRange]="boundIsInRange"
[boundIsInComparisonRange]="boundIsInComparisonRange"
[boundIsPreviewInRange]="boundIsPreviewInRange"
[boundGetAriaLabel]="boundGetAriaLabel"
[boundGetDayCellCustomClasses]="boundGetDayCellCustomClasses"
[boundGetDayCellTooltip]="boundGetDayCellTooltip"
[boundFormatDayNumber]="boundFormatDayNumber"
[getMonthYearLabel]="boundGetMonthYearLabel"
[getCalendarAriaLabelForMonth]="boundGetCalendarAriaLabelForMonth"
[isTimelineMonthSelected]="boundIsTimelineMonthSelected"
[formatTimeSliderValue]="boundFormatTimeSliderValue"
(backdropClick)="onBackdropInteract($event)"
(escapeKey)="onPopoverEscape($event)"
(containerKeyDown)="onKeyDown($event)"
(touchStartContainer)="onBottomSheetTouchStart($event)"
(touchMoveContainer)="onBottomSheetTouchMove($event)"
(touchEndContainer)="onBottomSheetTouchEnd($event)"
(rangeSelect)="selectRange($event)"
(timezoneChange)="setTimezone($event)"
(previousMonth)="changeMonth(-1)"
(nextMonth)="changeMonth(1)"
(currentMonthChange)="currentMonth = $event"
(currentYearChange)="onYearSelectChange($event)"
(dateClick)="onDateClick($event)"
(dateHover)="onDateHover($event)"
(dateFocus)="onDateFocus($event)"
(swipeStart)="onCalendarSwipeStart($event)"
(swipeMove)="onCalendarSwipeMove($event)"
(swipeEnd)="onCalendarSwipeEnd($event)"
(touchStart)="onDateCellTouchStart($event.event, $event.day)"
(touchMove)="onDateCellTouchMove($event)"
(touchEnd)="onDateCellTouchEnd($event.event, $event.day)"
(viewModeChange)="onViewModeChange($event)"
(changeYear)="changeYear($event)"
(yearClick)="onYearClick($event)"
(changeDecade)="changeDecade($event)"
(decadeClick)="onDecadeClick($event)"
(timelineZoomOut)="timelineZoomOut()"
(timelineZoomIn)="timelineZoomIn()"
(timelineMonthClick)="onTimelineMonthClick($event)"
(startTimeSliderChange)="onStartTimeSliderChange($event)"
(endTimeSliderChange)="onEndTimeSliderChange($event)"
(currentDisplayHourChange)="currentDisplayHour = $event"
(currentMinuteChange)="currentMinute = $event"
(currentSecondChange)="currentSecond = $event"
(isPmChange)="isPm = $event"
(timeChange)="timeChange()"
(startDisplayHourChange)="startDisplayHour = $event"
(startMinuteChange)="startMinute = $event"
(startSecondChange)="startSecond = $event"
(startIsPmChange)="startIsPm = $event"
(endDisplayHourChange)="endDisplayHour = $event"
(endMinuteChange)="endMinute = $event"
(endSecondChange)="endSecond = $event"
(endIsPmChange)="endIsPm = $event"
(timeRangeChange)="timeRangeChange()"
(clearValue)="clearValue($event)"
(closeCalendar)="closeCalendarWithFocusRestore()"
></ngxsmk-datepicker-content>
</ng-template>
@if (isCalendarVisible && !_shouldAppendToBody) {
<ng-container *ngTemplateOutlet="portalContent"></ng-container>
}
@if (isKeyboardHelpOpen) {
<ngxsmk-datepicker-keyboard-help
[title]="getTranslation('keyboardShortcuts')"
[closeLabel]="getTranslation('close')"
[backdropLabel]="getTranslation('closeCalendarOverlay')"
(closeRequested)="toggleKeyboardHelp()"
/>
}
</div>
`, styles: ["ngxsmk-datepicker,.ngxsmk-popover-container,.ngxsmk-backdrop{--datepicker-primary-color: var(--ion-color-primary, #6d28d9);--datepicker-primary-contrast: var(--ion-color-primary-contrast, #ffffff);--datepicker-range-background: var(--ion-color-primary-tint, #f5f3ff);--datepicker-comparison-range-color: #f59e0b;--datepicker-background: var(--ion-background-color, #ffffff);--datepicker-text-color: var(--ion-text-color, #1f2937);--datepicker-subtle-text-color: var(--ion-text-color-step-400, #6b7280);--datepicker-border-color: var(--ion-item-border-color, var(--ion-border-color, #e5e7eb));--datepicker-hover-background: #f3f4f6;--datepicker-shadow-focus: 0 0 0 3px color-mix(in srgb, var(--datepicker-primary-color) 15%, transparent);--ngxsmk-color-primary: var(--datepicker-primary-color);--ngxsmk-color-on-primary: var(--datepicker-primary-contrast);--ngxsmk-color-range-bg: var(--datepicker-range-background);--ngxsmk-color-surface: var(--datepicker-background);--ngxsmk-color-surface-hover: var(--datepicker-hover-background);--ngxsmk-color-text-main: var(--datepicker-text-color);--ngxsmk-color-text-muted: var(--datepicker-subtle-text-color);--ngxsmk-color-border: var(--datepicker-border-color);--datepicker-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);--datepicker-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .06);--datepicker-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--datepicker-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, .1), 0 10px 10px -5px rgba(0, 0, 0, .04);--datepicker-font-size-xs: 10px;--datepicker-font-size-sm: 12px;--datepicker-font-size-base: 14px;--datepicker-font-size-lg: 16px;--datepicker-font-size-xl: 18px;--datepicker-line-height: 1.5;--datepicker-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;--datepicker-spacing-xs: 4px;--datepicker-spacing-sm: 8px;--datepicker-spacing-md: 12px;--datepicker-spacing-lg: 16px;--datepicker-spacing-xl: 20px;--datepicker-spacing-2xl: 24px;--datepicker-radius-sm: 6px;--datepicker-radius-md: 8px;--datepicker-radius-lg: 12px;--datepicker-radius-xl: 16px;--datepicker-border-radius: var(--datepicker-radius-lg);--datepicker-transition-duration: .15s;--datepicker-transition-easing: cubic-bezier(.4, 0, .2, 1);--datepicker-transition-property: all;--datepicker-transition: var(--datepicker-transition-property) var(--datepicker-transition-duration) var(--datepicker-transition-easing);--datepicker-z-index-base: 2147483647;--datepicker-z-index-backdrop: 2147483646}ngxsmk-datepicker.dark-theme,.ngxsmk-popover-container.dark-theme,.ngxsmk-backdrop.dark-theme{--datepicker-range-background: rgba(139, 92, 246, .15);--datepicker-background: #1f2937;--datepicker-text-color: #f3f4f6;--datepicker-subtle-text-color: #9ca3af;--datepicker-border-color: #374151;--datepicker-hover-background: #374151;--datepicker-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .3);--datepicker-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .3), 0 2px 4px -1px rgba(0, 0, 0, .2);--datepicker-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .3), 0 4px 6px -2px rgba(0, 0, 0, .2)}ngxsmk-datepicker.dark-theme *{--datepicker-range-background: rgba(139, 92, 246, .15);--datepicker-background: #1f2937;--datepicker-text-color: #f3f4f6;--datepicker-subtle-text-color: #9ca3af;--datepicker-border-color: #374151;--datepicker-hover-background: #374151}ngxsmk-datepicker.glass-theme,.ngxsmk-popover-container.glass-theme{--datepicker-background: rgba(255, 255, 255, .7);--datepicker-border-color: rgba(255, 255, 255, .3);--datepicker-shadow-lg: 0 8px 32px 0 rgba(31, 38, 135, .37);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--datepicker-border-color)}ngxsmk-datepicker.glass-theme.dark-theme,.ngxsmk-popover-container.glass-theme.dark-theme{--datepicker-background: rgba(31, 41, 55, .7);--datepicker-border-color: rgba(255, 255, 255, .1)}ngxsmk-datepicker.md3-theme{--datepicker-primary-color: #6750a4;--datepicker-radius-lg: 28px;--datepicker-font-family: \"Roboto\", sans-serif;--datepicker-shadow-md: 0px 1px 3px 1px rgba(0, 0, 0, .15), 0px 1px 2px rgba(0, 0, 0, .3)}@media(prefers-contrast:high){ngxsmk-datepicker{--datepicker-border-color: #000000;--datepicker-text-color: #000000;--datepicker-subtle-text-color: #000000;--datepicker-background: #ffffff;--datepicker-hover-background: #f0f0f0;--datepicker-primary-color: #0000ff;--datepicker-primary-contrast: #ffffff;--datepicker-range-background: #e0e0e0;--datepicker-focus-outline: #000000}ngxsmk-datepicker.dark-theme{--datepicker-border-color: #ffffff;--datepicker-text-color: #ffffff;--datepicker-subtle-text-color: #ffffff;--datepicker-background: #000000;--datepicker-hover-background: #333333;--datepicker-primary-color: #ffffff;--datepicker-primary-contrast: #000000;--datepicker-range-background: #333333;--datepicker-focus-outline: #ffffff}ngxsmk-datepicker *{border-color:var(--datepicker-border-color)!important}.ngxsmk-day-cell{border:2px solid transparent!important}.ngxsmk-day-cell:not(.disabled):not(.empty):hover{border-color:var(--datepicker-border-color)!important;background-color:var(--datepicker-hover-background)!important}.ngxsmk-day-cell.disabled{opacity:.5!important;border-color:#ccc!important}.ngxsmk-day-cell.selected,.ngxsmk-day-cell.start-date,.ngxsmk-day-cell.end-date{border:3px solid var(--datepicker-border-color)!important;background-color:var(--datepicker-primary-color)!important;color:var(--datepicker-primary-contrast)!important}.ngxsmk-day-cell.focused,.ngxsmk-day-cell:focus-visible{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-nav-button{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-nav-button.focused,.ngxsmk-nav-button:focus-visible{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-nav-button:hover:not(:disabled){background-color:var(--datepicker-hover-background)!important;border-color:var(--datepicker-border-color)!important}.ngxsmk-input-group{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-input-group:focus-within{border-color:var(--datepicker-border-color)!important;outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-clear-button,.ngxsmk-calendar-button{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-clear-button.focused,.ngxsmk-clear-button:focus-visible,.ngxsmk-calendar-button.focused,.ngxsmk-calendar-button:focus-visible{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-popover-container{border:3px solid var(--datepicker-border-color)!important;box-shadow:0 4px 8px #0000004d!important}.ngxsmk-day-cell.in-range{background-color:var(--datepicker-range-background)!important;border-color:var(--datepicker-border-color)!important}.ngxsmk-custom-select{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-custom-select.focused,.ngxsmk-custom-select:focus-within{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}.ngxsmk-time-input{border:2px solid var(--datepicker-border-color)!important}.ngxsmk-time-input.focused,.ngxsmk-time-input:focus{outline:3px solid var(--datepicker-focus-outline)!important;outline-offset:2px!important}}@media(prefers-reduced-motion:reduce){ngxsmk-datepicker{--datepicker-transition: none}ngxsmk-datepicker *,ngxsmk-datepicker *:before,ngxsmk-datepicker *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}\n", "ngxsmk-datepicker{display:block;width:100%;position:relative;font-family:var(--datepicker-font-family);user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;overflow:visible}ngxsmk-datepicker:has(.ngxsmk-calendar-open){z-index:1000!important}.ngxsmk-datepicker-wrapper.ngxsmk-calendar-open{z-index:var(--datepicker-z-index-base);position:relative;isolation:isolate}@media(max-width:1023px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode){isolation:auto!important;transform:none!important;-webkit-transform:none!important;contain:none!important;overflow:visible!important;clip-path:none!important;clip:auto!important;position:static!important;z-index:auto!important}ngxsmk-datepicker:not(:has(.ngxsmk-no-responsive)){display:block!important;width:100%!important;overflow:visible!important;position:relative!important;isolation:auto!important;transform:none!important;-webkit-transform:none!important;contain:none!important}ngxsmk-datepicker:not(:has(.ngxsmk-no-responsive)):has(.ngxsmk-calendar-open:not(.ngxsmk-inline-mode)){overflow:visible!important;isolation:auto!important;transform:none!important;-webkit-transform:none!important;contain:none!important;z-index:auto!important}}@media(min-width:1024px){.ngxsmk-backdrop:not(.ngxsmk-inline-backdrop){display:block!important;position:fixed!important;inset:0;background:transparent!important;z-index:var(--datepicker-z-index-backdrop)!important;pointer-events:auto!important}.ngxsmk-backdrop.ngxsmk-backdrop-allow-modal-scroll{pointer-events:none!important}}@media(max-width:1023px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-backdrop,.ngxsmk-backdrop:not(.ngxsmk-inline-backdrop){display:block;visibility:visible;opacity:1;position:fixed;inset:0;width:100vw;height:100vh;height:100dvh;z-index:var(--datepicker-z-index-backdrop);background:#0000008c;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent;pointer-events:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive):not(.ngxsmk-calendar-open) .ngxsmk-backdrop,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open.ngxsmk-inline-mode .ngxsmk-backdrop{display:none;visibility:hidden;opacity:0;pointer-events:none}body:has(.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode)),html:has(.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode)){overflow:hidden}.ngxsmk-backdrop.ngxsmk-backdrop-allow-modal-scroll{pointer-events:none}}.ngxsmk-input-group{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;cursor:pointer;width:100%;border:1px solid var(--datepicker-border-color);-webkit-border-radius:var(--datepicker-radius-md);-moz-border-radius:var(--datepicker-radius-md);border-radius:var(--datepicker-radius-md);background:var(--datepicker-background);-webkit-transition:var(--datepicker-transition);-moz-transition:var(--datepicker-transition);-o-transition:var(--datepicker-transition);transition:var(--datepicker-transition);position:relative;overflow:hidden;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);-ms-touch-action:manipulation;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent}.ngxsmk-input-group:focus-within{border-color:var(--datepicker-primary-color);-webkit-box-shadow:var(--datepicker-shadow-focus),var(--datepicker-shadow-md);-moz-box-shadow:var(--datepicker-shadow-focus),var(--datepicker-shadow-md);box-shadow:var(--datepicker-shadow-focus),var(--datepicker-shadow-md);outline:none}.ngxsmk-input-group:hover:not(.disabled){border-color:var(--datepicker-primary-color);-webkit-box-shadow:var(--datepicker-shadow-md);-moz-box-shadow:var(--datepicker-shadow-md);box-shadow:var(--datepicker-shadow-md)}.ngxsmk-input-group.disabled{cursor:not-allowed;opacity:.6;background:var(--datepicker-hover-background)}.ngxsmk-native-input-group{cursor:default}.ngxsmk-native-input-group .ngxsmk-native-input{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none}.ngxsmk-native-input-group .ngxsmk-native-input::-webkit-calendar-picker-indicator{cursor:pointer;opacity:1;margin-left:4px}.ngxsmk-native-input-group .ngxsmk-native-input::-webkit-inner-spin-button,.ngxsmk-native-input-group .ngxsmk-native-input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.ngxsmk-display-input{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;width:100%;padding:var(--datepicker-spacing-md) var(--datepicker-spacing-lg);font-size:var(--datepicker-font-size-base);line-height:var(--datepicker-line-height);color:var(--datepicker-text-color);background:transparent;border:none;outline:none;cursor:pointer;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;-webkit-transition:var(--datepicker-transition);-moz-transition:var(--datepicker-transition);-o-transition:var(--datepicker-transition);transition:var(--datepicker-transition);min-height:20px;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;font-weight:400;-ms-touch-action:manipulation;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent}.ngxsmk-display-input:disabled{cursor:not-allowed;opacity:.6}.ngxsmk-display-input::placeholder{color:var(--datepicker-subtle-text-color);font-weight:400}.ngxsmk-clear-button{background:none;border:none;padding:var(--datepicker-spacing-sm);margin-right:var(--datepicker-spacing-sm);cursor:pointer;color:var(--datepicker-subtle-text-color);line-height:1;-webkit-border-radius:var(--datepicker-radius-sm);-moz-border-radius:var(--datepicker-radius-sm);border-radius:var(--datepicker-radius-sm);-webkit-transition:var(--datepicker-transition);-moz-transition:var(--datepicker-transition);-o-transition:var(--datepicker-transition);transition:var(--datepicker-transition);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;will-change:background-color;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-ms-touch-action:manipulation;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent}.ngxsmk-clear-button svg{width:16px!important;height:16px!important;min-width:16px;min-height:16px;max-width:16px;max-height:16px}.ngxsmk-clear-button:hover:not(:disabled){color:var(--datepicker-text-color);background:var(--datepicker-hover-background)}.ngxsmk-clear-button:focus-visible{outline:2px solid var(--datepicker-primary-color);outline-offset:2px}.ngxsmk-calendar-button{background:none;border:none;padding:var(--datepicker-spacing-sm);margin-right:var(--datepicker-spacing-sm);cursor:pointer;color:var(--datepicker-subtle-text-color);line-height:1;-webkit-border-radius:var(--datepicker-radius-sm);-moz-border-radius:var(--datepicker-radius-sm);border-radius:var(--datepicker-radius-sm);-webkit-transition:var(--datepicker-transition);-moz-transition:var(--datepicker-transition);-o-transition:var(--datepicker-transition);transition:var(--datepicker-transition);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-ms-touch-action:manipulation;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent;flex-shrink:0}.ngxsmk-calendar-button svg{width:18px;height:18px}.ngxsmk-calendar-button:hover:not(:disabled){color:var(--datepicker-primary-color);background:var(--datepicker-hover-background)}.ngxsmk-calendar-button:focus-visible{outline:2px solid var(--datepicker-primary-color);outline-offset:2px}.ngxsmk-calendar-button:disabled{cursor:not-allowed;opacity:.6}.ngxsmk-popover-container{position:absolute;top:calc(100% + 8px);left:0;z-index:var(--datepicker-z-index-base);width:100%;min-width:100%;max-width:100%;overflow:visible;display:block;visibility:visible;opacity:1;pointer-events:auto}.ngxsmk-popover-container.ngxsmk-inline-container{position:static!important;top:0;left:0;margin:0;transform:none!important;box-shadow:none!important;animation:none!important;width:auto;min-width:0;max-width:100%;display:inline-block}.ngxsmk-popover-container.ngxsmk-popover-open{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content;max-width:-webkit-fit-content;max-width:-moz-fit-content;max-width:fit-content}.ngxsmk-popover-container.ngxsmk-popover-open .ngxsmk-calendar-container{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content}.ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open{height:auto!important;max-height:none!important;overflow:visible!important}.ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open .ngxsmk-datepicker-container,.ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open .ngxsmk-calendar-container{overflow:visible!important;max-height:none!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-time-only-popover):not(.ngxsmk-has-time-selection){max-height:calc(100dvh - 48px)!important;min-height:0!important;overflow:hidden!important;display:-webkit-box!important;display:-webkit-flex!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-orient:vertical!important;-webkit-flex-direction:column!important;-ms-flex-direction:column!important;flex-direction:column!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-time-only-popover):not(.ngxsmk-has-time-selection)>*{-webkit-box-flex:1!important;-webkit-flex:1 1 auto!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important;min-height:0!important;min-width:0!important;width:100%!important;max-width:100%!important;overflow:hidden!important;box-sizing:border-box!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-time-only-popover):not(.ngxsmk-has-time-selection) .ngxsmk-datepicker-container{display:-webkit-box!important;display:-webkit-flex!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-orient:vertical!important;-webkit-flex-direction:column!important;-ms-flex-direction:column!important;flex-direction:column!important;min-height:0!important;min-width:0!important;width:100%!important;max-width:100%!important;overflow:hidden!important;box-sizing:border-box!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-time-only-popover):not(.ngxsmk-has-time-selection) .ngxsmk-calendar-container{-webkit-box-flex:1!important;-webkit-flex:1 1 auto!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important;min-height:0!important;min-width:0!important;width:100%!important;max-width:100%!important;overflow:hidden!important;display:-webkit-box!important;display:-webkit-flex!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-orient:vertical!important;-webkit-flex-direction:column!important;-ms-flex-direction:column!important;flex-direction:column!important;box-sizing:border-box!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-time-only-popover):not(.ngxsmk-has-time-selection) .ngxsmk-multi-calendar-container{-webkit-box-flex:1!important;-webkit-flex:1 1 auto!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important;min-height:0!important;min-width:0!important;width:100%!important;max-width:100%!important;overflow:hidden!important;box-sizing:border-box!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-time-only-popover):not(.ngxsmk-has-time-selection) .ngxsmk-calendar-month{min-height:0!important;min-width:0!important;width:100%!important;max-width:100%!important;overflow:hidden!important;box-sizing:border-box!important;display:-webkit-box!important;display:-webkit-flex!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-orient:vertical!important;-webkit-flex-direction:column!important;-ms-flex-direction:column!important;flex-direction:column!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-time-only-popover):not(.ngxsmk-has-time-selection) .ngxsmk-days-grid-wrapper{-webkit-box-flex:1!important;-webkit-flex:1 1 auto!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important;min-height:0!important;min-width:0!important;width:100%!important;max-width:100%!important;overflow:hidden!important;box-sizing:border-box!important}.ngxsmk-popover-container.ngxsmk-align-left{left:0!important;right:auto!important;transform:none!important}.ngxsmk-popover-container.ngxsmk-align-right{left:auto!important;right:0!important;transform:none!important}.ngxsmk-popover-container.ngxsmk-align-center{left:50%!important;right:auto!important;transform:translate(-50%)!important;-webkit-transform:translateX(-50%)!important}@media(max-width:1023px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container){isolation:auto;contain:none;clip-path:none;clip:auto;overflow:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-popover-container:not(.ngxsmk-inline-container),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;visibility:visible;opacity:1;position:fixed;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%);transform:translate(-50%,-50%);z-index:2147483647;pointer-events:none;width:calc(100% - 12px);min-width:280px;max-width:480px;max-height:calc(100vh - 48px - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px));max-height:calc(100dvh - 48px - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px));padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px);min-height:0;overflow:hidden}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-popover-open):not(.ngxsmk-inline-container){display:none;visibility:hidden;opacity:0;pointer-events:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container){height:auto;max-height:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-popover-container:not(.ngxsmk-inline-container)>*,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container)>*{pointer-events:auto;-webkit-box-flex:1;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;min-height:0;overflow:hidden}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-datepicker-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;min-height:0;overflow:hidden}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-calendar-container{-webkit-box-flex:1;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;min-height:0;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-multi-calendar-container{-webkit-box-flex:1;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;min-height:0;overflow:hidden}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-calendar-month{min-height:0;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-days-grid-wrapper{-webkit-box-flex:1;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;min-height:0;overflow:hidden}.ngxsmk-input-and-error{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;width:100%;gap:4px}.ngxsmk-validation-error{font-size:var(--datepicker-font-size-sm, 12px);color:var(--datepicker-error-color, #b00020);margin-top:2px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-datepicker-container,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-calendar-container{pointer-events:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-day-cell{pointer-events:auto;min-width:44px;min-height:44px;margin:2px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-day-number{pointer-events:auto;font-size:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive).ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-backdrop{pointer-events:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open.ngxsmk-bottom-sheet:not(.ngxsmk-inline-container){inset:auto 0 0;transform:none;width:100%;max-width:100%;border-radius:20px 20px 0 0;margin:0;-webkit-animation:slideUpMobile .3s cubic-bezier(.16,1,.3,1);-moz-animation:slideUpMobile .3s cubic-bezier(.16,1,.3,1);animation:slideUpMobile .3s cubic-bezier(.16,1,.3,1)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open.ngxsmk-fullscreen:not(.ngxsmk-inline-container){inset:0;transform:none;width:100%;height:100%;max-height:100%;max-width:100%;border-radius:0;margin:0;-webkit-animation:fadeInScale .2s ease-out;-moz-animation:fadeInScale .2s ease-out;animation:fadeInScale .2s ease-out}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container{width:100%;max-width:100%;overflow:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container{padding:8px;width:100%;max-width:100%;box-sizing:border-box;overflow:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header>*,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects>*,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons>*{margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header{display:flex;width:100%;gap:4px;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects{-webkit-box-flex:1;-webkit-flex:1 1 0%;-ms-flex:1 1 0%;flex:1 1 0%;min-width:0;display:flex;gap:4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects>*{-webkit-box-flex:1;-webkit-flex:1 1 0%;-ms-flex:1 1 0%;flex:1 1 0%;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons{-webkit-box-flex:0;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;display:flex;gap:4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{width:44px;height:44px;min-height:44px;padding:10px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-container{height:44px;min-height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select{flex:1 1 0%;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-container{height:100%;min-height:100%;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-display{height:100%;min-height:100%;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer>*{margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button{height:44px;min-height:44px;box-sizing:border-box;padding:10px 16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid{gap:2px;margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid>*{margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-name{font-size:13px;width:100%}}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-8px,0);transform:translate3d(0,-8px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}}@-moz-keyframes fadeInDown{0%{opacity:0;-moz-transform:translate3d(0,-8px,0);transform:translate3d(0,-8px,0)}to{opacity:1;-moz-transform:translate3d(0,0,0);transform:translateZ(0)}}@keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-8px,0);-moz-transform:translate3d(0,-8px,0);-ms-transform:translate3d(0,-8px,0);transform:translate3d(0,-8px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);-moz-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translateZ(0)}}@-webkit-keyframes fadeInDownMobile{0%{opacity:0;-webkit-transform:translate3d(-50%,-8px,0);transform:translate3d(-50%,-8px,0)}to{opacity:1;-webkit-transform:translate3d(-50%,0,0);transform:translate3d(-50%,0,0)}}@-moz-keyframes fadeInDownMobile{0%{opacity:0;-moz-transform:translate3d(-50%,-8px,0);transform:translate3d(-50%,-8px,0)}to{opacity:1;-moz-transform:translate3d(-50%,0,0);transform:translate3d(-50%,0,0)}}@keyframes fadeInDownMobile{0%{opacity:0;-webkit-transform:translate3d(-50%,-8px,0);-moz-transform:translate3d(-50%,-8px,0);-ms-transform:translate3d(-50%,-8px,0);transform:translate3d(-50%,-8px,0)}to{opacity:1;-webkit-transform:translate3d(-50%,0,0);-moz-transform:translate3d(-50%,0,0);-ms-transform:translate3d(-50%,0,0);transform:translate3d(-50%,0,0)}}@-webkit-keyframes fadeInScale{0%{opacity:0;-webkit-transform:translate(-50%,-50%) scale(.95);transform:translate(-50%,-50%) scale(.95)}to{opacity:1;-webkit-transform:translate(-50%,-50%) scale(1);transform:translate(-50%,-50%) scale(1)}}@-moz-keyframes fadeInScale{0%{opacity:0;-moz-transform:translate(-50%,-50%) scale(.95);transform:translate(-50%,-50%) scale(.95)}to{opacity:1;-moz-transform:translate(-50%,-50%) scale(1);transform:translate(-50%,-50%) scale(1)}}@keyframes fadeInScale{0%{opacity:0;-webkit-transform:translate(-50%,-50%) scale(.95);-moz-transform:translate(-50%,-50%) scale(.95);-ms-transform:translate(-50%,-50%) scale(.95);transform:translate(-50%,-50%) scale(.95)}to{opacity:1;-webkit-transform:translate(-50%,-50%) scale(1);-moz-transform:translate(-50%,-50%) scale(1);-ms-transform:translate(-50%,-50%) scale(1);transform:translate(-50%,-50%) scale(1)}}@-webkit-keyframes slideUpMobile{0%{transform:translateY(100%)}to{transform:translateY(0)}}@-moz-keyframes slideUpMobile{0%{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes slideUpMobile{0%{transform:translateY(100%)}to{transform:translateY(0)}}ngxsmk-datepicker.ngxsmk-inline{display:block;width:fit-content;max-width:100%}.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode{display:block;overflow:visible}.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode .ngxsmk-popover-container.ngxsmk-inline-container{overflow:visible;position:static!important}.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode .ngxsmk-datepicker-container{-webkit-box-shadow:none!important;-moz-box-shadow:none!important;box-shadow:none!important;overflow:visible;margin-top:0!important;position:static!important;z-index:0;width:auto!important;min-width:0!important;max-width:100%!important}.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode .ngxsmk-calendar-container{width:auto!important;min-width:0!important;max-width:100%!important}.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode .ngxsmk-ranges-container,.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode .ngxsmk-ranges-container ul{width:100%;max-width:100%;min-width:0;box-sizing:border-box;overflow:visible}.ngxsmk-datepicker-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex!important;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column!important;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;min-width:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;margin:0;padding:0;position:relative;z-index:var(--datepicker-z-index-base);pointer-events:auto;background:var(--datepicker-background);-webkit-box-shadow:var(--datepicker-shadow-lg);-moz-box-shadow:var(--datepicker-shadow-lg);box-shadow:var(--datepicker-shadow-lg);border:1px solid var(--datepicker-border-color);-webkit-border-radius:var(--datepicker-border-radius);-moz-border-radius:var(--datepicker-border-radius);border-radius:var(--datepicker-border-radius)}.ngxsmk-calendar-loading{position:absolute;inset:0;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;gap:var(--datepicker-spacing-md, 12px);background:var(--datepicker-background);opacity:.95;z-index:100}.ngxsmk-calendar-loading-spinner{width:28px;height:28px;border:3px solid var(--datepicker-border-color);border-top-color:var(--datepicker-accent-color, #1976d2);-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;-webkit-animation:ngxsmk-spin .7s linear infinite;-moz-animation:ngxsmk-spin .7s linear infinite;animation:ngxsmk-spin .7s linear infinite}.ngxsmk-calendar-loading-text{font-size:var(--datepicker-font-size-sm, 14px);color:var(--datepicker-text-secondary, #666)}@-webkit-keyframes ngxsmk-spin{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes ngxsmk-spin{to{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes ngxsmk-spin{to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);transform:rotate(360deg)}}.ngxsmk-calendar-container{font-size:var(--datepicker-font-size-base);line-height:var(--datepicker-line-height);padding:var(--datepicker-spacing-xs);width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content}.ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:auto;max-width:100%;padding:var(--datepicker-spacing-xs);box-sizing:border-box}.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal,.ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal){width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:auto!important;max-width:100%!important}.ngxsmk-calendar-container.ngxsmk-time-only-mode{padding:var(--datepicker-spacing-xl)!important;min-width:320px;overflow:visible!important}.ngxsmk-calendar-container.ngxsmk-time-only-mode .ngxsmk-time-selection{overflow:visible!important;align-items:baseline;position:relative;z-index:1;margin-top:0;padding-top:0;border-top:none}.ngxsmk-calendar-container.ngxsmk-time-only-mode .ngxsmk-time-selection ngxsmk-custom-select{position:relative;z-index:10}.ngxsmk-calendar-container.ngxsmk-time-only-mode .ngxsmk-time-selection ngxsmk-custom-select[data-open=true]{z-index:10000000!important}.ngxsmk-ranges-container{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content;max-width:100%;padding:var(--datepicker-spacing-lg);background:var(--datepicker-hover-background);border-radius:var(--datepicker-radius-lg);border:1px solid var(--datepicker-border-color);box-sizing:border-box;flex-shrink:0;position:relative;z-index:var(--datepicker-z-index-base)}.ngxsmk-ranges-container ul{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;gap:var(--datepicker-spacing-sm);list-style:none;padding:0;margin:calc(var(--datepicker-spacing-sm) / -2);width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:auto;max-width:100%;overflow:visible}.ngxsmk-ranges-container ul>*{margin:calc(var(--datepicker-spacing-sm) / 2)}.ngxsmk-ranges-container li{padding:var(--datepicker-spacing-sm) var(--datepicker-spacing-md);font-size:var(--datepicker-font-size-sm);line-height:var(--datepicker-line-height);border:1px solid var(--datepicker-border-color);-webkit-border-radius:var(--datepicker-radius-md);-moz-border-radius:var(--datepicker-radius-md);border-radius:var(--datepicker-radius-md);cursor:pointer;-webkit-transition:var(--datepicker-transition);-moz-transition:var(--datepicker-transition);-o-transition:var(--datepicker-transition);transition:var(--datepicker-transition);-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;background:var(--datepicker-background);font-weight:500;-ms-touch-action:manipulation;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent}.ngxsmk-ranges-container li:hover:not(.disabled){background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);border-color:var(--datepicker-primary-color);-webkit-transform:translate3d(0,-1px,0);-moz-transform:translate3d(0,-1px,0);-ms-transform:translate3d(0,-1px,0);transform:translate3d(0,-1px,0);-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);will-change:transform}.ngxsmk-ranges-container li.disabled{cursor:not-allowed;opacity:.5;background-color:transparent!important;color:var(--datepicker-subtle-text-color)}.ngxsmk-ranges-container li.ngxsmk-preset-active{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);border-color:var(--datepicker-primary-color);font-weight:700}.ngxsmk-header{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;margin-bottom:var(--datepicker-spacing-lg);position:relative;z-index:2;gap:var(--datepicker-spacing-md);padding-bottom:var(--datepicker-spacing-md);border-bottom:1px solid var(--datepicker-border-color);margin-left:calc(var(--datepicker-spacing-md) / -2);margin-right:calc(var(--datepicker-spacing-md) / -2)}.ngxsmk-header>*{margin-left:calc(var(--datepicker-spacing-md) / 2);margin-right:calc(var(--datepicker-spacing-md) / 2)}.ngxsmk-month-year-selects{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;gap:var(--datepicker-spacing-sm);-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;margin:calc(var(--datepicker-spacing-sm) / -2)}.ngxsmk-month-year-selects>*{margin:calc(var(--datepicker-spacing-sm) / 2);cursor:pointer}.ngxsmk-month-year-selects ngxsmk-custom-select{cursor:pointer;flex:1;min-width:0;height:40px;min-height:40px}.ngxsmk-month-year-selects .ngxsmk-select-container,.ngxsmk-month-year-selects .ngxsmk-select-display{height:100%;min-height:100%;box-sizing:border-box}.ngxsmk-nav-buttons{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;gap:var(--datepicker-spacing-xs);margin:calc(var(--datepicker-spacing-xs) / -2)}.ngxsmk-nav-buttons>*{margin:calc(var(--datepicker-spacing-xs) / 2)}.ngxsmk-nav-button{padding:var(--datepicker-spacing-sm);border:1px solid transparent;-webkit-border-radius:var(--datepicker-border-radius);-moz-border-radius:var(--datepicker-border-radius);border-radius:var(--datepicker-border-radius);background:#00000008;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-webkit-align-items:center;will-change:transform;backface-visibility:hidden;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;color:var(--datepicker-text-color);-webkit-transition:var(--datepicker-transition);-moz-transition:var(--datepicker-transition);-o-transition:var(--datepicker-transition);transition:var(--datepicker-transition);min-width:36px;width:40px;height:40px;min-height:40px;box-sizing:border-box;-ms-touch-action:manipulation;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent}.ngxsmk-nav-button:hover:not(:disabled){background-color:var(--datepicker-hover-background);border-color:#0000000d;-webkit-transform:scale3d(1.05,1.05,1);-moz-transform:scale3d(1.05,1.05,1);-ms-transform:scale3d(1.05,1.05,1);transform:scale3d(1.05,1.05,1);will-change:transform}.ngxsmk-nav-button:active:not(:disabled){-webkit-transform:scale3d(.95,.95,1);-moz-transform:scale3d(.95,.95,1);-ms-transform:scale3d(.95,.95,1);transform:scale3d(.95,.95,1)}.ngxsmk-nav-button:disabled{cursor:not-allowed;opacity:.4}.ngxsmk-nav-button:focus-visible{outline:2px solid var(--datepicker-primary-color);outline-offset:2px}.ngxsmk-nav-button svg{width:18px;height:18px;stroke-width:32}.ngxsmk-days-grid-wrapper{margin-top:var(--datepicker-spacing-md);overflow:visible;position:relative;touch-action:pan-y;-webkit-overflow-scrolling:touch}.ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid-wrapper{width:100%;max-width:100%}.ngxsmk-days-grid{display:grid;grid-template-columns:repeat(7,1fr);-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;gap:var(--datepicker-spacing-xs);width:100%;position:relative;isolation:isolate;-ms-touch-action:manipulation;touch-action:manipulation;margin:calc(var(--datepicker-spacing-xs) / -2);contain:layout style;will-change:contents;transition:opacity .15s cubic-bezier(.4,0,.2,1)}.ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid{width:100%;max-width:100%}.ngxsmk-days-grid>*{margin:calc(var(--datepicker-spacing-xs) / 2)}.ngxsmk-days-grid.ngxsmk-with-week-numbers{grid-template-columns:minmax(1.5em,auto) repeat(7,1fr)}.ngxsmk-week-number,.ngxsmk-week-number-header{font-size:var(--datepicker-font-size-sm);color:var(--datepicker-subtle-text-color);display:flex;align-items:center;justify-content:center;-webkit-user-select:none;user-select:none}.ngxsmk-day-cell:has(.ngxsmk-day-secondary),.ngxsmk-day-cell:has(.ngxsmk-day-meta-label){-webkit-box-orient:vertical;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.ngxsmk-day-meta-label{font-size:.6em;line-height:1;color:var(--datepicker-subtle-text-color);margin-top:1px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ngxsmk-day-indicator{position:absolute;top:3px;right:3px;width:6px;height:6px;border-radius:50%;pointer-events:none}.ngxsmk-day-secondary{font-size:.62em;line-height:1;color:var(--datepicker-subtle-text-color);margin-top:1px}.ngxsmk-day-name{font-size:var(--datepicker-font-size-sm);padding:var(--datepicker-spacing-md) 0;color:var(--datepicker-subtle-text-color);font-weight:600;line-height:var(--datepicker-line-height);text-transform:uppercase;letter-spacing:.5px;width:100%;box-sizing:border-box}.ngxsmk-day-cell{width:40px;height:40px;min-width:40px;max-width:40px;min-height:40px;max-height:40px;position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;cursor:pointer;-webkit-border-radius:var(--datepicker-radius-sm);-moz-border-radius:var(--datepicker-radius-sm);border-radius:var(--datepicker-radius-sm);-webkit-transition:background-color .15s cubic-bezier(.4,0,.2,1);-moz-transition:background-color .15s cubic-bezier(.4,0,.2,1);-o-transition:background-color .15s cubic-bezier(.4,0,.2,1);transition:background-color .15s cubic-bezier(.4,0,.2,1);background-color:transparent;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;overflow:visible;z-index:1;margin:0 auto;contain:layout style;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;backface-visibility:hidden;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:auto;-webkit-touch-callout:none}@media(max-width:1024px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-cell{width:100%;height:auto;aspect-ratio:1/1;min-width:0;max-width:none;min-height:0;max-height:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-number{width:100%;height:100%;min-width:0;max-width:none;min-height:0;max-height:none;font-size:14px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{min-width:40px;min-height:40px;height:40px;padding:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-container{height:40px;min-height:40px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-display{min-height:100%;height:100%;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer{min-height:40px;padding:8px 12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-input-group{min-width:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input{min-height:44px;line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input::placeholder{line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input:not(:placeholder-shown){line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button{padding:var(--datepicker-spacing-xs);margin-right:var(--datepicker-spacing-xs)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-button{display:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button svg{width:16px;height:16px;min-width:16px;min-height:16px;max-width:16px;max-height:16px}}.ngxsmk-day-number{width:36px;height:36px;min-width:36px;max-width:36px;min-height:36px;max-height:36px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;color:var(--datepicker-text-color);font-size:var(--datepicker-font-size-base);line-height:var(--datepicker-line-height);position:relative;z-index:2;font-weight:500;-webkit-transition:background-color .15s cubic-bezier(.4,0,.2,1),color .15s cubic-bezier(.4,0,.2,1),-webkit-transform .15s cubic-bezier(.4,0,.2,1),border-color .15s cubic-bezier(.4,0,.2,1),-webkit-box-shadow .15s cubic-bezier(.4,0,.2,1);-moz-transition:background-color .15s cubic-bezier(.4,0,.2,1),color .15s cubic-bezier(.4,0,.2,1),-moz-transform .15s cubic-bezier(.4,0,.2,1),border-color .15s cubic-bezier(.4,0,.2,1),-moz-box-shadow .15s cubic-bezier(.4,0,.2,1);-o-transition:background-color .15s cubic-bezier(.4,0,.2,1),color .15s cubic-bezier(.4,0,.2,1),-o-transform .15s cubic-bezier(.4,0,.2,1),border-color .15s cubic-bezier(.4,0,.2,1),box-shadow .15s cubic-bezier(.4,0,.2,1);transition:background-color .15s cubic-bezier(.4,0,.2,1),color .15s cubic-bezier(.4,0,.2,1),transform .15s cubic-bezier(.4,0,.2,1),border-color .15s cubic-bezier(.4,0,.2,1),box-shadow .15s cubic-bezier(.4,0,.2,1);-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;contain:layout style;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0)}.ngxsmk-day-cell:not(.disabled):not(.empty):hover .ngxsmk-day-number{background-color:var(--datepicker-hover-background);color:var(--datepicker-primary-color);-webkit-transform:scale3d(1.1,1.1,1);-moz-transform:scale3d(1.1,1.1,1);-ms-transform:scale3d(1.1,1.1,1);transform:scale3d(1.1,1.1,1);will-change:transform}.ngxsmk-day-cell.focused{z-index:10}.ngxsmk-day-cell.focused .ngxsmk-day-number{outline:2px solid var(--datepicker-primary-color);outline-offset:2px;background-color:var(--datepicker-hover-background);z-index:11}.ngxsmk-day-cell.selected:not(.start-date):not(.end-date):not(.in-range):not(.preview-range){z-index:3}.ngxsmk-day-cell.selected:not(.start-date):not(.end-date):not(.in-range):not(.preview-range) .ngxsmk-day-number{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);position:relative;z-index:4}.ngxsmk-day-cell.start-date:not(.in-range):not(.preview-range):not(.end-date),.ngxsmk-day-cell.end-date:not(.in-range):not(.preview-range):not(.start-date){z-index:3}.ngxsmk-day-cell.start-date:not(.in-range):not(.preview-range):not(.end-date) .ngxsmk-day-number,.ngxsmk-day-cell.end-date:not(.in-range):not(.preview-range):not(.start-date) .ngxsmk-day-number{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);position:relative;z-index:4;border:none}.ngxsmk-day-cell.multiple-selected .ngxsmk-day-number{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;border:2px solid var(--datepicker-primary-contrast);-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);position:relative;z-index:2}.ngxsmk-day-cell.in-range,.ngxsmk-day-cell.preview-range{background-color:var(--datepicker-range-background);z-index:1}.ngxsmk-day-cell.in-comparison-range{position:relative}.ngxsmk-day-cell.in-comparison-range:after{content:\"\";position:absolute;right:2px;bottom:2px;left:2px;height:2px;border-radius:1px;background-color:var(--datepicker-comparison-range-color, #f59e0b);pointer-events:none;z-index:3}.ngxsmk-day-cell.start-date.in-range,.ngxsmk-day-cell.end-date.in-range,.ngxsmk-day-cell.start-date.preview-range,.ngxsmk-day-cell.end-date.preview-range{background-color:var(--datepicker-range-background);z-index:2}.ngxsmk-day-cell.start-date.in-range .ngxsmk-day-number,.ngxsmk-day-cell.end-date.in-range .ngxsmk-day-number,.ngxsmk-day-cell.start-date.preview-range .ngxsmk-day-number,.ngxsmk-day-cell.end-date.preview-range .ngxsmk-day-number{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);position:relative;z-index:7}.ngxsmk-day-cell.start-date.end-date:not(.in-range) .ngxsmk-day-number{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);position:relative;z-index:2}.ngxsmk-day-cell.start-date.end-date.in-range{background-color:var(--datepicker-range-background)}.ngxsmk-day-cell.holiday:not(.selected):not(.start-date):not(.end-date) .ngxsmk-day-number{color:#f97316;font-weight:600;position:relative}.ngxsmk-day-cell.holiday:not(.selected):not(.start-date):not(.end-date) .ngxsmk-day-number:after{content:\"\";position:absolute;bottom:4px;left:50%;transform:translate(-50%);width:4px;height:4px;background-color:#f97316;border-radius:50%}.ngxsmk-day-cell.start-date.end-date.in-range .ngxsmk-day-number{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);position:relative;z-index:2}.ngxsmk-day-cell.disabled{background-color:transparent!important;color:var(--datepicker-subtle-text-color);cursor:not-allowed;pointer-events:none;opacity:.4}.ngxsmk-day-cell.disabled.in-range,.ngxsmk-day-cell.disabled.preview-range{background-color:transparent!important}.ngxsmk-day-cell.disabled.in-range .ngxsmk-day-number,.ngxsmk-day-cell.disabled.preview-range .ngxsmk-day-number{text-decoration:line-through;text-decoration-color:var(--datepicker-error-color, #ef4444);text-decoration-thickness:2px}.ngxsmk-day-cell.empty{opacity:.3;cursor:default}.ngxsmk-day-cell.empty .ngxsmk-day-number{color:var(--datepicker-subtle-text-color)}.ngxsmk-day-cell.today:not(.selected):not(.start-date):not(.end-date):not(.in-range):not(.preview-range):not(.multiple-selected){z-index:5}.ngxsmk-day-cell.today:not(.selected):not(.start-date):not(.end-date):not(.in-range):not(.preview-range):not(.multiple-selected) .ngxsmk-day-number{border:2px solid var(--datepicker-primary-color);font-weight:600;background-color:transparent;color:var(--datepicker-primary-color);z-index:6;box-sizing:border-box}.ngxsmk-day-cell.today.selected:not(.start-date):not(.end-date):not(.in-range):not(.preview-range):not(.multiple-selected) .ngxsmk-day-number{border:none;background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);z-index:4}.ngxsmk-day-cell.today.start-date:not(.in-range):not(.preview-range):not(.end-date) .ngxsmk-day-number,.ngxsmk-day-cell.today.end-date:not(.in-range):not(.preview-range):not(.start-date) .ngxsmk-day-number{border:none;background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);font-weight:600;-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);z-index:4}.ngxsmk-day-cell.today.in-range:not(.start-date):not(.end-date) .ngxsmk-day-number,.ngxsmk-day-cell.today.preview-range:not(.start-date):not(.end-date) .ngxsmk-day-number{background-color:transparent;border:2px solid var(--datepicker-primary-color);color:var(--datepicker-primary-color);font-weight:600;box-sizing:border-box}.ngxsmk-day-cell.holiday .ngxsmk-day-number{color:var(--datepicker-primary-color);position:relative}.ngxsmk-day-cell.holiday .ngxsmk-day-number:after{content:\"\";position:absolute;bottom:2px;left:50%;-webkit-transform:translate3d(-50%,0,0);-moz-transform:translate3d(-50%,0,0);-ms-transform:translate3d(-50%,0,0);transform:translate3d(-50%,0,0);width:4px;height:4px;background:var(--datepicker-primary-color);-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%}.ngxsmk-time-selection{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;gap:var(--datepicker-spacing-sm);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;margin-top:var(--datepicker-spacing-lg);padding-top:var(--datepicker-spacing-lg);border-top:1px solid var(--datepicker-border-color);width:100%;overflow:visible!important;position:relative;z-index:1;min-width:0;margin-left:calc(var(--datepicker-spacing-sm) / -2);margin-right:calc(var(--datepicker-spacing-sm) / -2)}.ngxsmk-time-selection>*{margin-left:calc(var(--datepicker-spacing-sm) / 2);margin-right:calc(var(--datepicker-spacing-sm) / 2);-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-time-selection ngxsmk-custom-select{--custom-select-width: 75px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;position:relative;z-index:10;cursor:pointer}.ngxsmk-time-selection ngxsmk-custom-select[data-open=true]{z-index:10000000!important;position:relative}.ngxsmk-time-selection ngxsmk-custom-select[data-open=true] .ngxsmk-options-panel{z-index:10000001!important;position:absolute!important}.ngxsmk-popover-container.ngxsmk-has-time-selection,.ngxsmk-popover-container.ngxsmk-has-time-selection .ngxsmk-datepicker-container,.ngxsmk-popover-container.ngxsmk-has-time-selection .ngxsmk-calendar-container{overflow:visible!important}.ngxsmk-time-label{font-size:var(--datepicker-font-size-base);line-height:var(--datepicker-line-height);color:var(--datepicker-text-color);font-weight:500;margin-right:var(--datepicker-spacing-xs);white-space:nowrap;flex-shrink:0}.ngxsmk-time-separator{font-weight:600;color:var(--datepicker-text-color);font-size:var(--datepicker-font-size-lg);white-space:nowrap;flex-shrink:0}.ngxsmk-timezone-selection{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;gap:var(--datepicker-spacing-sm);margin-top:var(--datepicker-spacing-md);padding-top:var(--datepicker-spacing-md);border-top:1px solid var(--datepicker-border-color);width:100%}.ngxsmk-timezone-label{font-size:var(--datepicker-font-size-base);color:var(--datepicker-text-color);font-weight:500;white-space:nowrap}.ngxsmk-timezone-selection ngxsmk-custom-select{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.ngxsmk-time-range-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:var(--datepicker-spacing-md);width:100%;margin-top:var(--datepicker-spacing-lg);padding-top:var(--datepicker-spacing-lg);border-top:1px solid var(--datepicker-border-color)}.ngxsmk-time-range-start,.ngxsmk-time-range-end{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;gap:var(--datepicker-spacing-sm)}.ngxsmk-time-range-label{font-size:var(--datepicker-font-size-base);line-height:var(--datepicker-line-height);color:var(--datepicker-text-color);font-weight:500;white-space:nowrap;min-width:40px}.ngxsmk-footer{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end;gap:var(--datepicker-spacing-sm);margin-top:var(--datepicker-spacing-sm);padding-top:var(--datepicker-spacing-sm);border-top:1px solid var(--datepicker-border-color);margin-left:calc(var(--datepicker-spacing-sm) / -2);margin-right:calc(var(--datepicker-spacing-sm) / -2)}.ngxsmk-footer>*{margin-left:calc(var(--datepicker-spacing-sm) / 2);margin-right:calc(var(--datepicker-spacing-sm) / 2)}.ngxsmk-clear-button-footer,.ngxsmk-close-button{padding:var(--datepicker-spacing-xs) var(--datepicker-spacing-md);-webkit-border-radius:var(--datepicker-radius-md);-moz-border-radius:var(--datepicker-radius-md);border-radius:var(--datepicker-radius-md);font-size:var(--datepicker-font-size-base);line-height:var(--datepicker-line-height);cursor:pointer;-webkit-transition:var(--datepicker-transition);-moz-transition:var(--datepicker-transition);-o-transition:var(--datepicker-transition);transition:var(--datepicker-transition);border:1px solid var(--datepicker-border-color);font-weight:500;min-height:40px;-ms-touch-action:manipulation;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-ms-tap-highlight-color:transparent}.ngxsmk-clear-button-footer{background:var(--datepicker-background);color:var(--datepicker-text-color)}.ngxsmk-clear-button-footer:hover:not(:disabled){background-color:var(--datepicker-hover-background);border-color:var(--datepicker-border-color)}.ngxsmk-close-button{background-color:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);border-color:var(--datepicker-primary-color)}.ngxsmk-close-button:hover:not(:disabled){background-color:var(--datepicker-primary-color);opacity:.9;-webkit-transform:translate3d(0,-1px,0);-moz-transform:translate3d(0,-1px,0);-ms-transform:translate3d(0,-1px,0);transform:translate3d(0,-1px,0);-webkit-box-shadow:var(--datepicker-shadow-sm);-moz-box-shadow:var(--datepicker-shadow-sm);box-shadow:var(--datepicker-shadow-sm);will-change:transform}.ngxsmk-close-button:active:not(:disabled){-webkit-transform:translate3d(0,0,0);-moz-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translateZ(0)}.ngxsmk-clear-button-footer:focus-visible,.ngxsmk-close-button:focus-visible{outline:2px solid var(--datepicker-primary-color);outline-offset:2px}@media(min-width:320px)and (max-width:1023px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):not(.ngxsmk-bottom-sheet):not(.ngxsmk-fullscreen){position:fixed;inset:50% auto auto 50%;-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%);transform:translate(-50%,-50%);width:-webkit-fit-content;width:-moz-fit-content;width:100%;min-width:280px;max-width:min(100vw - 32px,500px);max-height:calc(100vh - 64px);max-height:calc(100dvh - 64px);z-index:2147483647;margin:0;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch;display:block;visibility:visible;opacity:1;will-change:transform,opacity}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open.ngxsmk-bottom-sheet:not(.ngxsmk-inline-container){position:fixed;inset:auto 0 0;-webkit-transform:translateY(0);-moz-transform:translateY(0);-ms-transform:translateY(0);-o-transform:translateY(0);transform:translateY(0);width:100%;max-width:100%;max-height:min(90dvh,90dvh - env(safe-area-inset-bottom,0px));padding-bottom:env(safe-area-inset-bottom,0px);border-radius:16px 16px 0 0;margin:0;z-index:2147483647;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch;display:block;visibility:visible;opacity:1;will-change:transform,opacity;-webkit-transition:transform .3s cubic-bezier(.4,0,.2,1),opacity .3s cubic-bezier(.4,0,.2,1);-moz-transition:transform .3s cubic-bezier(.4,0,.2,1),opacity .3s cubic-bezier(.4,0,.2,1);-o-transition:transform .3s cubic-bezier(.4,0,.2,1),opacity .3s cubic-bezier(.4,0,.2,1);transition:transform .3s cubic-bezier(.4,0,.2,1),opacity .3s cubic-bezier(.4,0,.2,1)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open.ngxsmk-fullscreen:not(.ngxsmk-inline-container){position:fixed;inset:0;width:100%;max-width:100%;max-height:100vh;max-height:100dvh;padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px);height:calc(100vh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px));height:calc(100dvh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px));border-radius:0;margin:0;z-index:2147483647;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch;display:block;visibility:visible;opacity:1;will-change:transform,opacity}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-has-multi-calendar){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:280px;max-width:min(100vw - 32px,500px);height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;max-height:calc(100vh - 64px)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:280px;max-width:min(100vw - 32px,800px);overflow-x:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-has-time-selection.ngxsmk-popover-open:not(.ngxsmk-inline-container){overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container){overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-has-time-selection.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-datepicker-container{overflow:visible;overflow-y:visible;max-height:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-datepicker-container{overflow:visible;overflow-y:visible;max-height:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-calendar-container{overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container)>*{pointer-events:auto}.ngxsmk-datepicker-wrapper.ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-popover-container:not(.ngxsmk-inline-container){display:block;visibility:visible;opacity:1;pointer-events:none}.ngxsmk-datepicker-wrapper.ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-popover-container:not(.ngxsmk-inline-container)>*{pointer-events:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-input-group{min-width:100%;width:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input{padding:10px 12px;min-height:44px;line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input::placeholder{line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input:not(:placeholder-shown){line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button{padding:8px;margin-right:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-button{display:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button svg{width:16px;height:16px;min-width:16px;min-height:16px;max-width:16px;max-height:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-cell{width:100%;height:auto;aspect-ratio:1/1;min-width:0;max-width:48px;min-height:0;max-height:48px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-number{width:85%;height:85%;min-width:30px;max-width:38px;min-height:30px;max-height:38px;font-size:var(--datepicker-font-size-base);display:flex;align-items:center;justify-content:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-name{font-size:11px;font-weight:700;padding:8px 0;width:100%;text-align:center;color:var(--datepicker-subtle-text-color);display:flex;align-items:center;justify-content:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container{padding:0;width:100%;min-width:0;max-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:var(--datepicker-spacing-xs) auto 0;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:var(--datepicker-spacing-xs) 0 0 0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid{gap:4px;padding:0;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;width:100%;max-width:100%;margin:0 auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid{width:100%;max-width:100%;margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header>*,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects>*,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons>*{margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header{display:flex;padding:4px;margin-bottom:var(--datepicker-spacing-sm);width:100%;max-width:100%;margin-left:0;margin-right:0;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;gap:2px;box-sizing:border-box;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects{-webkit-box-flex:1;-webkit-flex:1 1 0%;-ms-flex:1 1 0%;flex:1 1 0%;width:auto;gap:4px;min-width:0;display:flex}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects>*{-webkit-box-flex:1;-webkit-flex:1 1 0%;-ms-flex:1 1 0%;flex:1 1 0%;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons{-webkit-box-flex:0;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;display:flex;gap:2px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-container{height:36px;min-height:36px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{width:36px;height:36px;padding:6px;min-height:36px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button svg{width:20px;height:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-container,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects .ngxsmk-select-display{height:100%;min-height:100%;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-select-display{padding:0 6px;font-size:13px;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-arrow-icon{width:12px;height:12px;margin-left:2px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection{margin-top:var(--datepicker-spacing-sm);padding-top:var(--datepicker-spacing-sm);width:100%;max-width:100%;margin-left:auto;margin-right:auto;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;justify-content:center;gap:2px;overflow:visible;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding-left:0;padding-right:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection>*{-webkit-flex-shrink:1;-ms-flex-negative:1;flex-shrink:1;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select{height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-separator{font-size:20px;padding:0 4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-label{font-size:12px;font-weight:600;color:var(--datepicker-text-color);white-space:nowrap;margin-right:2px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer>*{margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer{margin-top:var(--datepicker-spacing-sm);padding-top:var(--datepicker-spacing-sm);width:100%;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;justify-content:center;gap:12px 8px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button{-webkit-box-flex:1;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;min-width:0;height:36px;min-height:36px;padding:6px 12px;font-size:14px;font-weight:600;border-radius:12px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container{width:100%;min-width:0;max-width:100%;overflow:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container ul{flex-wrap:wrap;justify-content:center;gap:8px 6px;overflow:visible;width:100%;min-width:0;max-width:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container li{flex-shrink:0;white-space:nowrap;min-width:fit-content}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container{width:100%;min-width:0;max-width:100%;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;position:relative;z-index:var(--datepicker-z-index-base)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container:has(.ngxsmk-calendar-container.ngxsmk-has-multi-calendar){height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}}@media(min-width:600px)and (max-width:767px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input{font-size:15px;padding:12px 16px;min-height:44px;line-height:1.5}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button{padding:10px;margin-right:8px;min-width:44px;min-height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button svg{width:20px;height:20px;min-width:20px;min-height:20px;max-width:20px;max-height:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:360px;max-width:min(90vw,550px);max-height:calc(100dvh - 40px);position:fixed;inset:50% auto auto 50%;-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%);transform:translate(-50%,-50%);border-radius:20px;padding:0;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch;z-index:2147483647}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-has-time-selection.ngxsmk-popover-open:not(.ngxsmk-inline-container),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container){overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-has-time-selection.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-datepicker-container,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-datepicker-container,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container) .ngxsmk-calendar-container{overflow:visible;overflow-y:visible;max-height:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container)>*{pointer-events:auto}.ngxsmk-datepicker-wrapper.ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-popover-container:not(.ngxsmk-inline-container),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){display:block;visibility:visible;opacity:1;pointer-events:none}.ngxsmk-datepicker-wrapper.ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-popover-container:not(.ngxsmk-inline-container)>*,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container)>*{pointer-events:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-popover-open):not(.ngxsmk-inline-container){display:none;visibility:hidden;opacity:0;pointer-events:none}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container{padding:0;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container{padding:20px;max-width:100%;width:100%;border-radius:20px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:16px;overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-has-time-selection .ngxsmk-calendar-container,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container.ngxsmk-time-only-mode{overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container{padding:16px;width:100%;max-width:100%;margin-bottom:0;border-radius:0;border-bottom:1px solid var(--datepicker-border-color);background:var(--datepicker-background);order:-1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container ul{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;overflow-x:visible;overflow-y:visible;padding-bottom:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container li{padding:12px 18px;line-height:1.4;min-height:44px;white-space:nowrap;text-align:center;border-radius:12px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header{width:100%;padding:16px 12px;margin-bottom:16px;gap:16px;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;border-bottom:1px solid var(--datepicker-border-color);-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects{gap:12px;font-size:15px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select{height:44px;min-height:44px;font-size:15px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons{gap:10px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{width:44px;height:44px;min-width:44px;min-height:44px;padding:12px;border-radius:12px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button svg{width:22px;height:22px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:0;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid{gap:6px;width:100%;max-width:100%;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-name{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-size:13px;font-weight:600;padding:12px 0;text-align:center;color:var(--datepicker-subtle-text-color)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-cell{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;aspect-ratio:1;min-height:44px;max-height:none;margin:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-number{width:100%;height:100%;min-width:0;max-width:100%;min-height:44px;max-height:none;font-size:15px;font-weight:500;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection{gap:12px;padding:20px 12px;margin-top:16px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;overflow-x:visible;overflow-y:hidden}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-label{font-size:15px;font-weight:500;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select{--custom-select-width: 80px !important;min-width:80px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-separator{font-size:22px;font-weight:600;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;padding:0 8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer{gap:12px;padding:20px 12px 16px;margin-top:16px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:end;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button{padding:14px 24px;font-size:15px;font-weight:500;min-height:44px;-webkit-box-flex:0;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;min-width:140px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container{width:100%;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;gap:24px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-auto{flex-direction:row;flex-wrap:wrap;justify-content:center;gap:24px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-vertical{flex-direction:column;gap:24px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal{flex-direction:row;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;gap:20px;padding:0 12px;scroll-snap-type:x mandatory;flex-wrap:nowrap}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{scroll-snap-align:start;min-width:320px;max-width:340px;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container:not(.ngxsmk-calendar-vertical) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{min-width:calc(50% - 12px);max-width:calc(50% - 12px);width:calc(50% - 12px);flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month-header{font-size:16px;font-weight:600;padding:16px 12px;margin-bottom:16px;text-align:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){padding:20px;max-width:100%;width:100%;overflow-x:visible;overflow-y:visible;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal){max-width:min(90vw,800px);overflow-x:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-grid,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-grid{gap:12px;padding:16px 12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-cell,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-cell{min-height:44px;padding:14px 18px;font-size:15px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-view{padding:16px 12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-header{padding:16px 12px;margin-bottom:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-controls{gap:14px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-out,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-in{min-width:44px;min-height:44px;padding:12px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-range{font-size:15px;padding:14px 18px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-container{padding:16px 12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-month{min-height:80px;padding:14px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-view{padding:16px 12px;gap:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-header{padding:16px 12px;gap:14px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-label{font-size:15px;font-weight:500}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-value{font-size:18px;font-weight:600}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-container{padding:16px 12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider{width:100%;height:44px}}@media(min-width:1024px){.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){position:absolute!important;top:calc(100% + 8px)!important;left:0!important;transform:none!important;-webkit-transform:none!important;-moz-transform:none!important;-ms-transform:none!important;-o-transform:none!important;width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:280px!important;max-width:min(90vw,500px)!important;max-height:none!important;z-index:2147483647!important;margin:0!important;overflow:visible!important;overflow-y:visible!important;-webkit-overflow-scrolling:auto!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-has-multi-calendar),.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:auto!important;max-width:min(95vw,1400px)!important}.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal),.ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal){width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:auto!important;max-width:min(95vw,1400px)!important}.ngxsmk-datepicker-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content;max-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.ngxsmk-datepicker-wrapper .ngxsmk-calendar-container{padding:var(--datepicker-spacing-md);width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content;max-width:100%;margin:0 auto}.ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:auto;max-width:100%;margin:0;padding:var(--datepicker-spacing-md, 16px);box-sizing:border-box}.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal,.ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal){width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:auto!important;max-width:100%!important}.ngxsmk-days-grid-wrapper{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;margin:var(--datepicker-spacing-sm) auto 0}.ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:var(--datepicker-spacing-sm) 0 0 0}.ngxsmk-days-grid{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;margin:0 auto}.ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid{width:100%;max-width:100%;margin:0}.ngxsmk-ranges-container{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content;padding:var(--datepicker-spacing-lg);border-right:none;border-bottom:1px solid var(--datepicker-border-color);border-radius:var(--datepicker-radius-lg) var(--datepicker-radius-lg) 0 0;background:var(--datepicker-hover-background)}.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode .ngxsmk-ranges-container{width:100%!important;max-width:100%!important;min-width:0!important}.ngxsmk-ranges-container ul{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:start;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;gap:var(--datepicker-spacing-xs);width:100%;min-width:100%;max-width:100%;overflow:visible}.ngxsmk-ranges-container li{padding:var(--datepicker-spacing-sm) var(--datepicker-spacing-md);margin-bottom:0;border:none;font-size:var(--datepicker-font-size-sm);width:auto;text-align:center;-webkit-box-flex:0;-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto}.ngxsmk-day-cell{width:40px;height:40px;min-width:40px;max-width:40px;min-height:40px;max-height:40px}.ngxsmk-day-number{width:36px;height:36px;min-width:36px;max-width:36px;min-height:36px;max-height:36px;font-size:var(--datepicker-font-size-base)}.ngxsmk-header{width:100%;margin-left:auto;margin-right:auto}.ngxsmk-datepicker-container .ngxsmk-calendar-container{padding-left:var(--datepicker-spacing-sm);padding-right:var(--datepicker-spacing-sm)}.ngxsmk-time-selection{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;margin-left:auto;margin-right:auto}.ngxsmk-day-name{width:auto!important;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content}}@media(min-width:1024px){.ngxsmk-popover-container:not(.ngxsmk-inline-container){width:auto;min-width:600px;max-width:800px}.ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:auto!important;max-width:min(95vw,1400px)!important}.ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal){width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:auto!important;max-width:min(95vw,1400px)!important}.ngxsmk-datepicker-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;width:100%;min-width:0;max-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.ngxsmk-datepicker-container:has(.ngxsmk-calendar-container.ngxsmk-has-multi-calendar){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;min-width:0}.ngxsmk-datepicker-wrapper .ngxsmk-calendar-container{padding:var(--datepicker-spacing-2xl);width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content}.ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:auto;max-width:100%;padding:var(--datepicker-spacing-md, 16px);box-sizing:border-box}.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal,.ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal){width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important;min-width:auto!important;max-width:100%!important}.ngxsmk-ranges-container{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content;padding:var(--datepicker-spacing-xl);border-right:1px solid var(--datepicker-border-color);-webkit-border-radius:var(--datepicker-radius-lg) 0 0 var(--datepicker-radius-lg);-moz-border-radius:var(--datepicker-radius-lg) 0 0 var(--datepicker-radius-lg);border-radius:var(--datepicker-radius-lg) 0 0 var(--datepicker-radius-lg);background:var(--datepicker-hover-background)}.ngxsmk-datepicker-wrapper.ngxsmk-inline-mode .ngxsmk-ranges-container{width:100%!important;max-width:100%!important;min-width:0!important;border-right:none!important;border-bottom:1px solid var(--datepicker-border-color)!important;border-radius:var(--datepicker-radius-lg) var(--datepicker-radius-lg) 0 0!important}.ngxsmk-ranges-container ul{-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:start;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;gap:var(--datepicker-spacing-xs);width:100%;min-width:100%;max-width:100%;overflow:visible}.ngxsmk-ranges-container li{padding:var(--datepicker-spacing-md) var(--datepicker-spacing-lg);margin-bottom:0;border:none;font-size:var(--datepicker-font-size-base);width:100%;text-align:left}.ngxsmk-day-cell{width:44px;height:44px;min-width:44px;max-width:44px;min-height:44px;max-height:44px}.ngxsmk-day-number{width:40px;height:40px;min-width:40px;max-width:40px;min-height:40px;max-height:40px;font-size:var(--datepicker-font-size-base)}}@media(hover:none)and (pointer:coarse){.ngxsmk-nav-button:hover:not(:disabled){background-color:transparent;transform:none}.ngxsmk-day-cell:not(.disabled):not(.empty):hover .ngxsmk-day-number{background-color:transparent;color:var(--datepicker-text-color);transform:none}.ngxsmk-clear-button:hover:not(:disabled){color:var(--datepicker-subtle-text-color);background:transparent}.ngxsmk-ranges-container li:hover:not(.disabled){background-color:var(--datepicker-background);color:var(--datepicker-text-color);border-color:var(--datepicker-border-color);transform:none;box-shadow:none}.ngxsmk-close-button:hover:not(:disabled){transform:none;box-shadow:none}}@media print{.ngxsmk-datepicker-wrapper{display:none}}.ngxsmk-day-cell:focus-visible{outline:none}.ngxsmk-day-cell:focus-visible .ngxsmk-day-number{outline:2px solid var(--datepicker-primary-color);outline-offset:2px;background-color:var(--datepicker-hover-background)}.ngxsmk-ranges-container::-webkit-scrollbar{width:6px}.ngxsmk-ranges-container::-webkit-scrollbar-track{background:var(--datepicker-hover-background);border-radius:var(--datepicker-radius-sm)}.ngxsmk-ranges-container::-webkit-scrollbar-thumb{background:var(--datepicker-subtle-text-color);border-radius:var(--datepicker-radius-sm)}.ngxsmk-ranges-container::-webkit-scrollbar-thumb:hover{background:var(--datepicker-text-color)}.ngxsmk-year-grid-container,.ngxsmk-decade-grid-container{overflow-y:auto;overflow-x:hidden;position:relative;will-change:transform;scroll-behavior:smooth}.ngxsmk-year-grid-container::-webkit-scrollbar,.ngxsmk-decade-grid-container::-webkit-scrollbar{width:8px}.ngxsmk-year-grid-container::-webkit-scrollbar-track,.ngxsmk-decade-grid-container::-webkit-scrollbar-track{background:var(--datepicker-bg-color)}.ngxsmk-year-grid-container::-webkit-scrollbar-thumb,.ngxsmk-decade-grid-container::-webkit-scrollbar-thumb{background:var(--datepicker-subtle-text-color);border-radius:var(--datepicker-radius-sm)}.ngxsmk-year-grid-container::-webkit-scrollbar-thumb:hover,.ngxsmk-decade-grid-container::-webkit-scrollbar-thumb:hover{background:var(--datepicker-text-color)}.ngxsmk-year-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--datepicker-spacing-sm);padding:var(--datepicker-spacing-md)}.ngxsmk-decade-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--datepicker-spacing-sm);padding:var(--datepicker-spacing-md)}.ngxsmk-year-cell,.ngxsmk-decade-cell{padding:var(--datepicker-spacing-md);border:1px solid var(--datepicker-border-color);border-radius:var(--datepicker-border-radius);background:var(--datepicker-background);color:var(--datepicker-text-color);font-size:var(--datepicker-font-size-base);font-weight:500;cursor:pointer;transition:var(--datepicker-transition);text-align:center}.ngxsmk-decade-cell{padding:var(--datepicker-spacing-lg)}.ngxsmk-year-cell:hover:not(:disabled),.ngxsmk-decade-cell:hover:not(:disabled){background:var(--datepicker-hover-background);border-color:var(--datepicker-primary-color)}.ngxsmk-year-cell.selected,.ngxsmk-decade-cell.selected{background:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);border-color:var(--datepicker-primary-color)}.ngxsmk-year-cell.today{border-color:var(--datepicker-primary-color);font-weight:600}.ngxsmk-year-cell:disabled,.ngxsmk-decade-cell:disabled{opacity:.5;cursor:not-allowed}.ngxsmk-year-display,.ngxsmk-decade-display{flex:1;display:flex;align-items:center;justify-content:center;font-size:var(--datepicker-font-size-base);font-weight:600;color:var(--datepicker-text-color)}.ngxsmk-view-toggle{background:transparent;border:none;color:var(--datepicker-text-color);font-size:var(--datepicker-font-size-base);font-weight:600;cursor:pointer;padding:var(--datepicker-spacing-sm) var(--datepicker-spacing-md);border-radius:var(--datepicker-border-radius);transition:var(--datepicker-transition)}.ngxsmk-view-toggle:hover:not(:disabled){background:var(--datepicker-hover-background)}.ngxsmk-view-toggle:disabled{opacity:.5;cursor:not-allowed}.ngxsmk-timeline-view{padding:var(--datepicker-spacing-md)}.ngxsmk-timeline-header{margin-bottom:var(--datepicker-spacing-md)}.ngxsmk-timeline-controls{display:flex;align-items:center;justify-content:space-between;gap:var(--datepicker-spacing-md)}.ngxsmk-timeline-zoom-in,.ngxsmk-timeline-zoom-out{width:32px;height:32px;border:1px solid var(--datepicker-border-color);border-radius:var(--datepicker-border-radius);background:var(--datepicker-background);color:var(--datepicker-text-color);font-size:var(--datepicker-font-size-lg);font-weight:600;cursor:pointer;transition:var(--datepicker-transition);display:flex;align-items:center;justify-content:center}.ngxsmk-timeline-zoom-in:hover:not(:disabled),.ngxsmk-timeline-zoom-out:hover:not(:disabled){background:var(--datepicker-hover-background);border-color:var(--datepicker-primary-color)}.ngxsmk-timeline-zoom-in:disabled,.ngxsmk-timeline-zoom-out:disabled{opacity:.5;cursor:not-allowed}.ngxsmk-timeline-range{font-size:var(--datepicker-font-size-sm);color:var(--datepicker-subtle-text-color);font-weight:500}.ngxsmk-timeline-container{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;overscroll-behavior:contain}.ngxsmk-timeline-track{display:flex;gap:var(--datepicker-spacing-xs);min-width:max-content;padding:var(--datepicker-spacing-sm) 0}.ngxsmk-timeline-month{min-width:80px;padding:var(--datepicker-spacing-md);border:1px solid var(--datepicker-border-color);border-radius:var(--datepicker-border-radius);background:var(--datepicker-background);color:var(--datepicker-text-color);cursor:pointer;transition:var(--datepicker-transition);text-align:center;display:flex;flex-direction:column;gap:var(--datepicker-spacing-xs)}.ngxsmk-timeline-month:hover:not(.selected){background:var(--datepicker-hover-background);border-color:var(--datepicker-primary-color)}.ngxsmk-timeline-month.selected{background:var(--datepicker-primary-color);color:var(--datepicker-primary-contrast);border-color:var(--datepicker-primary-color)}.ngxsmk-timeline-month-label{font-size:var(--datepicker-font-size-sm);font-weight:600;text-transform:uppercase}.ngxsmk-timeline-month-year{font-size:var(--datepicker-font-size-xs);opacity:.8}.ngxsmk-time-slider-view{padding:var(--datepicker-spacing-lg)}.ngxsmk-time-slider-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--datepicker-spacing-sm)}.ngxsmk-time-slider-label{font-size:var(--datepicker-font-size-sm);font-weight:600;color:var(--datepicker-text-color)}.ngxsmk-time-slider-value{font-size:var(--datepicker-font-size-base);font-weight:600;color:var(--datepicker-primary-color);font-variant-numeric:tabular-nums}.ngxsmk-time-slider-container{margin-bottom:var(--datepicker-spacing-lg)}.ngxsmk-time-slider{width:100%;height:8px;border-radius:4px;background:var(--datepicker-hover-background);outline:none;-webkit-appearance:none;appearance:none}.ngxsmk-time-slider::-webkit-slider-thumb{-webkit-appearance:none;width:20px;height:20px;border-radius:50%;background:var(--datepicker-primary-color);cursor:pointer;transition:var(--datepicker-transition)}.ngxsmk-time-slider::-webkit-slider-thumb:hover{transform:scale(1.1);box-shadow:var(--datepicker-shadow-md)}.ngxsmk-time-slider::-moz-range-thumb{width:20px;height:20px;border-radius:50%;background:var(--datepicker-primary-color);cursor:pointer;border:none;transition:var(--datepicker-transition)}.ngxsmk-time-slider::-moz-range-thumb:hover{transform:scale(1.1);box-shadow:var(--datepicker-shadow-md)}.ngxsmk-time-slider:disabled{opacity:.5;cursor:not-allowed}.ngxsmk-time-slider:disabled::-webkit-slider-thumb,.ngxsmk-time-slider:disabled::-moz-range-thumb{cursor:not-allowed}.ngxsmk-datepicker-wrapper[dir=rtl],.ngxsmk-datepicker-wrapper.ngxsmk-rtl{direction:rtl}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-input-group,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-input-group{flex-direction:row-reverse}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-clear-button,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-clear-button,.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-calendar-button,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-calendar-button{margin-right:0;margin-left:var(--datepicker-spacing-sm)}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-popover-container,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-popover-container{left:auto;right:0}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-header,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-header,.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-nav-buttons,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-nav-buttons{flex-direction:row-reverse}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-nav-button:first-child svg,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-nav-button:first-child svg{transform:scaleX(-1)}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-nav-button:last-child svg,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-nav-button:last-child svg{transform:scaleX(-1)}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-days-grid,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-days-grid{direction:rtl}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-time-selection,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-time-selection,.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-footer,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-footer{flex-direction:row-reverse}.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-ranges-container ul,.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-ranges-container ul{direction:rtl;text-align:right}@media(max-width:1023px){.ngxsmk-datepicker-wrapper[dir=rtl] .ngxsmk-popover-container:not(.ngxsmk-inline-container),.ngxsmk-datepicker-wrapper.ngxsmk-rtl .ngxsmk-popover-container:not(.ngxsmk-inline-container){left:auto;right:auto}}.ngxsmk-multi-calendar-container{display:flex;flex-direction:column;gap:var(--datepicker-spacing-xl, 32px);width:100%;align-items:stretch}.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar{flex-direction:row;flex-wrap:wrap;justify-content:flex-start;align-items:flex-start;width:100%;gap:var(--datepicker-spacing-xl, 32px)}.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal,.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal{flex-direction:row!important;flex-wrap:nowrap!important;width:100%!important;overflow-x:auto;-webkit-overflow-scrolling:touch;gap:var(--datepicker-spacing-xl, 32px)!important}.ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical,.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-vertical{flex-direction:column!important;flex-wrap:nowrap!important;width:100%!important;overflow-y:auto;-webkit-overflow-scrolling:touch;gap:var(--datepicker-spacing-xl, 32px)!important}.ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical .ngxsmk-calendar-month.ngxsmk-calendar-month-multi,.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-vertical .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{width:100%!important;max-width:100%!important;min-width:100%!important;margin:0!important}.ngxsmk-multi-calendar-container.ngxsmk-calendar-auto,.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-auto{flex-direction:row;flex-wrap:wrap;width:100%}.ngxsmk-calendar-month{width:100%;flex-shrink:0;display:flex;flex-direction:column}.ngxsmk-calendar-month.ngxsmk-calendar-month-multi{flex:0 0 auto;min-width:280px;width:auto;display:flex;flex-direction:column;margin:0;padding:0;box-sizing:border-box}.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi,.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{flex:0 0 auto!important;margin:0!important;box-sizing:border-box!important}.ngxsmk-calendar-month .ngxsmk-days-grid-wrapper,.ngxsmk-calendar-month .ngxsmk-days-grid{width:100%;max-width:100%}.ngxsmk-calendar-month-header{padding:var(--datepicker-spacing-sm, 8px) var(--datepicker-spacing-md, 12px);text-align:center;font-weight:600;font-size:var(--datepicker-font-size-base, 14px);color:var(--datepicker-text-color, #1f2937);border-bottom:1px solid var(--datepicker-border-color, #e5e7eb);margin-bottom:var(--datepicker-spacing-sm, 8px)}.ngxsmk-calendar-month-title{display:block}@media(min-width:768px)and (max-width:1023px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input{font-size:15px;padding:12px 16px;min-height:44px;line-height:1.5}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button{padding:10px;margin-right:8px;min-width:44px;min-height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button svg{width:20px;height:20px;min-width:20px;min-height:20px;max-width:20px;max-height:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:400px;max-width:min(90vw,600px);max-height:calc(100dvh - 40px);position:fixed;inset:50% auto auto 50%;-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%);transform:translate(-50%,-50%);padding:0;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch;z-index:2147483647}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-has-multi-calendar){min-width:400px;max-width:min(95vw,900px)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal){min-width:400px;max-width:min(95vw,1000px);overflow-x:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-has-time-selection.ngxsmk-popover-open:not(.ngxsmk-inline-container),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-time-only-popover.ngxsmk-popover-open:not(.ngxsmk-inline-container){overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container{padding:0;width:100%;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container{padding:24px;max-width:100%;width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:20px;overflow:visible;overflow-y:visible;max-height:none;-webkit-overflow-scrolling:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container{padding:20px;width:100%;max-width:100%;margin-bottom:0;border-radius:0;border-bottom:1px solid var(--datepicker-border-color);background:var(--datepicker-background);order:-1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container ul{display:grid;gap:12px;overflow-x:visible;overflow-y:visible;padding-bottom:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container li{padding:14px 20px;line-height:1.4;min-height:44px;white-space:nowrap;text-align:center;border-radius:12px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header{padding:20px 16px;margin-bottom:20px;gap:20px;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;border-bottom:1px solid var(--datepicker-border-color);-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects{gap:14px;font-size:16px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select{height:44px;min-height:44px;font-size:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons{gap:12px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{width:44px;height:44px;min-width:44px;min-height:44px;padding:12px;border-radius:12px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button svg{width:24px;height:24px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:0;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid{gap:8px;width:100%;max-width:100%;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-name{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-weight:600;padding:14px 0;text-align:center;color:var(--datepicker-subtle-text-color)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-cell{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;aspect-ratio:1;min-height:48px;max-height:none;margin:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-number{width:100%;height:100%;min-width:0;max-width:100%;min-height:48px;max-height:none;font-size:16px;font-weight:500;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection{gap:14px;padding:24px 16px;margin-top:20px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;overflow-x:visible;overflow-y:hidden}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-label{font-size:16px;font-weight:500;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select{--custom-select-width: 90px !important;min-width:90px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-separator{font-size:24px;font-weight:600;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;padding:0 10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer{gap:14px;padding:24px 16px 20px;margin-top:20px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:end;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button{padding:16px 28px;font-size:16px;font-weight:500;min-height:44px;-webkit-box-flex:0;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;min-width:160px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container{width:100%;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;gap:28px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-auto{flex-direction:row;flex-wrap:wrap;gap:28px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-vertical{flex-direction:column;gap:28px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal{flex-direction:row;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;gap:24px;padding:0 16px;scroll-snap-type:x mandatory;flex-wrap:nowrap}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{scroll-snap-align:start;min-width:360px;max-width:380px;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container:not(.ngxsmk-calendar-vertical) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{min-width:calc(50% - 14px);max-width:calc(50% - 14px);width:calc(50% - 14px);flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi .ngxsmk-days-grid{width:100%;max-width:100%;margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month-header{font-size:17px;font-weight:600;padding:18px 16px;margin-bottom:18px;text-align:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal){max-width:min(95vw,1000px);overflow-x:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){padding:24px;max-width:100%;width:100%;overflow-x:visible;overflow-y:visible;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}.ngxsmk-datepicker-wrapper .ngxsmk-multi-calendar-container{height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}}@media(min-width:768px)and (max-width:1023px)and (orientation:landscape){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){max-width:min(95vw,800px)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-auto{flex-direction:row;flex-wrap:nowrap}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{min-width:calc(33.333% - 19px);max-width:calc(33.333% - 19px);width:calc(33.333% - 19px)}}@media(min-width:768px)and (max-width:1023px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-grid,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-grid{gap:14px;padding:20px 16px;grid-template-columns:repeat(5,1fr)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-cell,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-cell{min-height:44px;padding:16px 20px;font-size:16px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-view{padding:20px 16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-header{padding:20px 16px;margin-bottom:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-controls{gap:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-out,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-in{min-width:44px;min-height:44px;padding:12px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-range{font-size:16px;padding:16px 20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-container{padding:20px 16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-month{min-height:90px;padding:16px;border-radius:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-view{padding:20px 16px;gap:24px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-header{padding:20px 16px;gap:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-label{font-size:16px;font-weight:500}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-value{font-size:20px;font-weight:600}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-container{padding:20px 16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider{width:100%;height:44px}}@media(min-width:769px)and (max-width:1024px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{min-width:260px;max-width:300px}}@media(min-width:480px)and (max-width:599px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input{padding:10px 14px;min-height:44px;line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input::placeholder{line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input:not(:placeholder-shown){line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button{padding:10px;margin-right:8px;min-width:44px;min-height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button svg{width:18px;height:18px;min-width:18px;min-height:18px;max-width:18px;max-height:18px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:320px;max-width:calc(100vw - 24px);max-height:calc(100dvh - 40px);position:fixed;inset:50% auto auto 50%;-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%);transform:translate(-50%,-50%);border-radius:16px;padding:0;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container{padding:0;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container{padding:16px;max-width:100%;width:100%;border-radius:16px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container{padding:12px;width:100%;max-width:100%;margin-bottom:0;border-radius:0;border-bottom:1px solid var(--datepicker-border-color);background:var(--datepicker-background);order:-1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container ul{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;overflow-x:visible;overflow-y:visible;padding-bottom:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container li{padding:12px 16px;font-size:13px;line-height:1.4;min-height:44px;white-space:nowrap;text-align:center;border-radius:10px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header{align-items:center;padding:12px 8px;margin-bottom:12px;gap:12px;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;border-bottom:1px solid var(--datepicker-border-color);-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects{gap:10px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select{height:44px;min-height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons{gap:8px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{width:44px;height:44px;min-width:44px;min-height:44px;padding:10px;border-radius:10px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button svg{width:20px;height:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:0;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid{gap:5px;width:100%;max-width:100%;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-name{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-size:12px;font-weight:600;padding:10px 0;text-align:center;color:var(--datepicker-subtle-text-color)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-cell{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;aspect-ratio:1;min-height:44px;max-height:none;margin:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-number{width:100%;height:100%;min-width:0;max-width:100%;min-height:44px;max-height:none;font-weight:500;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection{gap:10px;align-items:baseline;padding:16px 8px;margin-top:12px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;overflow-x:visible;overflow-y:hidden}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-label{font-weight:500;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select{--custom-select-width: 75px !important;min-width:75px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-separator{font-size:20px;font-weight:600;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;padding:0 6px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer{gap:12px;padding:16px 8px 12px;margin-top:12px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:end;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button{padding:12px 20px;font-weight:500;min-height:44px;-webkit-box-flex:0;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;min-width:120px;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container{width:100%;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-vertical{flex-direction:column;gap:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal{flex-direction:row;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;gap:16px;padding:0 8px;scroll-snap-type:x mandatory}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{scroll-snap-align:start;min-width:300px;max-width:320px;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{min-width:100%;max-width:100%;width:100%;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month-header{font-size:15px;font-weight:600;padding:12px 8px;margin-bottom:12px;text-align:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){padding:16px;max-width:100%;width:100%;overflow-x:visible;overflow-y:visible;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal){max-width:100vw;overflow-x:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-grid,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-grid{gap:10px;padding:12px 8px;grid-template-columns:repeat(3,1fr)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-cell,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-cell{min-height:44px;padding:12px 16px;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-view{padding:12px 8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-header{padding:12px 8px;margin-bottom:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-controls{gap:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-out,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-in{min-width:44px;min-height:44px;padding:10px;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-range{padding:12px 16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-container{padding:12px 8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-month{min-height:70px;padding:12px;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-view{padding:12px 8px;gap:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-header{padding:12px 8px;gap:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-label{font-weight:500}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-value{font-size:16px;font-weight:600}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-container{padding:12px 8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider{width:100%;height:44px}}@media(min-width:375px)and (max-width:479px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input{padding:10px 12px;min-height:44px;line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input::placeholder{line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input:not(:placeholder-shown){line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button{padding:8px;margin-right:8px;min-width:44px;min-height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button svg{width:18px;height:18px;min-width:18px;min-height:18px;max-width:18px;max-height:18px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;min-width:300px;max-width:calc(100vw - 24px);max-height:calc(100dvh - 48px);position:fixed;inset:50% auto auto 50%;-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%);transform:translate(-50%,-50%);border-radius:16px;padding:0;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container{padding:0;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container{padding:12px;max-width:100%;width:100%;border-radius:16px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container{padding:10px;width:100%;max-width:100%;margin-bottom:0;border-radius:0;border-bottom:1px solid var(--datepicker-border-color);background:var(--datepicker-background);order:-1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container ul{flex-wrap:wrap;justify-content:flex-start;gap:8px;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;padding-bottom:4px;scroll-snap-type:x mandatory}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container li{padding:10px 16px;font-size:13px;line-height:1.4;flex-shrink:0;white-space:nowrap;min-height:44px;scroll-snap-align:start;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header{padding:10px 6px;margin-bottom:10px;gap:10px;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;border-bottom:1px solid var(--datepicker-border-color);-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects{gap:8px;font-size:13px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select{height:44px;min-height:44px;font-size:13px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons{gap:6px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{width:44px;height:44px;min-width:44px;min-height:44px;padding:10px;border-radius:10px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button svg{width:20px;height:20px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:0;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid{gap:4px;width:100%;max-width:100%;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-name{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-size:11px;font-weight:600;padding:8px 0;text-align:center;color:var(--datepicker-subtle-text-color)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-cell{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;aspect-ratio:1;min-height:44px;max-height:none;margin:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-number{width:100%;height:100%;min-width:0;max-width:100%;min-height:44px;max-height:none;font-weight:500;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection{gap:8px;margin-top:10px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-label{font-size:13px;font-weight:500;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select{--custom-select-width: 70px !important;min-width:70px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-separator{font-size:18px;font-weight:600;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;padding:0 4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer{gap:10px;padding:14px 6px 10px;margin-top:10px;border-top:1px solid var(--datepicker-border-color);-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:stretch;-webkit-justify-content:stretch;-ms-flex-pack:stretch;justify-content:stretch}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button{padding:12px 18px;font-weight:500;min-height:44px;-webkit-box-flex:1;-webkit-flex:1 1 calc(50% - 5px);-ms-flex:1 1 calc(50% - 5px);flex:1 1 calc(50% - 5px);min-width:0;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container{width:100%;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:18px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-vertical{flex-direction:column;gap:18px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal{flex-direction:row;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;gap:14px;padding:0 6px;scroll-snap-type:x mandatory}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{scroll-snap-align:start;min-width:280px;max-width:300px;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{min-width:100%;max-width:100%;width:100%;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month-header{font-weight:600;padding:10px 6px;margin-bottom:10px;text-align:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){padding:12px;max-width:100%;width:100%;overflow-x:visible;overflow-y:visible;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal){max-width:100vw;overflow-x:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-grid,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-grid{gap:8px;padding:10px 6px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-cell,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-cell{min-height:44px;padding:12px 16px;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-view{padding:10px 6px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-header{padding:10px 6px;margin-bottom:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-controls{gap:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-out,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-in{min-width:44px;min-height:44px;padding:10px;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-range{font-size:13px;padding:10px 14px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-container{padding:10px 6px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-month{min-height:64px;padding:10px;border-radius:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-view{padding:10px 6px;gap:14px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-header{padding:10px 6px;gap:10px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-label{font-size:13px;font-weight:500}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-value{font-size:15px;font-weight:600}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-container{padding:10px 6px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider{width:100%;height:44px}}@media(max-width:374px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-input-group{min-width:100%;width:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input{font-size:13px;padding:8px 10px;min-height:44px;line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input::placeholder{font-size:13px;line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-display-input:not(:placeholder-shown){font-size:13px;line-height:1.5;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button{padding:8px;margin-right:6px;min-width:44px;min-height:44px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button svg{width:16px;height:16px;min-width:16px;min-height:16px;max-width:16px;max-height:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container.ngxsmk-popover-open:not(.ngxsmk-inline-container){width:calc(100% - 20px);min-width:280px;max-width:100%;max-height:calc(100dvh - 20px);position:fixed;inset:0;margin:auto;-webkit-transform:none;-moz-transform:none;-ms-transform:none;-o-transform:none;transform:none;border-radius:12px;padding:0;overflow-y:auto;overflow-x:visible;-webkit-overflow-scrolling:touch}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-container{padding:0;width:100%;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;overflow:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container{padding:6px 4px;max-width:100%;width:100%;border-radius:0;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:2px;overflow:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container{padding:8px;width:100%;max-width:100%;margin-bottom:0;border-radius:0;border-bottom:1px solid var(--datepicker-border-color);background:var(--datepicker-background);order:-1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container ul{flex-wrap:nowrap;justify-content:flex-start;gap:6px;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;padding-bottom:4px;scroll-snap-type:x mandatory;-webkit-box-pack:start;-webkit-justify-content:flex-start;-ms-flex-pack:start}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-ranges-container li{padding:10px 14px;font-size:12px;line-height:1.4;flex-shrink:0;white-space:nowrap;min-height:44px;scroll-snap-align:start;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-header{padding:4px 6px 10px;margin-bottom:6px;gap:4px;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;border-bottom:1px solid var(--datepicker-border-color);-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;align-items:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects{gap:6px;font-size:12px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;min-width:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-month-year-selects ngxsmk-custom-select{height:40px;min-height:40px;font-size:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-buttons{gap:4px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button{width:40px;height:40px;min-width:40px;min-height:40px;padding:8px;border-radius:8px;box-sizing:border-box}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-nav-button svg{width:18px;height:18px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid-wrapper{width:100%;max-width:100%;margin:0;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-days-grid{gap:3px;width:100%;max-width:100%;padding:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-name{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-size:10px;font-weight:600;padding:6px 0;text-align:center;color:var(--datepicker-subtle-text-color)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-cell{width:auto;min-width:0;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;aspect-ratio:1;min-height:40px;max-height:none;margin:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-day-number{width:100%;height:100%;min-width:0;max-width:100%;min-height:40px;max-height:none;font-size:13px;font-weight:500;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection{display:flex;gap:8px;padding:12px 4px;margin-top:8px;border-top:1px solid var(--datepicker-border-color);flex-wrap:nowrap;justify-content:center;align-items:center;overflow:visible;position:relative;z-index:100}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-label{font-size:13px;font-weight:600;flex-shrink:0;margin:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select{--custom-select-width: 70px;min-width:70px;flex-shrink:0;position:relative;z-index:auto}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection:has(ngxsmk-custom-select[data-open=true]){z-index:1000}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select[data-open=true]{z-index:10000}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select[data-open=true] .ngxsmk-options-panel{z-index:10001;top:auto;bottom:calc(100% + 4px);transform-origin:bottom center;animation:fadeInUp .12s cubic-bezier(.4,0,.2,1)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-separator{font-size:18px;font-weight:700;flex-shrink:0;padding:0 4px;line-height:1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer{gap:8px;padding:12px 4px 8px;margin-top:0;border-top:1px solid var(--datepicker-border-color);flex-wrap:nowrap;justify-content:stretch;position:relative;z-index:1}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-clear-button-footer,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-close-button{padding:12px 16px;font-size:13px;font-weight:500;min-height:44px;-webkit-box-flex:1;-webkit-flex:1 1 calc(50% - 4px);-ms-flex:1 1 calc(50% - 4px);flex:1 1 calc(50% - 4px);min-width:0;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container{width:100%;max-width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-auto,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-vertical{flex-direction:column;gap:16px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal{flex-direction:row;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;gap:12px;padding:0 4px;scroll-snap-type:x mandatory}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-multi-calendar.ngxsmk-calendar-horizontal .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{scroll-snap-align:start;min-width:260px;max-width:280px;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month.ngxsmk-calendar-month-multi{min-width:100%;max-width:100%;width:100%;flex-shrink:0}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-month-header{font-size:13px;font-weight:600;padding:8px 4px;margin-bottom:8px;text-align:center}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container.ngxsmk-has-multi-calendar,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-calendar-container:has(.ngxsmk-multi-calendar-container.ngxsmk-multi-calendar){padding:8px;max-width:100%;width:100%;overflow-x:visible;overflow-y:visible;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal),.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-popover-container:not(.ngxsmk-inline-container):has(.ngxsmk-calendar-container.ngxsmk-calendar-layout-horizontal){max-width:100vw;overflow-x:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-grid,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-grid{gap:6px;padding:8px 4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-year-cell,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-decade-cell{min-height:44px;padding:10px 12px;font-size:13px;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-view{padding:8px 4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-header{padding:8px 4px;margin-bottom:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-controls{gap:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-out,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-zoom-in{min-width:44px;min-height:44px;padding:8px;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-range{font-size:12px;padding:8px 12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-container{padding:8px 4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-timeline-month{min-height:60px;padding:8px;border-radius:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-view{padding:8px 4px;gap:12px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-header{padding:8px 4px;gap:8px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-label{font-size:12px;font-weight:500}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-value{font-weight:600}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider-container{padding:8px 4px}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-slider{width:100%;height:44px}}@media(max-width:991px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection{position:relative;z-index:100;overflow:visible}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection ngxsmk-custom-select[data-open=true] .ngxsmk-options-panel{z-index:10001;top:auto;bottom:calc(100% + 4px);transform-origin:bottom center;animation:fadeInUp .12s cubic-bezier(.4,0,.2,1)}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-time-selection:has(ngxsmk-custom-select[data-open=true]){z-index:1000}.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-footer{position:relative;z-index:1}}.ngxsmk-datepicker-inner-content{display:flex!important;flex-direction:row!important;width:100%;flex:1 1 auto}@media(max-width:1023px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-datepicker-inner-content{flex-direction:column}}.md3-theme .ngxsmk-day-number{border-radius:12px;transition:all .2s cubic-bezier(0,0,.2,1)}.md3-theme .ngxsmk-day-cell.selected .ngxsmk-day-number,.md3-theme .ngxsmk-day-cell.start-date .ngxsmk-day-number,.md3-theme .ngxsmk-day-cell.end-date .ngxsmk-day-number{border-radius:12px;box-shadow:0 4px 8px #0000001a;transform:translateY(-1px)}@keyframes fadeInUp{0%{opacity:0;transform:translate3d(0,4px,0)}to{opacity:1;transform:translateZ(0)}}@media(min-width:992px){.ngxsmk-datepicker-container{display:grid!important;grid-template-areas:\"header header\" \"ranges calendar\" \"time time\" \"footer footer\";grid-template-columns:auto auto;width:-webkit-fit-content!important;width:-moz-fit-content!important;width:fit-content!important}.ngxsmk-range-duration-header{grid-area:header}.ngxsmk-ranges-container{grid-area:ranges}.ngxsmk-calendar-container{grid-area:calendar}.ngxsmk-footer{grid-area:footer}ngxsmk-time-selection{grid-area:time}}.ngxsmk-multi-calendar-container.ngxsmk-sync-scroll-enabled{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:var(--datepicker-calendar-gap, 16px);align-items:start}.ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal.ngxsmk-sync-scroll-enabled{display:flex;flex-direction:row;flex-wrap:nowrap;gap:var(--datepicker-calendar-gap, 16px);align-items:flex-start;overflow-x:auto;overflow-y:hidden;scroll-behavior:smooth;position:relative}.ngxsmk-multi-calendar-container.ngxsmk-calendar-vertical.ngxsmk-sync-scroll-enabled{display:flex;flex-direction:column;gap:var(--datepicker-calendar-gap, 16px);align-items:stretch;overflow-y:auto;overflow-x:hidden;scroll-behavior:smooth}.ngxsmk-calendar-horizontal.ngxsmk-sync-scroll-enabled .ngxsmk-calendar-month{min-width:320px;width:100%;flex-basis:100%}.ngxsmk-multi-calendar.ngxsmk-sync-scroll-enabled{overflow:hidden}.ngxsmk-multi-calendar-container.ngxsmk-sync-scroll-enabled:focus-within{outline:2px solid var(--datepicker-focus-color, #4a90e2);outline-offset:2px;border-radius:4px}@media(max-width:768px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-multi-calendar-container.ngxsmk-calendar-horizontal.ngxsmk-sync-scroll-enabled{flex-direction:column;overflow-x:hidden;overflow-y:auto}.ngxsmk-calendar-horizontal.ngxsmk-sync-scroll-enabled .ngxsmk-calendar-month{min-width:100%;width:100%}}:root{--ngxsmk-space-xs: .25rem;--ngxsmk-space-sm: .5rem;--ngxsmk-space-md: .75rem;--ngxsmk-space-lg: 1rem;--ngxsmk-space-xl: 1.5rem;--ngxsmk-space-2xl: 2rem;--ngxsmk-touch-target: 44px;--ngxsmk-cell-size-desktop: 40px;--ngxsmk-font-size-xs: .625rem;--ngxsmk-font-size-sm: .75rem;--ngxsmk-font-size-base: 1rem;--ngxsmk-font-size-lg: 1.125rem;--ngxsmk-font-size-xl: 1.25rem;--ngxsmk-line-height: 1.5;--ngxsmk-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;--ngxsmk-color-primary: var(--ion-color-primary, #6d28d9);--ngxsmk-color-on-primary: var(--ion-color-primary-contrast, #ffffff);--ngxsmk-color-range-bg: var(--ion-color-primary-tint, #f5f3ff);--ngxsmk-color-surface: var(--ion-background-color, #ffffff);--ngxsmk-color-surface-hover: #f3f4f6;--ngxsmk-color-text-main: var(--ion-text-color, #1f2937);--ngxsmk-color-text-muted: var(--ion-text-color-step-400, #6b7280);--ngxsmk-color-border: var(--ion-item-border-color, var(--ion-border-color, #e5e7eb));--ngxsmk-radius-sm: 6px;--ngxsmk-radius-md: 8px;--ngxsmk-radius-lg: 12px;--ngxsmk-radius-xl: 16px;--ngxsmk-radius-popup: 16px;--ngxsmk-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);--ngxsmk-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .06);--ngxsmk-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--ngxsmk-transition-duration: .15s;--ngxsmk-transition-easing: cubic-bezier(.4, 0, .2, 1);--ngxsmk-transition: all var(--ngxsmk-transition-duration) var(--ngxsmk-transition-easing);--ngxsmk-z-backdrop: 2147483646;--ngxsmk-z-popover: 2147483647;--datepicker-primary-color: var(--ngxsmk-color-primary);--datepicker-primary-contrast: var(--ngxsmk-color-on-primary);--datepicker-range-background: var(--ngxsmk-color-range-bg);--datepicker-background: var(--ngxsmk-color-surface);--datepicker-hover-background: var(--ngxsmk-color-surface-hover);--datepicker-text-color: var(--ngxsmk-color-text-main);--datepicker-subtle-text-color: var(--ngxsmk-color-text-muted);--datepicker-border-color: var(--ngxsmk-color-border);--datepicker-radius-sm: var(--ngxsmk-radius-sm);--datepicker-radius-md: var(--ngxsmk-radius-md);--datepicker-radius-lg: var(--ngxsmk-radius-lg);--datepicker-border-radius: var(--ngxsmk-radius-lg);--datepicker-font-size-base: var(--ngxsmk-font-size-base);--datepicker-font-size-lg: var(--ngxsmk-font-size-lg);--datepicker-line-height: var(--ngxsmk-line-height);--datepicker-spacing-xs: var(--ngxsmk-space-xs);--datepicker-spacing-sm: var(--ngxsmk-space-sm);--datepicker-spacing-md: var(--ngxsmk-space-md);--datepicker-spacing-lg: var(--ngxsmk-space-lg);--datepicker-transition: var(--ngxsmk-transition)}ngxsmk-datepicker{display:block;width:100%;position:relative;font-family:var(--ngxsmk-font-family);user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;overflow:visible}ngxsmk-datepicker *,ngxsmk-datepicker *:before,ngxsmk-datepicker *:after{box-sizing:border-box;-webkit-tap-highlight-color:transparent}ngxsmk-datepicker:has(.ngxsmk-calendar-open){z-index:var(--ngxsmk-z-popover)!important}.ngxsmk-datepicker-wrapper{position:relative;width:100%;z-index:1;overflow:visible}.ngxsmk-datepicker-wrapper.ngxsmk-calendar-open{z-index:var(--ngxsmk-z-popover);position:relative;isolation:isolate}.ngxsmk-datepicker-container,.ngxsmk-calendar-container{border-radius:var(--ngxsmk-radius-lg)!important;overflow:hidden!important}.ngxsmk-datepicker-container .ngxsmk-calendar-container{border-radius:inherit!important}.ngxsmk-backdrop{display:none}@media(min-width:1025px){.ngxsmk-backdrop:not(.ngxsmk-inline-backdrop){display:block!important;position:fixed!important;inset:0;background:transparent!important;z-index:var(--ngxsmk-z-backdrop)!important;pointer-events:auto!important}.ngxsmk-backdrop:not(.ngxsmk-inline-backdrop).ngxsmk-backdrop-allow-modal-scroll{pointer-events:none!important}}@media(max-width:1024px){.ngxsmk-datepicker-wrapper.ngxsmk-calendar-open:not(.ngxsmk-inline-mode) .ngxsmk-backdrop,.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-backdrop:not(.ngxsmk-inline-backdrop){display:block;visibility:visible;opacity:1;position:fixed;inset:0;background:#0006;z-index:var(--ngxsmk-z-backdrop);pointer-events:auto;transition:opacity .3s ease-out}}@media(prefers-reduced-motion:reduce){ngxsmk-datepicker,.ngxsmk-datepicker,.ngxsmk-datepicker-panel,.ngxsmk-datepicker-overlay,.ngxsmk-datepicker-dialog,.ngxsmk-datepicker-dropdown,.ngxsmk-datepicker-container,.ngxsmk-presets-drawer{--ngxsmk-transition: none !important;--ngxsmk-transition-duration: .01ms !important}ngxsmk-datepicker *,ngxsmk-datepicker *:before,ngxsmk-datepicker *:after,.ngxsmk-datepicker-panel *,.ngxsmk-datepicker-panel *:before,.ngxsmk-datepicker-panel *:after,.ngxsmk-datepicker-overlay *,.ngxsmk-datepicker-dialog *,.ngxsmk-datepicker-dropdown *,.ngxsmk-presets-drawer *{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}ngxsmk-datepicker.dark-theme,.ngxsmk-popover-container.dark-theme,.ngxsmk-backdrop.dark-theme{--ngxsmk-color-range-bg: rgba(139, 92, 246, .15);--ngxsmk-color-surface: #1f2937;--ngxsmk-color-text-main: #f3f4f6;--ngxsmk-color-text-muted: #9ca3af;--ngxsmk-color-border: #374151;--ngxsmk-color-surface-hover: #374151;--ngxsmk-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .3);--ngxsmk-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .3), 0 2px 4px -1px rgba(0, 0, 0, .2);--ngxsmk-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .3), 0 4px 6px -2px rgba(0, 0, 0, .2)}ngxsmk-datepicker.glass-theme,.ngxsmk-popover-container.glass-theme{--ngxsmk-color-surface: rgba(255, 255, 255, .7);--ngxsmk-color-border: rgba(255, 255, 255, .3);--ngxsmk-shadow-lg: 0 8px 32px 0 rgba(31, 38, 135, .37);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--ngxsmk-color-border)}ngxsmk-datepicker.glass-theme.dark-theme,.ngxsmk-popover-container.glass-theme.dark-theme{--ngxsmk-color-surface: rgba(31, 41, 55, .7);--ngxsmk-color-border: rgba(255, 255, 255, .1)}ngxsmk-datepicker.md3-theme{--ngxsmk-color-primary: #6750a4;--ngxsmk-radius-lg: 28px;--ngxsmk-font-family: \"Roboto\", sans-serif;--ngxsmk-shadow-md: 0px 1px 3px 1px rgba(0, 0, 0, .15), 0px 1px 2px rgba(0, 0, 0, .3)}.ngxsmk-input-group{display:flex;align-items:center;position:relative;width:100%;border:1px solid var(--ngxsmk-color-border);border-radius:var(--ngxsmk-radius-md);background:var(--ngxsmk-color-surface);transition:var(--ngxsmk-transition);min-height:44px}.ngxsmk-input-group:focus-within{border-color:var(--ngxsmk-color-primary);box-shadow:var(--datepicker-shadow-focus)}.ngxsmk-input-group.disabled{opacity:.6;background:var(--ngxsmk-color-surface-hover);cursor:not-allowed}.ngxsmk-display-input{flex-grow:1;width:100%;padding:var(--ngxsmk-space-md) var(--ngxsmk-space-lg);font-size:var(--ngxsmk-font-size-base);line-height:var(--ngxsmk-line-height);color:var(--ngxsmk-color-text-main);background:transparent;border:none;outline:none;cursor:pointer;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.ngxsmk-display-input:disabled{cursor:not-allowed}.ngxsmk-calendar-button,.ngxsmk-clear-button{display:flex;align-items:center;justify-content:center;width:var(--ngxsmk-touch-target);height:var(--ngxsmk-touch-target);background:transparent;border:none;cursor:pointer;color:var(--ngxsmk-color-text-muted);transition:var(--ngxsmk-transition);border-radius:var(--ngxsmk-radius-md)}.ngxsmk-calendar-button svg,.ngxsmk-clear-button svg{width:20px;height:20px;fill:currentColor}@media(hover:hover){.ngxsmk-calendar-button:hover:not(:disabled),.ngxsmk-clear-button:hover:not(:disabled){color:var(--ngxsmk-color-primary);background:var(--ngxsmk-color-surface-hover)}}.ngxsmk-calendar-button:focus-visible,.ngxsmk-clear-button:focus-visible{outline:2px solid var(--ngxsmk-color-primary);outline-offset:-2px}.ngxsmk-calendar-button:disabled,.ngxsmk-clear-button:disabled{cursor:not-allowed;opacity:.5}.ngxsmk-calendar-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:var(--ngxsmk-space-xs);padding:0 var(--ngxsmk-space-md) var(--ngxsmk-space-md);width:100%}.ngxsmk-weekdays{display:grid;grid-template-columns:repeat(7,1fr);gap:var(--ngxsmk-space-xs);padding:var(--ngxsmk-space-md);border-bottom:1px solid var(--ngxsmk-color-border);margin-bottom:var(--ngxsmk-space-sm)}.ngxsmk-weekdays .ngxsmk-weekday{text-align:center;font-size:var(--ngxsmk-font-size-xs);font-weight:600;color:var(--ngxsmk-color-text-muted);text-transform:uppercase;letter-spacing:.05em}.ngxsmk-day-cell{min-width:var(--ngxsmk-touch-target);min-height:var(--ngxsmk-touch-target);width:100%;aspect-ratio:1/1;display:flex;align-items:center;justify-content:center}@media(min-width:1025px){.ngxsmk-day-cell{width:var(--ngxsmk-cell-size-desktop);height:var(--ngxsmk-cell-size-desktop);min-width:0;min-height:0}}.ngxsmk-month-grid,.ngxsmk-year-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--ngxsmk-space-sm);padding:var(--ngxsmk-space-md)}.ngxsmk-month-grid .ngxsmk-grid-cell,.ngxsmk-year-grid .ngxsmk-grid-cell{min-height:var(--ngxsmk-touch-target);padding:var(--ngxsmk-space-md) var(--ngxsmk-space-sm);display:flex;align-items:center;justify-content:center;border-radius:var(--ngxsmk-radius-sm);font-size:var(--ngxsmk-font-size-base);color:var(--ngxsmk-color-text-main);background:transparent;border:1px solid transparent;cursor:pointer;transition:var(--ngxsmk-transition)}@media(hover:hover){.ngxsmk-month-grid .ngxsmk-grid-cell:hover:not(.disabled),.ngxsmk-year-grid .ngxsmk-grid-cell:hover:not(.disabled){background:var(--ngxsmk-color-surface-hover);border-color:var(--ngxsmk-color-border)}}.ngxsmk-month-grid .ngxsmk-grid-cell.selected,.ngxsmk-year-grid .ngxsmk-grid-cell.selected{background:var(--datepicker-primary-color, var(--ngxsmk-color-primary));color:var(--datepicker-primary-contrast, var(--ngxsmk-color-on-primary));font-weight:600;border-color:var(--datepicker-primary-color, var(--ngxsmk-color-primary))}.ngxsmk-month-grid .ngxsmk-grid-cell.disabled,.ngxsmk-year-grid .ngxsmk-grid-cell.disabled{color:var(--ngxsmk-color-text-muted);opacity:.5;cursor:not-allowed}@media(min-width:768px){.ngxsmk-month-grid .ngxsmk-grid-cell,.ngxsmk-year-grid .ngxsmk-grid-cell{padding:var(--ngxsmk-space-lg) var(--ngxsmk-space-sm)}}.ngxsmk-year-grid{grid-template-columns:repeat(4,1fr)}.ngxsmk-header{display:flex;align-items:center;justify-content:space-between;padding:var(--ngxsmk-space-sm) var(--ngxsmk-space-md) var(--ngxsmk-space-xs);width:100%;gap:var(--ngxsmk-space-sm)}@media(min-width:1025px){.ngxsmk-header{padding:var(--ngxsmk-space-sm) var(--ngxsmk-space-md)}}.ngxsmk-month-year-selects{display:flex;align-items:center;gap:var(--ngxsmk-space-xs);flex:1;min-width:0}.ngxsmk-month-year-selects .month-select{flex:1.5}.ngxsmk-month-year-selects .year-select{flex:1}.ngxsmk-nav-buttons{display:flex;align-items:center;gap:var(--ngxsmk-space-xs)}ngxsmk-custom-select{position:relative;display:flex;flex:1;min-width:0;-webkit-user-select:none;user-select:none;box-sizing:border-box;z-index:1}ngxsmk-custom-select[data-open=true]{z-index:10000}.ngxsmk-select-container{cursor:pointer;position:relative;display:flex;width:100%;height:100%;min-height:100%;box-sizing:border-box;outline:none}.ngxsmk-select-container.is-open{z-index:10000}.ngxsmk-select-display{display:flex;align-items:center;justify-content:space-between;flex:1;width:100%;height:100%;min-height:var(--ngxsmk-touch-target);padding:0 var(--ngxsmk-space-md);background:var(--ngxsmk-color-surface);border:1px solid var(--ngxsmk-color-border);border-radius:var(--ngxsmk-radius-sm);font-size:var(--ngxsmk-font-size-base);font-family:inherit;color:var(--ngxsmk-color-text-main);cursor:pointer;transition:var(--ngxsmk-transition);appearance:none;-webkit-appearance:none;gap:var(--ngxsmk-space-sm)}.ngxsmk-select-display:focus-within{outline:none;border-color:var(--datepicker-primary-color, var(--ngxsmk-color-primary));box-shadow:var(--datepicker-shadow-focus)}.ngxsmk-select-display:disabled{background-color:var(--ngxsmk-color-surface-hover);cursor:not-allowed;opacity:.6}@media(hover:hover){.ngxsmk-select-display:hover:not(:disabled){background:var(--ngxsmk-color-surface-hover);border-color:var(--ngxsmk-color-text-muted)}}@media(min-width:1025px){.ngxsmk-select-display{min-height:36px;font-size:var(--ngxsmk-font-size-sm)}}.ngxsmk-arrow-icon{width:14px;height:14px;transition:transform var(--ngxsmk-transition-duration) ease;flex-shrink:0;color:var(--ngxsmk-color-text-muted)}.is-open .ngxsmk-arrow-icon{transform:rotate(180deg)}.ngxsmk-options-panel{position:absolute;top:calc(100% + 4px);left:0;width:100%;background:var(--ngxsmk-color-surface);border:1px solid var(--ngxsmk-color-border);border-radius:var(--ngxsmk-radius-md);box-shadow:var(--ngxsmk-shadow-lg);max-height:200px;overflow-y:auto;z-index:10001;scrollbar-width:none}.ngxsmk-options-panel::-webkit-scrollbar{display:none}.ngxsmk-options-panel ul{list-style:none;padding:var(--ngxsmk-space-xs);margin:0}.ngxsmk-options-panel li{padding:var(--ngxsmk-space-sm) var(--ngxsmk-space-md);border-radius:var(--ngxsmk-radius-sm);cursor:pointer;transition:background-color .12s ease;font-size:var(--ngxsmk-font-size-sm);margin:2px 0;color:var(--ngxsmk-color-text-main)!important;opacity:1!important;visibility:visible!important}.ngxsmk-options-panel li:hover:not(.selected){background-color:var(--ngxsmk-color-surface-hover)}.ngxsmk-options-panel li.selected{background-color:var(--datepicker-primary-color, var(--ngxsmk-color-primary));color:var(--datepicker-primary-contrast, var(--ngxsmk-color-on-primary))!important;font-weight:600}@media(max-width:768px){.ngxsmk-datepicker-wrapper:not(.ngxsmk-no-responsive) .ngxsmk-options-panel li{padding:var(--ngxsmk-space-md) var(--ngxsmk-space-lg);min-height:var(--ngxsmk-touch-target);display:flex;align-items:center}}.ngxsmk-header-title{font-size:var(--ngxsmk-font-size-lg);font-weight:600;color:var(--ngxsmk-color-text-main);text-align:center;flex-grow:1}.ngxsmk-nav-button{cursor:pointer;display:flex;align-items:center;justify-content:center;width:var(--ngxsmk-touch-target);height:var(--ngxsmk-touch-target);background:transparent;border:none;border-radius:var(--ngxsmk-radius-sm);color:var(--ngxsmk-color-text-main);transition:var(--ngxsmk-transition)}.ngxsmk-nav-button svg{width:20px;height:20px}.ngxsmk-nav-button:hover:not(:disabled){background:var(--ngxsmk-color-surface-hover)}.ngxsmk-nav-button:disabled{opacity:.3;cursor:not-allowed}@media(min-width:1025px){.ngxsmk-nav-button{width:32px;height:32px}}:root{--ngxsmk-space-xs: .25rem;--ngxsmk-space-sm: .5rem;--ngxsmk-space-md: .75rem;--ngxsmk-space-lg: 1rem;--ngxsmk-space-xl: 1.5rem;--ngxsmk-space-2xl: 2rem;--ngxsmk-touch-target: 44px;--ngxsmk-cell-size-desktop: 40px;--ngxsmk-font-size-xs: .625rem;--ngxsmk-font-size-sm: .75rem;--ngxsmk-font-size-base: 1rem;--ngxsmk-font-size-lg: 1.125rem;--ngxsmk-font-size-xl: 1.25rem;--ngxsmk-line-height: 1.5;--ngxsmk-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;--ngxsmk-color-primary: var(--datepicker-primary-color);--ngxsmk-color-on-primary: var(--ion-color-primary-contrast, #ffffff);--ngxsmk-color-range-bg: var(--ion-color-primary-tint, #f5f3ff);--ngxsmk-color-surface: var(--ion-background-color, #ffffff);--ngxsmk-color-surface-hover: #f3f4f6;--ngxsmk-color-text-main: var(--ion-text-color, #1f2937);--ngxsmk-color-text-muted: var(--ion-text-color-step-400, #6b7280);--ngxsmk-color-border: var(--ion-item-border-color, var(--ion-border-color, #e5e7eb));--ngxsmk-radius-sm: 6px;--ngxsmk-radius-md: 8px;--ngxsmk-radius-lg: 12px;--ngxsmk-radius-xl: 16px;--ngxsmk-radius-popup: 16px;--ngxsmk-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);--ngxsmk-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .06);--ngxsmk-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--ngxsmk-transition-duration: .15s;--ngxsmk-transition-easing: cubic-bezier(.4, 0, .2, 1);--ngxsmk-transition: all var(--ngxsmk-transition-duration) var(--ngxsmk-transition-easing);--ngxsmk-z-backdrop: 2147483646;--ngxsmk-z-popover: 2147483647;--datepicker-primary-color: var(--ngxsmk-color-primary);--datepicker-primary-contrast: var(--ngxsmk-color-on-primary);--datepicker-range-background: var(--ngxsmk-color-range-bg);--datepicker-background: var(--ngxsmk-color-surface);--datepicker-hover-background: var(--ngxsmk-color-surface-hover);--datepicker-text-color: var(--ngxsmk-color-text-main);--datepicker-subtle-text-color: var(--ngxsmk-color-text-muted);--datepicker-border-color: var(--ngxsmk-color-border);--datepicker-radius-sm: var(--ngxsmk-radius-sm);--datepicker-radius-md: var(--ngxsmk-radius-md);--datepicker-radius-lg: var(--ngxsmk-radius-lg);--datepicker-border-radius: var(--ngxsmk-radius-lg);--datepicker-font-size-base: var(--ngxsmk-font-size-base);--datepicker-font-size-lg: var(--ngxsmk-font-size-lg);--datepicker-line-height: var(--ngxsmk-line-height);--datepicker-spacing-xs: var(--ngxsmk-space-xs);--datepicker-spacing-sm: var(--ngxsmk-space-sm);--datepicker-spacing-md: var(--ngxsmk-space-md);--datepicker-spacing-lg: var(--ngxsmk-space-lg);--datepicker-transition: var(--ngxsmk-transition)}.ngxsmk-popover-container{position:fixed;bottom:0;left:0;width:100%;max-height:90dvb;background:var(--ngxsmk-color-surface);border-radius:var(--ngxsmk-radius-popup) var(--ngxsmk-radius-popup) 0 0;z-index:var(--ngxsmk-z-popover);padding-top:max(0,env(safe-area-inset-top));padding-right:max(0,env(safe-area-inset-right));padding-bottom:max(1rem,env(safe-area-inset-bottom));padding-left:max(0,env(safe-area-inset-left));box-shadow:var(--ngxsmk-shadow-xl);border:unset!important;overflow-y:auto;overscroll-behavior:contain;display:flex;flex-direction:column}.ngxsmk-popover-container.ngxsmk-popover-open{visibility:visible;opacity:1}.ngxsmk-popover-container.ngxsmk-inline-container{position:static!important;width:100%;margin:0;transform:none!important;box-shadow:none!important;border:none;background:transparent;padding:0;max-height:none}@media(min-width:768px){.ngxsmk-popover-container{top:50%;left:50%;bottom:auto;transform:translate(-50%,-50%);width:clamp(320px,80vw,500px);border-radius:var(--ngxsmk-radius-popup);border:unset!important;max-height:calc(100vh - 64px);padding:0}}@media(min-width:1025px){.ngxsmk-popover-container{position:absolute;transform:none;width:max-content;min-width:360px;max-height:none;border-radius:var(--ngxsmk-radius-md);box-shadow:var(--ngxsmk-shadow-lg);border:unset!important}}.ngxsmk-calendar-container{display:flex;flex-direction:column;width:100%}@media(min-width:1025px){.ngxsmk-calendar-container.ngxsmk-has-multi-calendar{flex-direction:column;align-items:stretch;min-width:600px}.ngxsmk-calendar-container.ngxsmk-has-multi-calendar>ngxsmk-calendar-header,.ngxsmk-calendar-container.ngxsmk-has-multi-calendar>.ngxsmk-multi-calendar-container{width:100%;max-width:100%;min-width:0}}body.ngxsmk-scroll-locked{overflow:hidden!important;touch-action:none}.ngxsmk-day-cell.ngxsmk-other-month{opacity:.45;color:var(--datepicker-subtle-text-color)}.ngxsmk-ai-container{display:flex;flex-direction:column;gap:4px;margin:var(--datepicker-spacing-xs, 6px) var(--datepicker-spacing-sm, 10px) var(--datepicker-spacing-xs, 6px)}.ngxsmk-ai-bar{display:flex;align-items:center;gap:var(--datepicker-spacing-xs, 6px);padding:4px 8px;background:var(--datepicker-bg-color, #ffffff);border:1px solid var(--datepicker-border-color, #e2e8f0);border-radius:var(--datepicker-border-radius, 8px);box-shadow:0 1px 3px #0000000a;transition:border-color .2s ease,box-shadow .2s ease}.ngxsmk-popover-container.dark-theme .ngxsmk-ai-bar,.dark-theme .ngxsmk-ai-bar{background:var(--datepicker-bg-color, #1e293b);border-color:var(--datepicker-border-color, #334155);box-shadow:0 1px 3px #00000040}.ngxsmk-ai-bar:focus-within{border-color:var(--datepicker-primary-color, #3b82f6);box-shadow:0 0 0 2px #3b82f633}.ngxsmk-ai-icon{display:flex;align-items:center;justify-content:center;color:var(--datepicker-primary-color, #3b82f6);flex-shrink:0}.ngxsmk-ai-input{flex:1;min-width:0;border:none;background:transparent;outline:none;font-size:var(--datepicker-font-size-sm, 13px);font-family:inherit;color:var(--datepicker-text-color, #1e293b);padding:4px 2px}.ngxsmk-popover-container.dark-theme .ngxsmk-ai-input,.dark-theme .ngxsmk-ai-input{color:var(--datepicker-text-color, #f8fafc)}.ngxsmk-ai-input::placeholder{color:var(--datepicker-subtle-text-color, #94a3b8)}.ngxsmk-ai-submit{display:flex;align-items:center;justify-content:center;width:26px;height:26px;padding:0;background:var(--datepicker-primary-color, #3b82f6);color:#fff;border:none;border-radius:calc(var(--datepicker-border-radius, 8px) - 2px);cursor:pointer;transition:opacity .15s ease,transform .1s ease;flex-shrink:0}.ngxsmk-ai-submit:hover:not(:disabled){opacity:.9;transform:scale(1.03)}.ngxsmk-ai-submit:active:not(:disabled){transform:scale(.97)}.ngxsmk-ai-submit:disabled{opacity:.4;cursor:not-allowed}.ngxsmk-ai-spinner{width:12px;height:12px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:ngxsmk-ai-spin .6s linear infinite}@keyframes ngxsmk-ai-spin{to{transform:rotate(360deg)}}.ngxsmk-ai-chips{display:flex;flex-wrap:wrap;gap:4px;padding:0 2px}.ngxsmk-ai-chip{display:inline-flex;align-items:center;padding:2px 8px;font-size:11px;font-family:inherit;background:#3b82f614;color:var(--datepicker-primary-color, #3b82f6);border:1px solid rgba(59,130,246,.2);border-radius:12px;cursor:pointer;white-space:nowrap;transition:background-color .15s ease,border-color .15s ease}.ngxsmk-popover-container.dark-theme .ngxsmk-ai-chip,.dark-theme .ngxsmk-ai-chip{background:#3b82f626;border-color:#3b82f64d;color:#93c5fd}.ngxsmk-ai-chip:hover:not(:disabled){background:#3b82f62e;border-color:var(--datepicker-primary-color, #3b82f6)}.ngxsmk-ai-chip:disabled{opacity:.5;cursor:not-allowed}\n"] }]
}], ctorParameters: () => [], propDecorators: { mode: [{
type: Input
}], calendarViewMode: [{
type: Input
}], isInvalidDate: [{
type: Input
}], asyncDateFilter: [{
type: Input
}], asyncDateFilterLoading: [{ type: i0.Output, args: ["asyncDateFilterLoading"] }], asyncDateFilterError: [{ type: i0.Output, args: ["asyncDateFilterError"] }], showRanges: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRanges", required: false }] }], showPresets: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPresets", required: false }] }], showTime: [{
type: Input
}], timeOnly: [{
type: Input
}], timeRangeMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeRangeMode", required: false }] }], showCalendarButton: [{
type: Input
}], minuteInterval: [{
type: Input
}], use24Hour: [{
type: Input
}], secondInterval: [{
type: Input
}], showSeconds: [{
type: Input
}], holidayProvider: [{
type: Input
}], disableHolidays: [{
type: Input
}], disabledDates: [{
type: Input
}], disabledRanges: [{
type: Input
}], recurringPattern: [{
type: Input
}], dateTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateTemplate", required: false }] }], placeholder: [{
type: Input
}], inline: [{
type: Input
}], responsive: [{
type: Input
}], inputId: [{
type: Input
}], name: [{
type: Input
}], autocomplete: [{
type: Input
}], translations: [{
type: Input
}], translationService: [{
type: Input
}], clearLabel: [{
type: Input
}], closeLabel: [{
type: Input
}], prevMonthAriaLabel: [{
type: Input
}], nextMonthAriaLabel: [{
type: Input
}], clearAriaLabel: [{
type: Input
}], closeAriaLabel: [{
type: Input
}], weekStart: [{
type: Input
}], yearRange: [{
type: Input
}], timezone: [{
type: Input
}], showOtherMonths: [{
type: Input
}], hooks: [{
type: Input
}], enableKeyboardShortcuts: [{
type: Input
}], customShortcuts: [{
type: Input
}], autoApplyClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoApplyClose", required: false }] }], allowSameDay: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowSameDay", required: false }] }], displayFormat: [{
type: Input
}], allowTyping: [{
type: Input
}], inputMask: [{
type: Input
}], enableNaturalLanguage: [{
type: Input
}], naturalLanguagePreviewTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "naturalLanguagePreviewTemplate", required: false }] }], naturalLanguageResolved: [{ type: i0.Output, args: ["naturalLanguageResolved"] }], enableAi: [{
type: Input
}], aiPlaceholder: [{
type: Input
}], aiSuggestions: [{
type: Input
}], showAiSuggestions: [{
type: Input
}], aiResolver: [{
type: Input
}], aiPromptSubmitted: [{ type: i0.Output, args: ["aiPromptSubmitted"] }], calendars: [{
type: Input
}], rangePresetFactory: [{
type: Input
}], invalidRange: [{ type: i0.Output, args: ["invalidRange"] }], showTimezoneSelector: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTimezoneSelector", required: false }] }], defaultTimezone: [{
type: Input
}], timezoneChange: [{ type: i0.Output, args: ["timezoneChange"] }], calendarCount: [{
type: Input
}], calendarLayout: [{
type: Input
}], changeActiveMonthOnSelection: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], showWeekNumbers: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], weekNumberLabel: [{
type: Input
}], secondaryCalendar: [{
type: Input
}], dayMetadata: [{
type: Input
}], calendarHeaderTemplate: [{
type: Input
}], calendarFooterTemplate: [{
type: Input
}], defaultMonthOffset: [{
type: Input
}], syncScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "syncScroll", required: false }] }], align: [{
type: Input
}], useNativePicker: [{
type: Input
}], enableHapticFeedback: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableHapticFeedback", required: false }] }], mobileModalStyle: [{
type: Input
}], mobileTimePickerStyle: [{
type: Input
}], enablePullToRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "enablePullToRefresh", required: false }] }], mobileTheme: [{ type: i0.Input, args: [{ isSignal: true, alias: "mobileTheme", required: false }] }], enableVoiceInput: [{
type: Input
}], autoDetectMobile: [{
type: Input
}], disableFocusTrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableFocusTrap", required: false }] }], appendToBody: [{ type: i0.Input, args: [{ isSignal: true, alias: "appendToBody", required: false }] }], portalTemplate: [{
type: ViewChild,
args: ['portalContent', { static: true }]
}], value: [{
type: Input
}], field: [{
type: Input
}], startAt: [{
type: Input
}], locale: [{
type: Input
}], theme: [{
type: Input
}], isDarkMode: [{
type: HostBinding,
args: ['class.dark-theme']
}], dateFormatPattern: [{
type: Input
}], animationConfig: [{
type: Input
}], rtl: [{
type: Input
}], rtlClass: [{
type: HostBinding,
args: ['class.ngxsmk-rtl']
}], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }], disabledState: [{
type: Input
}], required: [{
type: Input
}], errorState: [{
type: Input
}], userAriaDescribedBy: [{
type: Input
}], valueChange: [{ type: i0.Output, args: ["valueChange"] }], action: [{ type: i0.Output, args: ["action"] }], validationError: [{ type: i0.Output, args: ["validationError"] }], minDate: [{
type: Input
}], maxDate: [{
type: Input
}], ranges: [{
type: Input
}], popoverContainer: [{
type: ViewChild,
args: ['popoverContainer', { static: false }]
}], datepickerInput: [{
type: ViewChild,
args: ['datepickerInput', { static: false }]
}], datepickerContent: [{
type: ViewChild,
args: ['datepickerContent', { static: false }]
}], comparisonRange: [{ type: i0.Input, args: [{ isSignal: true, alias: "comparisonRange", required: false }] }], onDocumentClick: [{
type: HostListener,
args: ['document:click', ['$event']]
}], onDocumentTouchStart: [{
type: HostListener,
args: ['document:touchstart', ['$event']]
}], onKeyDown: [{
type: HostListener,
args: ['keydown', ['$event']]
}] } });
/**
* 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 {}
* ```
*/
class NgxsmkDatepickerModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NgxsmkDatepickerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.11", ngImport: i0, type: NgxsmkDatepickerModule, imports: [NgxsmkDatepickerComponent], exports: [NgxsmkDatepickerComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NgxsmkDatepickerModule, imports: [NgxsmkDatepickerComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: NgxsmkDatepickerModule, decorators: [{
type: NgModule,
args: [{
imports: [NgxsmkDatepickerComponent],
exports: [NgxsmkDatepickerComponent],
}]
}] });
function exportToJson(value, options = {}) {
const data = serializeDateValue(value, options);
return JSON.stringify(data, null, 2);
}
function importFromJson(jsonString) {
try {
const data = JSON.parse(jsonString);
return deserializeDateValue(data);
}
catch (error) {
throw new Error(`Invalid JSON format: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
function exportToCsv(value, options = {}) {
const { csvHeaders = ['Type', 'Date', 'Time'] } = options;
const rows = [csvHeaders];
if (value === null || value === undefined) {
return rows.map((row) => row.join(',')).join('\n');
}
if (value instanceof Date) {
const dateStr = formatDateForExport(value, options);
const timeStr = options.includeTime ? formatTimeForExport(value) : '';
rows.push(['Single Date', dateStr, timeStr]);
}
else if (Array.isArray(value)) {
value.forEach((date) => {
if (date instanceof Date) {
const dateStr = formatDateForExport(date, options);
const timeStr = options.includeTime ? formatTimeForExport(date) : '';
rows.push(['Multiple Date', dateStr, timeStr]);
}
});
}
else if (typeof value === 'object' && 'start' in value && 'end' in value) {
const range = value;
if (range.start instanceof Date) {
const startDateStr = formatDateForExport(range.start, options);
const startTimeStr = options.includeTime ? formatTimeForExport(range.start) : '';
rows.push(['Range Start', startDateStr, startTimeStr]);
}
if (range.end instanceof Date) {
const endDateStr = formatDateForExport(range.end, options);
const endTimeStr = options.includeTime ? formatTimeForExport(range.end) : '';
rows.push(['Range End', endDateStr, endTimeStr]);
}
}
return rows.map((row) => row.map((cell) => `"${cell}"`).join(',')).join('\n');
}
function importFromCsv(csvString) {
const lines = csvString.trim().split('\n');
if (lines.length < 2) {
return null;
}
const dataRows = lines.slice(1);
const dates = [];
let rangeStart = null;
let rangeEnd = null;
for (const row of dataRows) {
const cells = row.split(',').map((cell) => cell.trim().replace(/^"|"$/g, ''));
if (cells.length < 2)
continue;
const type = cells[0];
const dateStr = cells[1];
const timeStr = cells[2] || '';
if (!dateStr)
continue;
try {
const date = parseDateFromString(dateStr, timeStr);
if (type === 'Single Date' || type === 'Multiple Date') {
dates.push(date);
}
else if (type === 'Range Start') {
rangeStart = date;
}
else if (type === 'Range End') {
rangeEnd = date;
}
}
catch {
// Silently handle parse errors
}
}
if (rangeStart && rangeEnd) {
return { start: rangeStart, end: rangeEnd };
}
else if (dates.length === 1) {
return dates[0] || null;
}
else if (dates.length > 1) {
return dates;
}
return null;
}
function exportToIcs(value, options = {}) {
const { summary = 'Date Selection', description = '', location = '' } = options;
const lines = [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:-//ngxsmk-datepicker//EN',
'CALSCALE:GREGORIAN',
'METHOD:PUBLISH',
];
if (value === null || value === undefined) {
lines.push('END:VCALENDAR');
return lines.join('\r\n');
}
if (value instanceof Date) {
lines.push(...createIcsEvent(value, value, summary, description, location));
}
else if (Array.isArray(value)) {
value.forEach((date, index) => {
if (date instanceof Date) {
lines.push(...createIcsEvent(date, date, `${summary} ${index + 1}`, description, location));
}
});
}
else if (typeof value === 'object' && 'start' in value && 'end' in value) {
const range = value;
if (range.start instanceof Date && range.end instanceof Date) {
lines.push(...createIcsEvent(range.start, range.end, summary, description, location));
}
}
lines.push('END:VCALENDAR');
return lines.join('\r\n');
}
function importFromIcs(icsString) {
const lines = icsString.split(/\r?\n/);
const events = [];
let currentEvent = null;
for (let i = 0; i < lines.length; i++) {
const line = lines[i]?.trim();
if (!line)
continue;
if (line === 'BEGIN:VEVENT') {
currentEvent = {};
}
else if (line === 'END:VEVENT' && currentEvent) {
if (currentEvent.dtstart && currentEvent.dtend) {
try {
const start = parseIcsDate(currentEvent.dtstart);
const end = parseIcsDate(currentEvent.dtend);
events.push({ start, end });
}
catch {
// Silently handle parse errors
}
}
currentEvent = null;
}
else if (currentEvent && line.startsWith('DTSTART')) {
currentEvent.dtstart = extractIcsValue(line);
}
else if (currentEvent && line.startsWith('DTEND')) {
currentEvent.dtend = extractIcsValue(line);
}
}
if (events.length === 0) {
return null;
}
else if (events.length === 1) {
const event = events[0];
if (!event)
return null;
if (event.start.getTime() === event.end.getTime()) {
return event.start;
}
return { start: event.start, end: event.end };
}
else {
return events.map((e) => e.start).filter((d) => d instanceof Date);
}
}
function serializeDateValue(value, options) {
if (value === null || value === undefined) {
return null;
}
if (value instanceof Date) {
return {
type: 'single',
date: formatDateForExport(value, options),
time: options.includeTime ? formatTimeForExport(value) : undefined,
iso: value.toISOString(),
};
}
if (Array.isArray(value)) {
return {
type: 'multiple',
dates: value.map((date) => ({
date: formatDateForExport(date, options),
time: options.includeTime ? formatTimeForExport(date) : undefined,
iso: date.toISOString(),
})),
};
}
if (typeof value === 'object' && 'start' in value && 'end' in value) {
const range = value;
if (range.start instanceof Date && range.end instanceof Date) {
return {
type: 'range',
start: {
date: formatDateForExport(range.start, options),
time: options.includeTime ? formatTimeForExport(range.start) : undefined,
iso: range.start.toISOString(),
},
end: {
date: formatDateForExport(range.end, options),
time: options.includeTime ? formatTimeForExport(range.end) : undefined,
iso: range.end.toISOString(),
},
};
}
}
return null;
}
function deserializeDateValue(data) {
if (!data || data === null) {
return null;
}
if (typeof data === 'object' && data !== null) {
const obj = data;
if (obj['type'] === 'single' && typeof obj['iso'] === 'string') {
return new Date(obj['iso']);
}
if (obj['type'] === 'multiple' && Array.isArray(obj['dates'])) {
return obj['dates']
.map((d) => {
if (typeof d === 'object' && d !== null && 'iso' in d && typeof d.iso === 'string') {
return new Date(d.iso);
}
return null;
})
.filter((d) => d !== null);
}
if (obj['type'] === 'range' &&
typeof obj['start'] === 'object' &&
obj['start'] !== null &&
'iso' in obj['start'] &&
typeof obj['end'] === 'object' &&
obj['end'] !== null &&
'iso' in obj['end']) {
const startObj = obj['start'];
const endObj = obj['end'];
if (typeof startObj['iso'] === 'string' && typeof endObj['iso'] === 'string') {
return {
start: new Date(startObj['iso']),
end: new Date(endObj['iso']),
};
}
}
}
return null;
}
function formatDateForExport(date, options) {
if (!date)
return '';
if (options.dateFormat) {
return formatDate(date, options.dateFormat);
}
const isoString = date.toISOString();
const datePart = isoString.split('T')[0];
return datePart || '';
}
function formatTimeForExport(date) {
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${hours}:${minutes}:${seconds}`;
}
function formatDate(date, format) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return format
.replace('YYYY', String(year))
.replace('YY', String(year).slice(-2))
.replace('MM', month)
.replace('DD', day)
.replace('HH', hours)
.replace('mm', minutes)
.replace('ss', seconds);
}
function parseDateFromString(dateStr, timeStr) {
if (dateStr.includes('T') || dateStr.includes(' ')) {
return new Date(dateStr + (timeStr ? ' ' + timeStr : ''));
}
const dateMatch = dateStr.match(/(\d{4})-(\d{2})-(\d{2})/);
if (dateMatch) {
const year = dateMatch[1];
const month = dateMatch[2];
const day = dateMatch[3];
if (year && month && day) {
const date = new Date(parseInt(year, 10), parseInt(month, 10) - 1, parseInt(day, 10));
if (timeStr) {
const timeMatch = timeStr.match(/(\d{2}):(\d{2}):(\d{2})/);
if (timeMatch) {
const hours = timeMatch[1];
const minutes = timeMatch[2];
const seconds = timeMatch[3];
if (hours && minutes && seconds) {
date.setHours(parseInt(hours, 10), parseInt(minutes, 10), parseInt(seconds, 10));
}
}
}
return date;
}
}
return new Date(dateStr + (timeStr ? ' ' + timeStr : ''));
}
function createIcsEvent(start, end, summary, description, location) {
const formatIcsDate = (date) => {
return date.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z';
};
const lines = [
'BEGIN:VEVENT',
`UID:${Date.now()}-${Math.random().toString(36).substr(2, 9)}@ngxsmk-datepicker`,
`DTSTAMP:${formatIcsDate(new Date())}`,
`DTSTART:${formatIcsDate(start)}`,
`DTEND:${formatIcsDate(end)}`,
`SUMMARY:${escapeIcsText(summary)}`,
];
if (description) {
lines.push(`DESCRIPTION:${escapeIcsText(description)}`);
}
if (location) {
lines.push(`LOCATION:${escapeIcsText(location)}`);
}
lines.push('END:VEVENT');
return lines;
}
function parseIcsDate(icsDate) {
const cleanDate = icsDate.replace(/Z$/, '');
const match = cleanDate.match(/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})/);
if (match) {
const year = match[1];
const month = match[2];
const day = match[3];
const hour = match[4];
const minute = match[5];
const second = match[6];
if (year && month && day && hour && minute && second) {
return new Date(parseInt(year, 10), parseInt(month, 10) - 1, parseInt(day, 10), parseInt(hour, 10), parseInt(minute, 10), parseInt(second, 10));
}
}
throw new Error(`Invalid ICS date format: ${icsDate}`);
}
function extractIcsValue(line) {
const colonIndex = line.indexOf(':');
return colonIndex >= 0 ? line.substring(colonIndex + 1) : '';
}
function escapeIcsText(text) {
return text.replace(/\\/g, '\\\\').replace(/;/g, '\\;').replace(/,/g, '\\,').replace(/\n/g, '\\n');
}
class DefaultTranslationService {
constructor() {
this.locale = 'en';
this.translationRegistry = inject(TranslationRegistryService);
this.translations = this.translationRegistry.getTranslations('en');
}
initialize(translations, locale = 'en') {
this.translations = translations;
this.locale = locale;
}
initializeFromLocale(locale) {
this.locale = locale;
this.translations = this.translationRegistry.getTranslations(locale);
}
translate(key, params) {
const translation = this.translations[key];
if (!translation) {
return key;
}
if (params && typeof translation === 'string') {
let result = translation;
for (const [paramKey, paramValue] of Object.entries(params)) {
result = result.replace(new RegExp(`{{${paramKey}}}`, 'g'), String(paramValue));
}
return result;
}
if (typeof translation === 'string') {
return translation;
}
if (typeof translation === 'function') {
return translation(params);
}
return key;
}
getCurrentLocale() {
return this.locale;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DefaultTranslationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DefaultTranslationService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DefaultTranslationService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [] });
/**
* Theme builder service for generating CSS-in-JS styles and managing themes
*/
class ThemeBuilderService {
constructor() {
this.platformId = inject(PLATFORM_ID);
this.styleElement = null;
this.scopedStyleElements = new Map();
}
/**
* Map theme color keys to actual CSS variable names
*/
mapColorKey(key) {
const colorMap = {
primary: 'primary-color',
primaryContrast: 'primary-contrast',
rangeBackground: 'range-background',
background: 'background',
text: 'text-color',
textSecondary: 'subtle-text-color',
subtleText: 'subtle-text-color',
border: 'border-color',
borderColor: 'border-color',
hover: 'hover-background',
hoverBackground: 'hover-background',
active: 'active',
disabled: 'disabled',
error: 'error',
secondary: 'secondary',
surface: 'surface',
};
return colorMap[key] || key;
}
/**
* Map typography keys to actual CSS variable names
*/
mapTypographyKey(key) {
const typographyMap = {
fontFamily: 'font-family',
fontSize: 'font-size-base',
fontSizeBase: 'font-size-base',
fontSizeXs: 'font-size-xs',
fontSizeSm: 'font-size-sm',
fontSizeLg: 'font-size-lg',
fontSizeXl: 'font-size-xl',
fontWeight: 'font-weight',
lineHeight: 'line-height',
};
return typographyMap[key] || key;
}
/**
* Generate CSS variables from a theme object
*/
generateTheme(theme) {
const cssVars = [];
if (theme.colors) {
Object.entries(theme.colors).forEach(([key, value]) => {
if (value !== undefined) {
const cssKey = this.mapColorKey(key);
cssVars.push(`--datepicker-${cssKey}: ${value};`);
}
});
// Bridge internal --ngxsmk-color-* from public --datepicker-* (Issue #222)
if (theme.colors.primary)
cssVars.push(`--ngxsmk-color-primary: ${theme.colors.primary};`);
if (theme.colors.background)
cssVars.push(`--ngxsmk-color-surface: ${theme.colors.background};`);
if (theme.colors.border ?? theme.colors['borderColor'])
cssVars.push(`--ngxsmk-color-border: ${theme.colors.border ?? theme.colors['borderColor']};`);
if (theme.colors.hover ?? theme.colors['hoverBackground'])
cssVars.push(`--ngxsmk-color-surface-hover: ${theme.colors.hover ?? theme.colors['hoverBackground']};`);
if (theme.colors.text)
cssVars.push(`--ngxsmk-color-text-main: ${theme.colors.text};`);
if (theme.colors.textSecondary ?? theme.colors['subtleText'])
cssVars.push(`--ngxsmk-color-text-muted: ${theme.colors.textSecondary ?? theme.colors['subtleText']};`);
if (theme.colors['rangeBackground'])
cssVars.push(`--ngxsmk-color-range-bg: ${theme.colors['rangeBackground']};`);
}
if (theme.spacing) {
Object.entries(theme.spacing).forEach(([key, value]) => {
if (value !== undefined) {
cssVars.push(`--datepicker-spacing-${key}: ${value};`);
}
});
}
if (theme.typography) {
Object.entries(theme.typography).forEach(([key, value]) => {
if (value !== undefined) {
const cssKey = this.mapTypographyKey(key);
cssVars.push(`--datepicker-${cssKey}: ${value};`);
}
});
}
if (theme.borderRadius) {
Object.entries(theme.borderRadius).forEach(([key, value]) => {
if (value !== undefined) {
cssVars.push(`--datepicker-radius-${key}: ${value};`);
}
});
}
if (theme.shadows) {
Object.entries(theme.shadows).forEach(([key, value]) => {
if (value !== undefined) {
const varName = key === 'focus' ? 'shadow-focus' : `shadow-${key}`;
cssVars.push(`--datepicker-${varName}: ${value};`);
}
});
}
return cssVars.join('\n');
}
/**
* 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, targetElement) {
if (!isPlatformBrowser(this.platformId)) {
return;
}
const themeCss = this.generateTheme(theme);
const styles = this.generateStyleObject(theme);
if (targetElement) {
// Apply theme to specific element only using a scoped style element
targetElement.dataset['themeApplied'] = 'true';
// Remove existing scoped style if any
const existingStyle = this.scopedStyleElements.get(targetElement);
if (existingStyle) {
existingStyle.remove();
this.scopedStyleElements.delete(targetElement);
}
// Create a scoped style element for this specific element
const scopedStyle = document.createElement('style');
scopedStyle.dataset['datepickerThemeScoped'] = '';
// Generate CSS targeting this specific element
const elementId = targetElement.id || `datepicker-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
if (!targetElement.id) {
targetElement.id = elementId;
}
// When target is a wrapper, also apply to descendant ngxsmk-datepicker so library defaults are overridden (Issue #222)
const selector = targetElement.tagName === 'NGXSMK-DATEPICKER'
? `#${elementId}, ngxsmk-datepicker-content .ngxsmk-popover-container, ngxsmk-datepicker-content .ngxsmk-backdrop`
: `#${elementId}, #${elementId} ngxsmk-datepicker, #${elementId} .ngxsmk-popover-container, #${elementId} .ngxsmk-backdrop, ngxsmk-datepicker-content .ngxsmk-popover-container, ngxsmk-datepicker-content .ngxsmk-backdrop`;
const css = `${selector} {\n${themeCss}\n}`;
scopedStyle.textContent = css;
document.head.appendChild(scopedStyle);
this.scopedStyleElements.set(targetElement, scopedStyle);
// Apply inline styles for immediate effect using batch updates
requestAnimationFrame(() => {
// Apply to the target element itself
Object.entries(styles).forEach(([property, value]) => {
targetElement.style.setProperty(property, value, 'important');
});
// Apply to nested ngxsmk-datepicker elements when target is a wrapper
const nestedDatepickers = targetElement.querySelectorAll('ngxsmk-datepicker');
nestedDatepickers.forEach((el) => {
const htmlElement = el;
Object.entries(styles).forEach(([property, value]) => {
htmlElement.style.setProperty(property, value, 'important');
});
});
const portalledElements = document.querySelectorAll('ngxsmk-datepicker-content .ngxsmk-popover-container, ngxsmk-datepicker-content .ngxsmk-backdrop');
portalledElements.forEach((element) => {
const htmlElement = element;
Object.entries(styles).forEach(([property, value]) => {
htmlElement.style.setProperty(property, value, 'important');
});
});
});
}
else {
// Apply globally (original behavior) - optimized version
if (!this.styleElement) {
this.styleElement = document.createElement('style');
this.styleElement.dataset['datepickerTheme'] = '';
document.head.appendChild(this.styleElement);
}
// Optimized CSS - use more specific selector to override global :root variables
// Also apply to body-portalled popovers and backdrops for mobile
const css = `:root, :root > body, ngxsmk-datepicker-content .ngxsmk-popover-container, ngxsmk-datepicker-content .ngxsmk-backdrop {\n${themeCss}\n}`;
this.styleElement.textContent = css;
// Apply to elements more efficiently using requestAnimationFrame
requestAnimationFrame(() => {
this.applyToElements(theme);
});
}
}
/**
* Apply theme variables directly to all datepicker elements (for global theme)
* Optimized version with batch DOM operations
*/
applyToElements(theme) {
if (!isPlatformBrowser(this.platformId)) {
return;
}
const datepickerElements = document.querySelectorAll('ngxsmk-datepicker, ngxsmk-datepicker-content .ngxsmk-popover-container, ngxsmk-datepicker-content .ngxsmk-backdrop');
const styles = this.generateStyleObject(theme);
// Batch DOM operations for better performance
datepickerElements.forEach((element) => {
const htmlElement = element;
// Mark element as having theme applied for CSS selector specificity
htmlElement.dataset['themeApplied'] = '';
// Batch style property updates
Object.entries(styles).forEach(([property, value]) => {
htmlElement.style.setProperty(property, value, 'important');
});
});
}
/**
* Generate CSS-in-JS style object (for styled-components, emotion, etc.)
*/
generateStyleObject(theme) {
const styles = {};
if (theme.colors) {
Object.entries(theme.colors).forEach(([key, value]) => {
if (value !== undefined) {
const cssKey = this.mapColorKey(key);
styles[`--datepicker-${cssKey}`] = value;
}
});
if (theme.colors.primary)
styles['--ngxsmk-color-primary'] = theme.colors.primary;
if (theme.colors.background)
styles['--ngxsmk-color-surface'] = theme.colors.background;
if (theme.colors.border ?? theme.colors['borderColor'])
styles['--ngxsmk-color-border'] = String(theme.colors.border ?? theme.colors['borderColor']);
if (theme.colors.hover ?? theme.colors['hoverBackground'])
styles['--ngxsmk-color-surface-hover'] = String(theme.colors.hover ?? theme.colors['hoverBackground']);
if (theme.colors.text)
styles['--ngxsmk-color-text-main'] = theme.colors.text;
if (theme.colors.textSecondary ?? theme.colors['subtleText'])
styles['--ngxsmk-color-text-muted'] = String(theme.colors.textSecondary ?? theme.colors['subtleText']);
if (theme.colors['rangeBackground'])
styles['--ngxsmk-color-range-bg'] = theme.colors['rangeBackground'];
}
if (theme.spacing) {
Object.entries(theme.spacing).forEach(([key, value]) => {
if (value !== undefined) {
styles[`--datepicker-spacing-${key}`] = value;
}
});
}
if (theme.typography) {
Object.entries(theme.typography).forEach(([key, value]) => {
if (value !== undefined) {
const cssKey = this.mapTypographyKey(key);
styles[`--datepicker-${cssKey}`] = value;
}
});
}
if (theme.borderRadius) {
Object.entries(theme.borderRadius).forEach(([key, value]) => {
if (value !== undefined) {
styles[`--datepicker-radius-${key}`] = value;
}
});
}
if (theme.shadows) {
Object.entries(theme.shadows).forEach(([key, value]) => {
if (value !== undefined) {
const varName = key === 'focus' ? 'shadow-focus' : `shadow-${key}`;
styles[`--datepicker-${varName}`] = value;
}
});
}
return styles;
}
/**
* Remove applied theme
* @param targetElement Optional specific element to remove theme from. If not provided, removes from all.
*/
removeTheme(targetElement) {
if (!isPlatformBrowser(this.platformId)) {
return;
}
if (targetElement) {
// Remove theme from specific element only
delete targetElement.dataset['themeApplied'];
// Remove scoped style element
const scopedStyle = this.scopedStyleElements.get(targetElement);
if (scopedStyle) {
scopedStyle.remove();
this.scopedStyleElements.delete(targetElement);
}
// Remove temporary ID if we added one
if (targetElement.id && targetElement.id.startsWith('datepicker-')) {
targetElement.removeAttribute('id');
}
// Remove all datepicker CSS variables from this element
const allStyles = Array.from(targetElement.style);
allStyles.forEach((prop) => {
if (prop.startsWith('--datepicker-')) {
targetElement.style.removeProperty(prop);
}
});
// Clear from nested ngxsmk-datepicker elements
const nestedDatepickers = targetElement.querySelectorAll('ngxsmk-datepicker');
nestedDatepickers.forEach((el) => {
const htmlEl = el;
const nestedStyles = Array.from(htmlEl.style);
nestedStyles.forEach((prop) => {
if (prop.startsWith('--datepicker-')) {
htmlEl.style.removeProperty(prop);
}
});
});
// Also clear from any open body-portalled popovers
const portalledElements = document.querySelectorAll('ngxsmk-datepicker-content .ngxsmk-popover-container, ngxsmk-datepicker-content .ngxsmk-backdrop');
portalledElements.forEach((element) => {
const htmlElement = element;
const portalledStyles = Array.from(htmlElement.style);
portalledStyles.forEach((prop) => {
if (prop.startsWith('--datepicker-')) {
htmlElement.style.removeProperty(prop);
}
});
});
}
else {
// Remove global theme (original behavior)
// Remove the style element
if (this.styleElement) {
this.styleElement.remove();
this.styleElement = null;
}
// Clear inline styles from all datepicker elements and body-portalled popovers
const datepickerElements = document.querySelectorAll('ngxsmk-datepicker, ngxsmk-datepicker-content .ngxsmk-popover-container, ngxsmk-datepicker-content .ngxsmk-backdrop');
datepickerElements.forEach((element) => {
const htmlElement = element;
// Remove theme attribute
delete htmlElement.dataset['themeApplied'];
// Remove all datepicker CSS variables
const allStyles = Array.from(htmlElement.style);
allStyles.forEach((prop) => {
if (prop.startsWith('--datepicker-')) {
htmlElement.style.removeProperty(prop);
}
});
});
}
}
/**
* Get current theme from CSS variables
*/
getCurrentTheme(selector = ':root') {
if (!isPlatformBrowser(this.platformId)) {
return {};
}
const element = selector === ':root' ? document.documentElement : document.querySelector(selector);
if (!element) {
return {};
}
const computedStyle = globalThis.getComputedStyle(element);
const theme = {
colors: {},
spacing: {},
typography: {},
borderRadius: {},
shadows: {},
};
// Extract CSS variables
const allStyles = Array.from(computedStyle).filter((prop) => prop.startsWith('--datepicker-'));
allStyles.forEach((prop) => {
const value = computedStyle.getPropertyValue(prop).trim();
const key = prop.replace('--datepicker-', '');
if (key.startsWith('color-') || key.startsWith('bg-') || key.startsWith('border-')) {
if (!theme.colors)
theme.colors = {};
theme.colors[key] = value;
}
else if (key.startsWith('spacing-')) {
if (!theme.spacing)
theme.spacing = {};
theme.spacing[key.replace('spacing-', '')] = value;
}
else if (key.startsWith('font-')) {
if (!theme.typography)
theme.typography = {};
theme.typography[key.replace('font-', '')] = value;
}
else if (key.startsWith('radius-')) {
if (!theme.borderRadius)
theme.borderRadius = {};
theme.borderRadius[key.replace('radius-', '')] = value;
}
else if (key.startsWith('shadow-')) {
if (!theme.shadows)
theme.shadows = {};
theme.shadows[key.replace('shadow-', '')] = value;
}
else if (key === 'focus-shadow') {
if (!theme.shadows)
theme.shadows = {};
theme.shadows['focus'] = value;
}
});
return theme;
}
/**
* Clean up all themes and resources when service is destroyed
*/
cleanupAllThemes() {
// Remove global theme
if (this.styleElement) {
this.styleElement.remove();
this.styleElement = null;
}
// Remove all scoped themes
this.scopedStyleElements.forEach((styleElement) => {
styleElement.remove();
});
this.scopedStyleElements.clear();
}
ngOnDestroy() {
this.cleanupAllThemes();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: ThemeBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: ThemeBuilderService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: ThemeBuilderService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
/**
* Service for managing date presets with localStorage persistence
*/
class DatePresetsService {
constructor() {
this.platformId = inject(PLATFORM_ID);
this.storageKey = 'ngxsmk-datepicker-presets';
this.presets = new Map();
if (isPlatformBrowser(this.platformId)) {
this.loadPresets();
}
}
/**
* Save a date preset
*/
savePreset(preset) {
const id = this.generateId();
const now = new Date();
const fullPreset = {
...preset,
id,
createdAt: now,
updatedAt: now,
};
this.presets.set(id, fullPreset);
this.persistPresets();
return fullPreset;
}
/**
* Update an existing preset
*/
updatePreset(id, updates) {
const existing = this.presets.get(id);
if (!existing) {
return null;
}
const updated = {
...existing,
...updates,
updatedAt: new Date(),
};
this.presets.set(id, updated);
this.persistPresets();
return updated;
}
/**
* Get a preset by ID
*/
getPreset(id) {
return this.presets.get(id) || null;
}
/**
* Get all presets
*/
getAllPresets() {
return Array.from(this.presets.values());
}
/**
* Get presets by category
*/
getPresetsByCategory(category) {
return Array.from(this.presets.values()).filter((p) => p.category === category);
}
/**
* Get all categories
*/
getCategories() {
const categories = new Set();
this.presets.forEach((preset) => {
if (preset.category) {
categories.add(preset.category);
}
});
return Array.from(categories);
}
/**
* Delete a preset
*/
deletePreset(id) {
const deleted = this.presets.delete(id);
if (deleted) {
this.persistPresets();
}
return deleted;
}
/**
* Clear all presets
*/
clearPresets() {
this.presets.clear();
this.persistPresets();
}
/**
* Apply a preset value (returns the value, not the preset object)
*/
applyPreset(id) {
const preset = this.presets.get(id);
if (!preset) {
return null;
}
// Deep clone the value to avoid reference issues
return this.cloneValue(preset.value);
}
/**
* Check if a preset exists
*/
hasPreset(id) {
return this.presets.has(id);
}
/**
* Get preset count
*/
getPresetCount() {
return this.presets.size;
}
/**
* Export presets to JSON
*/
exportPresets() {
const presetsArray = Array.from(this.presets.values());
return JSON.stringify(presetsArray, null, 2);
}
/**
* Import presets from JSON
*/
importPresets(jsonString, merge = false) {
try {
const presetsArray = JSON.parse(jsonString);
let imported = 0;
let errors = 0;
presetsArray.forEach((preset) => {
try {
// Validate preset structure
if (!preset.id || !preset.name || preset.value === undefined) {
errors++;
return;
}
// Convert date strings to Date objects
const processedPreset = {
...preset,
createdAt: preset.createdAt ? new Date(preset.createdAt) : new Date(),
updatedAt: preset.updatedAt ? new Date(preset.updatedAt) : new Date(),
value: this.deserializeValue(preset.value),
};
if (merge && this.presets.has(processedPreset.id)) {
// Update existing preset
this.updatePreset(processedPreset.id, processedPreset);
}
else {
// Add new preset
this.presets.set(processedPreset.id, processedPreset);
}
imported++;
}
catch {
errors++;
// Silently handle import errors
}
});
this.persistPresets();
return { imported, errors };
}
catch (error) {
throw new Error(`Invalid JSON format: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
// Private methods
loadPresets() {
if (!isPlatformBrowser(this.platformId)) {
return;
}
try {
const stored = localStorage.getItem(this.storageKey);
if (stored) {
const presetsArray = JSON.parse(stored);
presetsArray.forEach((preset) => {
// Convert date strings to Date objects
const processedPreset = {
...preset,
createdAt: preset.createdAt ? new Date(preset.createdAt) : new Date(),
updatedAt: preset.updatedAt ? new Date(preset.updatedAt) : new Date(),
value: this.deserializeValue(preset.value),
};
this.presets.set(processedPreset.id, processedPreset);
});
}
}
catch {
// Silently handle localStorage load errors
}
}
persistPresets() {
if (!isPlatformBrowser(this.platformId)) {
return;
}
try {
const presetsArray = Array.from(this.presets.values());
localStorage.setItem(this.storageKey, JSON.stringify(presetsArray));
}
catch {
// Silently handle localStorage save errors
}
}
generateId() {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
cloneValue(value) {
if (value === null || value === undefined) {
return null;
}
if (value instanceof Date) {
return new Date(value.getTime());
}
if (Array.isArray(value)) {
return value.map((date) => new Date(date.getTime()));
}
if (typeof value === 'object' && value !== null && 'start' in value && 'end' in value) {
const range = value;
return {
start: range.start ? new Date(range.start.getTime()) : null,
end: range.end ? new Date(range.end.getTime()) : null,
};
}
return value;
}
deserializeValue(value) {
if (value === null || value === undefined) {
return null;
}
if (typeof value === 'string') {
return new Date(value);
}
if (Array.isArray(value)) {
return value.map((item) => {
if (typeof item === 'string') {
return new Date(item);
}
return item instanceof Date ? item : new Date(item);
});
}
if (typeof value === 'object' && value !== null && 'start' in value && 'end' in value) {
const rangeValue = value;
const start = typeof rangeValue.start === 'string'
? new Date(rangeValue.start)
: rangeValue.start instanceof Date
? rangeValue.start
: null;
const end = typeof rangeValue.end === 'string'
? new Date(rangeValue.end)
: rangeValue.end instanceof Date
? rangeValue.end
: null;
if (start && end) {
return { start, end };
}
}
return null;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DatePresetsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DatePresetsService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DatePresetsService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: () => [] });
class DatepickerOverlayService {
constructor() {
this.platformId = inject(PLATFORM_ID);
this.isBrowser = isPlatformBrowser(this.platformId);
}
/**
* Calculates the best position for an overlay popover relative to a trigger anchor.
* Handles viewport collision detection and automatic top/bottom flip.
*/
calculatePosition(anchor, popover, options = {}) {
if (!this.isBrowser) {
return { top: 0, left: 0, placement: 'bottom' };
}
const { alignment = 'left', offset = 8, flipOnCollision = true, minWidth = 320 } = options;
const anchorRect = anchor instanceof HTMLElement ? anchor.getBoundingClientRect() : anchor;
const popoverWidth = popover instanceof HTMLElement ? popover.offsetWidth || minWidth : popover.width || minWidth;
const popoverHeight = popover instanceof HTMLElement ? popover.offsetHeight || 380 : popover.height || 380;
const viewportWidth = options.viewportWidth ?? (typeof window !== 'undefined' ? window.innerWidth : 1024);
const viewportHeight = options.viewportHeight ?? (typeof window !== 'undefined' ? window.innerHeight : 768);
const scrollX = typeof window !== 'undefined' ? window.scrollX || window.pageXOffset || 0 : 0;
const scrollY = typeof window !== 'undefined' ? window.scrollY || window.pageYOffset || 0 : 0;
const spaceBelow = viewportHeight - anchorRect.bottom;
const spaceAbove = anchorRect.top;
let placement = 'bottom';
let top = anchorRect.bottom + scrollY + offset;
if (flipOnCollision && spaceBelow < popoverHeight && spaceAbove > spaceBelow) {
placement = 'top';
top = anchorRect.top + scrollY - popoverHeight - offset;
}
let left = anchorRect.left + scrollX;
if (alignment === 'right') {
left = anchorRect.right + scrollX - popoverWidth;
}
else if (alignment === 'center') {
left = anchorRect.left + scrollX + (anchorRect.width - popoverWidth) / 2;
}
// Clamp horizontally within viewport if viewport is wide enough
if (viewportWidth >= popoverWidth + 16) {
if (left + popoverWidth > viewportWidth + scrollX - 8) {
left = viewportWidth + scrollX - popoverWidth - 8;
}
if (left < scrollX + 8) {
left = scrollX + 8;
}
}
return {
top: Math.round(top),
left: Math.round(left),
placement,
width: Math.max(minWidth, Math.round(anchorRect.width)),
};
}
/**
* Applies calculated position styles to a target popover element.
*/
applyPosition(popover, position, appendToBody = false) {
if (!popover || !this.isBrowser)
return;
if (appendToBody) {
popover.style.position = 'fixed';
popover.style.top = `${position.top - (window.scrollY || 0)}px`;
popover.style.left = `${position.left - (window.scrollX || 0)}px`;
popover.style.zIndex = '9999';
}
else {
popover.style.top = `${position.top}px`;
popover.style.left = `${position.left}px`;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DatepickerOverlayService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DatepickerOverlayService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DatepickerOverlayService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
function resolveDate(input) {
if (typeof input === 'function') {
const res = input();
return res ? normalizeDate(res) : null;
}
return input ? normalizeDate(input) : null;
}
function isDateRangeObject(val) {
return typeof val === 'object' && val !== null && ('start' in val || 'end' in val);
}
/**
* 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)));
* ```
*/
function ngxsmkMinDateValidator(minDate) {
return (control) => {
const value = control.value;
if (!value)
return null;
const min = resolveDate(minDate);
if (!min || Number.isNaN(min.getTime()))
return null;
const minTime = getStartOfDay(min).getTime();
// Single Date
if (value instanceof Date || typeof value === 'string') {
const parsed = normalizeDate(value);
if (parsed && !Number.isNaN(parsed.getTime())) {
if (getStartOfDay(parsed).getTime() < minTime) {
return { ngxsmkMinDate: { min, actual: parsed } };
}
}
}
// Range Object
if (isDateRangeObject(value)) {
const start = value.start ? normalizeDate(value.start) : null;
if (start && !Number.isNaN(start.getTime())) {
if (getStartOfDay(start).getTime() < minTime) {
return { ngxsmkMinDate: { min, actual: start } };
}
}
}
// Multiple Dates
if (Array.isArray(value)) {
for (const d of value) {
const parsed = normalizeDate(d);
if (parsed && !Number.isNaN(parsed.getTime()) && getStartOfDay(parsed).getTime() < minTime) {
return { ngxsmkMinDate: { min, actual: parsed } };
}
}
}
return null;
};
}
/**
* 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)));
* ```
*/
function ngxsmkMaxDateValidator(maxDate) {
return (control) => {
const value = control.value;
if (!value)
return null;
const max = resolveDate(maxDate);
if (!max || Number.isNaN(max.getTime()))
return null;
const maxTime = getEndOfDay(max).getTime();
// Single Date
if (value instanceof Date || typeof value === 'string') {
const parsed = normalizeDate(value);
if (parsed && !Number.isNaN(parsed.getTime())) {
if (getEndOfDay(parsed).getTime() > maxTime) {
return { ngxsmkMaxDate: { max, actual: parsed } };
}
}
}
// Range Object
if (isDateRangeObject(value)) {
const end = value.end ? normalizeDate(value.end) : null;
if (end && !Number.isNaN(end.getTime())) {
if (getEndOfDay(end).getTime() > maxTime) {
return { ngxsmkMaxDate: { max, actual: end } };
}
}
}
// Multiple Dates
if (Array.isArray(value)) {
for (const d of value) {
const parsed = normalizeDate(d);
if (parsed && !Number.isNaN(parsed.getTime()) && getEndOfDay(parsed).getTime() > maxTime) {
return { ngxsmkMaxDate: { max, actual: parsed } };
}
}
}
return null;
};
}
/**
* 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 }));
* ```
*/
function ngxsmkDateRangeValidator(options = {}) {
const { minDays, maxDays, requireBoth = false } = options;
return (control) => {
const value = control.value;
if (!value)
return null;
if (!isDateRangeObject(value))
return null;
const start = value.start ? normalizeDate(value.start) : null;
const end = value.end ? normalizeDate(value.end) : null;
if (requireBoth && (!start || !end)) {
return { ngxsmkRangeIncomplete: true };
}
if (start && end && !Number.isNaN(start.getTime()) && !Number.isNaN(end.getTime())) {
const startTime = getStartOfDay(start).getTime();
const endTime = getStartOfDay(end).getTime();
if (endTime < startTime) {
return { ngxsmkRangeInvalid: { message: 'End date must be after start date' } };
}
const diffDays = Math.round((endTime - startTime) / (1000 * 60 * 60 * 24)) + 1;
if (typeof minDays === 'number' && diffDays < minDays) {
return { ngxsmkRangeTooShort: { minDays, actualDays: diffDays } };
}
if (typeof maxDays === 'number' && diffDays > maxDays) {
return { ngxsmkRangeTooLong: { maxDays, actualDays: diffDays } };
}
}
return null;
};
}
/**
* 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)]));
* ```
*/
function ngxsmkBlockedDatesValidator(blocked) {
let isBlockedFn;
if (typeof blocked === 'function') {
isBlockedFn = blocked;
}
else {
const blockedSet = new Set();
for (const item of blocked) {
const d = normalizeDate(item);
if (d && !Number.isNaN(d.getTime())) {
blockedSet.add(getStartOfDay(d).getTime());
}
}
isBlockedFn = (d) => blockedSet.has(getStartOfDay(d).getTime());
}
return (control) => {
const value = control.value;
if (!value)
return null;
// Single Date
if (value instanceof Date || typeof value === 'string') {
const parsed = normalizeDate(value);
if (parsed && !Number.isNaN(parsed.getTime()) && isBlockedFn(parsed)) {
return { ngxsmkDateBlocked: { date: parsed } };
}
}
// Range Object
if (isDateRangeObject(value)) {
const start = value.start ? normalizeDate(value.start) : null;
const end = value.end ? normalizeDate(value.end) : null;
if (start && end) {
const curr = new Date(getStartOfDay(start));
const endDay = getStartOfDay(end);
const blockedFound = [];
while (curr.getTime() <= endDay.getTime()) {
if (isBlockedFn(curr)) {
blockedFound.push(new Date(curr));
}
curr.setDate(curr.getDate() + 1);
}
if (blockedFound.length > 0) {
return { ngxsmkRangeContainsBlocked: { blockedDates: blockedFound } };
}
}
else if (start && isBlockedFn(start)) {
return { ngxsmkDateBlocked: { date: start } };
}
}
// Multiple Dates
if (Array.isArray(value)) {
const blockedFound = [];
for (const d of value) {
const parsed = normalizeDate(d);
if (parsed && !Number.isNaN(parsed.getTime()) && isBlockedFn(parsed)) {
blockedFound.push(parsed);
}
}
if (blockedFound.length > 0) {
return { ngxsmkDatesBlocked: { blockedDates: blockedFound } };
}
}
return null;
};
}
/**
* Main entry point for ngxsmk-datepicker
*
* All exports are explicit to enable optimal tree-shaking.
* Import only what you need:
*
* @example
* ```typescript
* import { NgxsmkDatepickerComponent, getStartOfDay, normalizeDate } from 'ngxsmk-datepicker';
* ```
*/
// Material (mat-form-field) integration is optional. The main bundle does not import @angular/material.
// To use with mat-form-field: install @angular/material and @angular/cdk, then add the directive
// from the repo or use the snippet in INTEGRATION.md § Angular Material.
/**
* Generated bundle index. Do not edit.
*/
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 };
//# sourceMappingURL=ngxsmk-datepicker.mjs.map