Очистка диапазона

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

  • startDate — начало интервала
  • endDate — конец интервала

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

Ключевой особенностью становится необходимость учитывать не только визуальное состояние, но и внутренние ограничения (minDate / maxDate), а также возможные побочные эффекты событий onSelect.


Модель хранения диапазона

Типичная реализация диапазона строится вокруг двух инстансов:

const startPicker = new Pikaday({
    field: document.getElementById('start'),
    onSelect: function(date) {
        range.start = date;
        syncRange();
    }
});

const endPicker = new Pikaday({
    field: document.getElementById('end'),
    onSelect: function(date) {
        range.end = date;
        syncRange();
    }
});

const range = {
    start: null,
    end: null
};

Очистка диапазона в такой архитектуре означает приведение к начальному состоянию:

  • обнуление range.start и range.end
  • очистка значений input
  • сброс ограничений календарей
  • обновление UI-состояния выделения

Базовые стратегии очистки диапазона

Существует несколько устойчивых подходов к очистке диапазона дат в Pikaday-реализациях.

Полное сбрасывание состояния

Наиболее прямолинейный способ — ручное обнуление всех связанных значений:

function clearRange() {
    range.start = null;
    range.end = null;

    document.getElementById('start').value = '';
    document.getElementById('end').value = '';

    startPicker.setDate(null);
    endPicker.setDate(null);
}

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


Очистка через пересоздание инстансов

В сложных интерфейсах применяется радикальный способ — уничтожение и пересоздание календарей:

function resetPickers() {
    startPicker.destroy();
    endPicker.destroy();

    initializePickers();
}

Этот подход используется, когда:

  • сложная логика min/maxDate накопила противоречивое состояние
  • требуется гарантированно чистый UI
  • присутствуют кастомные плагины, изменяющие поведение рендера

Минусом является потеря текущих подписок и необходимость восстановления всех конфигураций.


Очистка с учётом логики диапазона

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

Сброс ограничений minDate и maxDate

При выборе диапазона часто применяется динамическая блокировка дат:

startPicker.setMaxDate(null);
endPicker.setMinDate(null);

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

Корректная последовательность:

function clearRange() {
    range.start = null;
    range.end = null;

    startPicker.setDate(null);
    endPicker.setDate(null);

    startPicker.setMaxDate(null);
    endPicker.setMinDate(null);

    startPicker.gotoToday();
    endPicker.gotoToday();
}

Метод gotoToday() используется для возврата календаря в нейтральное состояние отображения, предотвращая визуальную «залипшую» навигацию на старых месяцах.


Очистка одного конца диапазона

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

Сброс endDate

function clearEnd() {
    range.end = null;
    endPicker.setDate(null);

    startPicker.setMaxDate(null);
}

Это важно в сценариях, где:

  • пользователь заново выбирает конец диапазона
  • требуется расширение уже выбранного интервала
  • endDate зависит от startDate, но не наоборот

Сброс startDate

function clearStart() {
    range.start = null;
    startPicker.setDate(null);

    endPicker.setMinDate(null);
}

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


Очистка через событие onSelect

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

Защита от рекурсивных обновлений

let isClearing = false;

function clearRange() {
    isClearing = true;

    range.start = null;
    range.end = null;

    startPicker.setDate(null);
    endPicker.setDate(null);

    isClearing = false;
}

И в обработчиках:

onSelect: function(date) {
    if (isClearing) return;

    range.start = date;
    syncRange();
}

Такой механизм предотвращает каскадное обновление состояния.


Очистка визуального выделения диапазона

Если диапазон визуально подсвечивается через CSS-классы, очистка должна затрагивать DOM-слой.

Типичная схема подсветки

function updateHighlight() {
    const cells = document.querySelectorAll('.pika-day');

    cells.forEach(cell => {
        cell.classList.remove('is-in-range');
        cell.classList.remove('is-range-start');
        cell.classList.remove('is-range-end');
    });
}

Очистка подсветки

function clearHighlight() {
    updateHighlight();
}

В более сложных реализациях очистка выполняется через перерисовку календаря:

startPicker.draw();
endPicker.draw();

Очистка input-полей и синхронизация UI

Даже при корректном сбросе Pikaday-инстансов остаётся проблема синхронизации текстовых полей.

function clearInputs() {
    document.getElementById('start').value = '';
    document.getElementById('end').value = '';
}

При форматировании дат через кастомный toString или moment/dayjs-адаптер важно также сбрасывать форматированные значения, иначе визуально поле может оставаться заполненным.


Очистка через единый контроллер состояния

На практике удобнее централизовать очистку диапазона в одном контроллере:

const rangeController = {
    clear() {
        this.state = { start: null, end: null };

        startPicker.setDate(null);
        endPicker.setDate(null);

        startPicker.setMinDate(null);
        startPicker.setMaxDate(null);

        endPicker.setMinDate(null);
        endPicker.setMaxDate(null);

        document.getElementById('start').value = '';
        document.getElementById('end').value = '';

        startPicker.draw();
        endPicker.draw();
    },

    state: {
        start: null,
        end: null
    }
};

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


Очистка при внешних событиях

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

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

Пример:

document.getElementById('reset').addEventListener('click', () => {
    rangeController.clear();
});

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


Поведение при частично неконсистентных данных

Типичная проблема возникает, когда:

  • startDate есть, endDate отсутствует
  • или наоборот
  • или значения выходят за пределы min/max ограничений

Очистка в таких случаях должна нормализовать состояние:

function normalizeBeforeClear() {
    if (range.start && range.end && range.start > range.end) {
        range.end = null;
    }
}

После нормализации выполняется стандартный сброс.


Сброс состояния навигации календаря

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

function resetView() {
    startPicker.gotoToday();
    endPicker.gotoToday();
}

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

startPicker.gotoDate(new Date());

Итоговая структура очистки диапазона

Обобщённая последовательность очистки в диапазонной реализации:

function clearDateRange() {
    range.start = null;
    range.end = null;

    startPicker.setDate(null);
    endPicker.setDate(null);

    startPicker.setMinDate(null);
    startPicker.setMaxDate(null);
    endPicker.setMinDate(null);
    endPicker.setMaxDate(null);

    document.getElementById('start').value = '';
    document.getElementById('end').value = '';

    startPicker.draw();
    endPicker.draw();
}

Эта структура покрывает:

  • внутреннее состояние
  • UI-слой
  • ограничения календарей
  • визуальное обновление
  • синхронизацию инстансов