Параметр disableDayFn

Параметр disableDayFn в библиотеке Pikaday представляет собой функцию высшего порядка, предназначенную для динамического управления доступностью конкретных календарных дней. В отличие от статических ограничений вроде minDate или maxDate, данный параметр позволяет реализовать произвольную бизнес-логику блокировки дат на основе пользовательской функции.

Сигнатура функции имеет следующий вид:

disableDayFn: (date) => boolean

Аргумент date — это объект типа Date, передаваемый библиотекой при рендеринге каждого дня календаря. Возвращаемое значение строго интерпретируется как логический флаг:

  • true — день будет отключён (недоступен для выбора)
  • false — день остаётся доступным

Механика работы внутри календаря

При построении каждого месяца календаря происходит последовательный обход всех отображаемых дат. Для каждой даты выполняется проверка:

  1. Проверяются ограничения minDate и maxDate
  2. Проверяются статические отключения (disableWeekends, disableDayFn и другие флаги)
  3. Вызывается disableDayFn, если он задан
  4. Итоговое состояние дня определяется объединением всех условий

Если хотя бы одно условие возвращает блокировку, день становится неактивным в интерфейсе.

Важно учитывать, что disableDayFn вызывается для каждой отрисовываемой даты, поэтому производительность функции критична при больших диапазонах календаря или сложной логике.


Базовое использование

Наиболее простая реализация позволяет отключать конкретные дни недели или отдельные даты.

new Pikaday({
    field: document.getElementById('input'),
    disableDayFn: function(date) {
        return date.getDay() === 0; // отключение воскресений
    }
});

В данном случае логика эквивалентна disableWeekends, но реализована вручную, что позволяет гибко расширять условия.


Комбинирование с датами и условиями

disableDayFn часто применяется для блокировки нестандартных календарных правил, например исключения праздников или периодов обслуживания.

const disabledDates = [
    new Date(2026, 0, 1),
    new Date(2026, 11, 31)
];

new Pikaday({
    field: document.getElementById('input'),
    disableDayFn: function(date) {
        return disabledDates.some(d =>
            d.getFullYear() === date.getFullYear() &&
            d.getMonth() === date.getMonth() &&
            d.getDate() === date.getDate()
        );
    }
});

Здесь происходит сравнение только календарных компонентов даты, без учета времени, поскольку объект Date может содержать часы, минуты и секунды, влияющие на прямое сравнение.


Приоритет логики относительно других параметров

В системе ограничений календаря disableDayFn не заменяет, а дополняет другие параметры. Типичная схема приоритета выглядит следующим образом:

  • minDate / maxDate — задают жесткий диапазон
  • disableWeekends — отключает фиксированные шаблоны дней
  • disableDayFn — добавляет пользовательскую бизнес-логику

Даже если disableDayFn возвращает false, дата останется недоступной, если она выходит за границы minDate или maxDate.


Работа с временными зонами и датами

При работе с Date в JavaScript необходимо учитывать локальное смещение времени. В контексте календаря Pikaday используются локальные даты, поэтому:

date.getFullYear()
date.getMonth()
date.getDate()

являются предпочтительными методами извлечения компонентов.

Использование toISOString() внутри disableDayFn может привести к смещению даты на один день из-за UTC-формата.


Производительность функции

Так как disableDayFn вызывается для каждой ячейки календаря, даже небольшие оптимизации могут значительно повлиять на скорость отрисовки:

  • избегать создания новых массивов внутри функции
  • минимизировать использование .find, .filter, .some на больших списках
  • заранее нормализовать данные (например, преобразовать список дат в Set-структуру через timestamp)

Оптимизированный вариант:

const blocked = new Set([
    new Date(2026, 0, 1).setHours(0, 0, 0, 0),
    new Date(2026, 11, 31).setHours(0, 0, 0, 0)
]);

new Pikaday({
    field: document.getElementById('input'),
    disableDayFn: function(date) {
        return blocked.has(date.setHours(0, 0, 0, 0));
    }
});

Такой подход снижает вычислительную нагрузку при больших календарях.


Сценарии практического применения

1. Блокировка рабочих дней вне графика

disableDayFn: function(date) {
    const day = date.getDay();
    return day !== 1 && day !== 3 && day !== 5; // только пн, ср, пт
}

2. Ограничение по пользовательским правилам

Например, запрет записи на будущие даты дальше 30 дней:

disableDayFn: function(date) {
    const now = new Date();
    const limit = new Date();
    limit.setDate(now.getDate() + 30);

    return date > limit;
}

3. Интеграция с серверными данными

let serverBlockedDays = [];

fetch('/api/blocked-days')
    .then(res => res.json())
    .then(data => {
        serverBlockedDays = data.map(d => new Date(d));
    });

new Pikaday({
    field: document.getElementById('input'),
    disableDayFn: function(date) {
        return serverBlockedDays.some(d =>
            d.toDateString() === date.toDateString()
        );
    }
});

Особенности взаимодействия с UI-состоянием

Дни, отключённые через disableDayFn, визуально отображаются как неактивные элементы календаря. Они:

  • не реагируют на клики
  • не передают значение в onSelect
  • могут быть стилизованы через CSS-класс is-disabled

Важно учитывать, что внешний стиль не влияет на логику — только возвращаемое значение функции определяет доступность.


Типичные ошибки реализации

Часто встречающиеся проблемы при использовании disableDayFn:

  • сравнение объектов Date через === вместо сравнения компонентов
  • отсутствие нормализации времени (разные часы/минуты)
  • тяжелые вычисления внутри функции
  • изменение внешнего состояния внутри disableDayFn, приводящее к нестабильному рендеру
  • использование асинхронных операций (функция должна быть синхронной)

Расширение логики через композицию

В сложных сценариях disableDayFn становится частью композиции правил:

function isWeekend(date) {
    return date.getDay() === 0 || date.getDay() === 6;
}

function isHoliday(date, holidays) {
    return holidays.includes(date.toDateString());
}

new Pikaday({
    field: document.getElementById('input'),
    disableDayFn: function(date) {
        return isWeekend(date) || isHoliday(date, holidays);
    }
});

Такой подход упрощает сопровождение и позволяет переиспользовать логику вне календаря.


Поведение при изменении состояния

Если список запрещённых дат изменяется динамически, календарь не всегда автоматически перерисовывает состояние. В таких случаях требуется принудительное обновление экземпляра календаря через пересоздание или методы обновления (в зависимости от интеграции). Это связано с тем, что функция disableDayFn не отслеживает реактивные зависимости, а вызывается только в момент отрисовки.