Отображение нескольких месяцев

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

Основной механизм, отвечающий за мульти-месячный режим, задаётся параметром конфигурации numberOfMonths. Именно он определяет, сколько календарных сеток будет отрисовано одновременно в одном экземпляре виджета.

const picker = new Pikaday({
    field: document.getElementById('date'),
    numberOfMonths: 3
});

При значении 3 интерфейс календаря формирует три независимых, но синхронизированных блока месяцев, расположенных горизонтально. Каждый блок отображает собственную сетку дней, однако навигационные действия (перелистывание вперёд или назад) применяются ко всем месяцам одновременно, сохраняя их относительное смещение.


Внутренняя модель рендеринга нескольких месяцев

При активации numberOfMonths > 1 Pikaday использует единый внутренний объект состояния текущего месяца (currentMonth и currentYear) как базовую точку отсчёта. Дополнительные месяцы вычисляются смещением:

  • первый календарь: базовый месяц
  • второй календарь: +1 месяц
  • третий календарь: +2 месяца
  • и так далее

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

Визуально каждый месяц является отдельным DOM-блоком, но логически они объединены общими обработчиками событий и состоянием выбора дат.


Управление шагом навигации

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

Пример: если отображается 3 месяца (март, апрель, май), то при переходе вперёд набор станет (апрель, май, июнь).

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


Стилизация много-месячного режима

Отрисовка нескольких месяцев создаёт горизонтальную структуру, где каждый календарь помещается в контейнер .pika-lendar. Pikaday автоматически группирует их в общий wrapper .pika-multiple-months.

Базовая CSS-модель предполагает inline-block или flex-раскладку.

Пример адаптации под flex:

.pika-multiple-months {
    display: flex;
    gap: 16px;
}

.pika-lendar {
    width: 280px;
}

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


Адаптация под разные разрешения экрана

Мульти-месячный режим требует учёта ограниченного пространства на мобильных устройствах. При фиксированном значении numberOfMonths: 2 или 3 интерфейс может выходить за пределы экрана.

Типичная стратегия адаптации — динамическое изменение количества отображаемых месяцев:

function getMonthsCount() {
    return window.innerWidth < 600 ? 1 : 2;
}

const picker = new Pikaday({
    field: document.getElementById('date'),
    numberOfMonths: getMonthsCount()
});

Дополнительно возможно обновление параметра при ресайзе окна с пересозданием экземпляра календаря или через переинициализацию рендера.


Синхронизация выбора дат в нескольких месяцах

Все месяцы в одном экземпляре Pikaday работают с единым состоянием выбранной даты (selectedDate). Это означает, что выбор даты в любом из отображаемых месяцев влияет на общий state.

При использовании диапазонных сценариев (start/end date) мульти-месячный режим становится особенно полезным, так как позволяет визуально охватывать несколько месяцев без переключения.

Пример интеграции диапазона:

let startDate = null;
let endDate = null;

const picker = new Pikaday({
    field: document.getElementById('range'),
    numberOfMonths: 2,
    onSelect: function () {
        const selected = this.getDate();

        if (!startDate || (startDate && endDate)) {
            startDate = selected;
            endDate = null;
        } else {
            endDate = selected;
        }
    }
});

Ограничения дат в мульти-месячном режиме

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

const picker = new Pikaday({
    field: document.getElementById('date'),
    numberOfMonths: 3,
    minDate: new Date(2026, 0, 1),
    maxDate: new Date(2026, 11, 31)
});

Недоступные даты автоматически получают CSS-класс is-disabled, независимо от того, в каком из месяцев они отображаются.


Производительность при большом количестве месяцев

Увеличение numberOfMonths напрямую влияет на количество создаваемых DOM-элементов. Каждый дополнительный месяц добавляет:

  • таблицу дней (обычно 42 ячейки)
  • заголовок дней недели
  • обработчики событий наведения и клика

При значениях выше 3–4 возможно снижение производительности в старых браузерах из-за увеличенного количества событий и перерасчётов layout.

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


Кастомизация отображения заголовков месяцев

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

const picker = new Pikaday({
    field: document.getElementById('date'),
    numberOfMonths: 2,
    i18n: {
        previousMonth: 'Предыдущий месяц',
        nextMonth: 'Следующий месяц',
        months: [
            'Январь','Февраль','Март','Апрель','Май','Июнь',
            'Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'
        ]
    }
});

Каждый из месяцев отображает свой заголовок, но структура навигации остаётся общей.


Поведение при переходе между годами

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

  • декабрь 2026
  • январь 2027
  • февраль 2027

Это происходит за счёт встроенной нормализации даты в JavaScript Date, используемой внутри Pikaday.


Комбинация с режимом выбора диапазона

Мульти-месячное отображение часто применяется совместно с логикой range picker. В таком случае важны два аспекта:

  1. визуальное покрытие диапазона
  2. минимизация прокрутки

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


Поведение при кастомном позиционировании

При использовании bound: false и ручного позиционирования календаря через CSS или JavaScript мульти-месячный режим требует дополнительного контроля ширины контейнера.

const picker = new Pikaday({
    field: document.getElementById('date'),
    numberOfMonths: 3,
    bound: false,
    container: document.getElementById('calendar-container')
});

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


Взаимодействие с клавиатурной навигацией

Клавиатурное управление (стрелки, PageUp, PageDown) работает в рамках общего календарного состояния. При этом фокус остаётся в пределах активной ячейки, но при достижении границ текущего месяца может автоматически переходить в соседний отображаемый месяц.

Это поведение особенно заметно при numberOfMonths > 1, где логическая сетка становится непрерывной по горизонтали.


Особенности кастомных тем

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

  • единая ширина .pika-lendar
  • синхронизированные отступы заголовков
  • одинаковая высота сетки дней

Любые расхождения в CSS могут привести к визуальному «разъезжанию» календарей, особенно при разных локализациях, где длина названий месяцев отличается.