Динамическое изменение ограничений

Базовая модель ограничений и их роль в рантайме

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

Ключевые механизмы ограничений:

  • минимальная дата (minDate)
  • максимальная дата (maxDate)
  • функция отключения дат (disableDayFn)
  • выбор режима отображения недель и локализации, влияющий на интерпретацию дат

Эти параметры не фиксируются навсегда при создании экземпляра и могут быть изменены через API или прямое обновление свойств с последующим пересчётом состояния календаря.


Изменение диапазона дат во время работы календаря

Наиболее распространённый сценарий динамических ограничений — изменение допустимого диапазона выбора.

Установка минимальной даты

Минимальная дата ограничивает выбор слева от календаря:

picker.setMinDate(new Date(2026, 0, 10));

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

Внутренне происходит:

  • обновление состояния экземпляра
  • пересчёт доступных дней
  • перерисовка текущего месяца при необходимости

Установка максимальной даты

Аналогично задаётся верхняя граница:

picker.setMaxDate(new Date(2026, 11, 31));

При этом все даты позже указанной становятся недоступными для выбора и визуально блокируются.


Синхронизация min/max при зависимых полях

Частый сценарий — два связанных календаря (например, дата начала и дата окончания периода). При изменении одной даты необходимо динамически корректировать ограничения второй.

Пример: дата начала влияет на дату окончания

startPicker.on('select', function () {
    endPicker.setMinDate(this.getDate());
});

Логика:

  • выбранная дата начала становится минимально допустимой для окончания
  • предотвращается выбор неконсистентных диапазонов

Обратная зависимость

endPicker.on('select', function () {
    startPicker.setMaxDate(this.getDate());
});

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


Полная динамическая перенастройка диапазона

В некоторых случаях ограничения формируются на основе внешних данных: ответа API, пользовательских прав, календаря занятости.

fetch('/api/availability')
  .then(res => res.json())
  .then(data => {
      picker.setMinDate(new Date(data.min));
      picker.setMaxDate(new Date(data.max));
  });

Особенность такого подхода:

  • календарь может быть уже открыт
  • ограничения применяются без пересоздания экземпляра
  • UI обновляется синхронно с новыми правилами

Работа с отключёнными датами через disableDayFn

Помимо диапазонов используется функция постраничного контроля доступности дат.

const picker = new Pikaday({
    disableDayFn: function (date) {
        return date.getDay() === 0 || date.getDay() === 6;
    }
});

Здесь логика определяет, что выходные недоступны.

Динамическое изменение правила

Хотя прямого сеттера для disableDayFn нет, поведение можно менять через замену функции и перерисовку:

picker.config.disableDayFn = function (date) {
    return blockedDates.includes(date.toISOString().slice(0, 10));
};

picker.draw();

Ключевой момент:

  • изменение конфигурации требует принудительного обновления отображения через draw()

Принудительное обновление интерфейса после изменения ограничений

При динамическом изменении параметров важно обеспечить актуализацию UI.

Используются методы:

picker.draw();
picker.gotoToday();

draw() пересобирает текущую визуальную сетку календаря, учитывая новые ограничения.


Зависимость ограничений от состояния приложения

В сложных интерфейсах ограничения часто зависят от состояния формы:

  • выбранный тип услуги
  • регион пользователя
  • доступность ресурсов
  • тарифные ограничения

Пример:

select.addEventListener('change', function () {
    if (this.value === 'premium') {
        picker.setMinDate(new Date(2026, 0, 1));
        picker.setMaxDate(new Date(2026, 11, 31));
    } else {
        picker.setMinDate(new Date());
        picker.setMaxDate(null);
    }
});

Особенности:

  • null снимает ограничение
  • пересчёт происходит мгновенно
  • состояние календаря всегда соответствует бизнес-логике

Взаимодействие динамических ограничений с выбранной датой

При изменении ограничений важно учитывать уже выбранную дату. Pikaday не всегда автоматически сбрасывает некорректное значение, поэтому применяется дополнительная проверка:

const selected = picker.getDate();

picker.setMinDate(new Date(2026, 5, 1));

if (selected && selected < picker.getMinDate()) {
    picker.setDate(null);
}

Это предотвращает ситуацию, когда текущий выбор оказывается вне допустимого диапазона.


Обновление ограничений без потери состояния месяца

При изменении min/max календарь может сохранять текущий месяц, если он остаётся валидным. Это поведение важно для UX:

  • пользователь не теряет контекст
  • интерфейс не “прыгает” без необходимости

Пример сценария:

  • пользователь листает календарь на июль
  • изменяется maxDate на август
  • календарь остаётся в июле, если он валиден

Комбинирование динамических ограничений

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

  • диапазон (minDate, maxDate)
  • фильтр (disableDayFn)
  • программное управление выбранной датой
  • внешние события (API, WebSocket)

Пример комплексной логики:

function applyRules(rules) {
    picker.setMinDate(new Date(rules.min));
    picker.setMaxDate(new Date(rules.max));

    picker.config.disableDayFn = function (date) {
        return rules.disabled.includes(date.toISOString().split('T')[0]);
    };

    picker.draw();
}

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


Производительность при частых изменениях ограничений

При интенсивных обновлениях важно учитывать стоимость перерисовки:

  • setMinDate и setMaxDate триггерят перерасчёт
  • частые вызовы draw() могут быть дорогими
  • лучше группировать изменения

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

picker.setMinDate(min);
picker.setMaxDate(max);
picker.config.disableDayFn = fn;
picker.draw();

Вместо:

picker.setMinDate(min);
picker.draw();
picker.setMaxDate(max);
picker.draw();

Поведение при снятии ограничений

Снятие ограничений реализуется через null:

picker.setMinDate(null);
picker.setMaxDate(null);

Это возвращает календарь в состояние полного диапазона, но сохраняет остальные правила, включая disableDayFn.


Влияние локализации на отображение ограничений

Хотя ограничения не зависят напрямую от локализации, визуальное восприятие может меняться:

  • первый день недели
  • формат отображения дат
  • порядок сетки

При динамическом изменении ограничений важно учитывать, что пересчёт сетки всегда использует текущие локализационные настройки экземпляра.