Перейти к содержимому
v2.5.0
English
Русский
Документация

Calendar

Calendar — продвинутый интерактивный составной компонент для согласованного выбора даты в видах дней, месяцев и лет.

Паттерны

Календарь бронирования

Когда использовать:

пользователь выбирает период проживания или аренды на видимом календаре, и некоторые дни нельзя забронировать.

Заметки по композиции:

Используйте selection='range' с min для первого доступного для бронирования дня, max для конца окна бронирования и предикатом disabledDates для недоступных дней. Предикат получает ISO-дни, поэтому может искать их напрямую в данных о доступности. Диапазон может охватывать отключённый день, поэтому проверяйте диапазон, прежде чем принять его (см. Ошибка: диапазон через заблокированный день); здесь одно сообщение, связанное с сеткой дней, показывает подсказку, ошибку или сводку, а кнопка «Reserve» ждёт корректного диапазона.

Компромиссы:

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

booking-calendar.tsx · 97 строк
import {
  Button,
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,

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

Когда использовать:

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

Заметки по композиции:

Дочерние элементы CalendarWeeks — шаблон ячейки дня: они рендерятся по одному разу на день внутри контекста ячейки. Читайте день через useCalendarDayContext() и размещайте содержимое внутри CalendarDay, чтобы ячейка сохранила поведение выбора, фокуса, отключения и соседних месяцев. То же работает для CalendarMonths с CalendarMonth и useCalendarMonthContext() и для CalendarYears с CalendarYear и useCalendarYearContext().

Контекст дня — { col, date, fields, label, inMonth }; date равен undefined для пустой ячейки соседнего месяца. Берите номер дня из label или fields.day, а не из ISO-строки date: в негригорианских календарях они различаются. Ищите данные по date — он одинаков в любом календаре.

Компромиссы:

Пользователь сравнивает дни с одного взгляда, но на маленьких размерах ячейки перегружаются (в примере — size='md'), а дополнительное содержимое не озвучивается, если не входит в текст ячейки. Объявляйте компонент шаблона вне рендера, чтобы React не пересоздавал ячейки при каждом рендере.

day-cell-custom-rendering.tsx · 85 строк
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,

Шаблоны дней недели, месяцев и годов

Когда использовать:

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

Заметки по композиции:

Дочерние элементы CalendarWeekdays, CalendarMonths и CalendarYears — шаблоны, как и ячейка дня. Читайте элемент через useCalendarWeekdayContext(), useCalendarMonthContext() или useCalendarYearContext() и рендерите CalendarWeekday, CalendarMonth или CalendarYear, чтобы элемент сохранил поведение по умолчанию.

Компромиссы:

Каждое представление может нести свою подсказку, но чисто визуальные отметки вроде цвета или жирного шрифта не озвучиваются; в примере квартал и високосный год повторены в aria-label элемента.

weekday-month-year-templates.tsx · 88 строк
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,

Негригорианский календарь

Когда использовать:

аудитория продукта пользуется персидским, еврейским, японским, буддийским, исламским или другим календарём.

Заметки по композиции:

Создайте календарь один раз через createCalendarSystem вне рендера (см. Даты и календарные системы) и передайте его в calendar вместе с подходящим locale. Длина месяцев, их количество, эры и подписи следуют календарю; value, min, max и disabledDates остаются ISO-днями YYYY-MM-DD. Calendar выставляет lang из locale, но не dir: для локали с письмом справа налево, например fa-IR, задайте dir='rtl' на корне или предке — кнопки навигации и шевроны развернутся вместе с ним.

Компромиссы:

Нативная длина месяцев, високосные месяцы и эры без изменения хранимого значения и без новых зависимостей; данные календаря берутся из Intl среды выполнения.

non-gregorian-calendar.tsx · 61 строка
import { createCalendarSystem } from '@vega-ui/i18n';
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,

Одна дата в двух календарях

Когда использовать:

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

Заметки по композиции:

Используйте одно ISO-значение в двух контролируемых корнях с разными calendar и locale. Выбор дня в любом из календарей выбирает тот же день в другом. Каждый корень хранит свой видимый месяц. Здесь корни показывают только дни с простой подписью месяца, а арабский календарь — с dir='rtl'.

Компромиссы:

Никакого кода преобразования в приложении, но два календаря занимают вдвое больше места; на узких экранах лучше показывать один календарь, а другую дату выводить текстом через formatCalendarDate.

one-date-in-two-calendars.tsx · 65 строк
import { createCalendarSystem } from '@vega-ui/i18n';
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonthLabel,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarWeekday,
  CalendarWeekdays,

Локаль для всего приложения

Когда использовать:

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

Заметки по композиции:

В библиотеке нет глобальных провайдеров. Один раз оберните Calendar с настройками приложения и частями и используйте обёртку везде; пропсы, переданные обёртке, по-прежнему имеют приоритет, как locale='fr-FR' у второго календаря.

Компромиссы:

Одно место для изменения настроек, но по дополнительному компоненту на каждый корень дат, который использует приложение.

app-wide-locale.tsx · 64 строки
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,

Сравнение

Быстрое правило выбора

Выбирайте Calendar, когда пользователю нужен полноценный визуальный выбор даты, а не простое поле для ввода.

Calendar или DateField

Используйте Calendar, когда:

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

Используйте DateField, когда:

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

Calendar или DateRangeField

Используйте Calendar, когда:

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

Используйте DateRangeField, когда:

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

Calendar или DayPicker

Используйте Calendar, когда:

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

Используйте DayPicker, когда:

  • нужно только поведение сетки дней

Calendar или MonthPicker / YearPicker

Используйте Calendar, когда:

  • пользователь должен переходить между видами дней, месяцев и лет в одном согласованном элементе управления

Используйте низкоуровневые компоненты выбора, когда:

  • UX нужна только одна из сеток: дней, месяцев или лет

Выбирайте этот компонент, когда

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

Не выбирайте этот компонент, когда

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

Решение проблем

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

Симптом

Компонент рендерится, но форма состояния в интеграции не совпадает с ожидаемой.

Вероятная причина

Режим selection изменили, не обновив модель значения в родителе.

Как проверить

  • проверьте selection
  • проверьте форму текущего value

Исправление

  • рассматривайте single, multiple и range как разные контракты значения

Отключённые даты по-прежнему выглядят доступными

Симптом

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

Вероятная причина

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

Как проверить

  • сравните пользовательский шаблон с обычным <CalendarDay />
  • проверьте взаимодействие и указателем, и с клавиатуры

Исправление

  • рендерите пользовательское содержимое внутри CalendarDay, который обрабатывает отключённое и исключённое состояния

Пользовательское содержимое календаря ломает навигацию

Симптом

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

Вероятная причина

Виды или элементы управления в заголовке пересобраны так, как будто это несвязанные компоненты.

Как проверить

  • проверьте переключение видов в реальной кастомизированной композиции

Исправление

  • сначала вернитесь к стандартной структуре CalendarHeader плюс CalendarContent
  • возвращайте пользовательские части постепенно