Паттерны
Календарь бронирования
Когда использовать:
пользователь выбирает период проживания или аренды на видимом календаре, и некоторые дни нельзя забронировать.
Заметки по композиции:
Используйте selection='range' с min для первого доступного для бронирования дня, max для конца окна бронирования и предикатом disabledDates для недоступных дней. Предикат получает ISO-дни, поэтому может искать их напрямую в данных о доступности. Диапазон может охватывать отключённый день, поэтому проверяйте диапазон, прежде чем принять его (см. Ошибка: диапазон через заблокированный день); здесь одно сообщение, связанное с сеткой дней, показывает подсказку, ошибку или сводку, а кнопка «Reserve» ждёт корректного диапазона.
Компромиссы:
Пользователь видит доступность до выбора, но календарь занимает гораздо больше места, чем поле даты, а проверка диапазона остаётся на приложении.
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 не пересоздавал ячейки при каждом рендере.
import {
Calendar,
CalendarContent,
CalendarDay,
CalendarDays,
CalendarHeader,
CalendarMonth,
CalendarMonthLabel,
CalendarMonths,
CalendarNextButton,
CalendarPrevButton,
CalendarViewButton,Шаблоны дней недели, месяцев и годов
Когда использовать:
заголовкам дней недели, месяцам или годам нужен собственный вид: приглушённые выходные, кварталы у месяцев или отмеченные високосные годы.
Заметки по композиции:
Дочерние элементы CalendarWeekdays, CalendarMonths и CalendarYears — шаблоны, как и ячейка дня. Читайте элемент через useCalendarWeekdayContext(), useCalendarMonthContext() или useCalendarYearContext() и рендерите CalendarWeekday, CalendarMonth или CalendarYear, чтобы элемент сохранил поведение по умолчанию.
Компромиссы:
Каждое представление может нести свою подсказку, но чисто визуальные отметки вроде цвета или жирного шрифта не озвучиваются; в примере квартал и високосный год повторены в aria-label элемента.
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 среды выполнения.
import { createCalendarSystem } from '@vega-ui/i18n';
import {
Calendar,
CalendarContent,
CalendarDay,
CalendarDays,
CalendarHeader,
CalendarMonth,
CalendarMonthLabel,
CalendarMonths,
CalendarNextButton,
CalendarPrevButton,Одна дата в двух календарях
Когда использовать:
пользователю нужно видеть дату сразу в двух календарных системах, например в григорианском календаре и календаре хиджры в туристической или государственной форме.
Заметки по композиции:
Используйте одно ISO-значение в двух контролируемых корнях с разными calendar и locale. Выбор дня в любом из календарей выбирает тот же день в другом. Каждый корень хранит свой видимый месяц. Здесь корни показывают только дни с простой подписью месяца, а арабский календарь — с dir='rtl'.
Компромиссы:
Никакого кода преобразования в приложении, но два календаря занимают вдвое больше места; на узких экранах лучше показывать один календарь, а другую дату выводить текстом через formatCalendarDate.
import { createCalendarSystem } from '@vega-ui/i18n';
import {
Calendar,
CalendarContent,
CalendarDay,
CalendarDays,
CalendarHeader,
CalendarMonthLabel,
CalendarNextButton,
CalendarPrevButton,
CalendarWeekday,
CalendarWeekdays,Локаль для всего приложения
Когда использовать:
все календари приложения используют одну локаль или календарную систему.
Заметки по композиции:
В библиотеке нет глобальных провайдеров. Один раз оберните Calendar с настройками приложения и частями и используйте обёртку везде; пропсы, переданные обёртке, по-прежнему имеют приоритет, как locale='fr-FR' у второго календаря.
Компромиссы:
Одно место для изменения настроек, но по дополнительному компоненту на каждый корень дат, который использует приложение.
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 - возвращайте пользовательские части постепенно