Ограничения на даты

Работа с датами в пользовательских интерфейсах почти всегда требует введения ограничений. Пользователю может быть разрешено выбирать даты только в будущем, только в пределах определённого периода, либо исключая конкретные дни. В интерфейсах календарей, date picker-ов и полей ввода дат такие ограничения играют важную роль в обеспечении корректности данных.

Библиотека React Aria предоставляет встроенные механизмы для задания ограничений при выборе дат. Эти механизмы интегрированы с компонентами календарей, диапазонов дат и полей ввода, а также учитывают требования доступности (ARIA), навигацию с клавиатуры и корректную работу скринридеров.

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

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

Все эти возможности используются в компонентах, построенных на базе хуков React Aria.


Минимальная и максимальная дата

Наиболее распространённый тип ограничения — установка минимальной (minValue) и максимальной (maxValue) допустимых дат.

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

Пример

import {useDatePickerState} from 'react-stately';
import {useDatePicker} from 'react-aria';
import {parseDate} from '@internationalized/date';

let state = useDatePickerState({
  minValue: parseDate('2024-01-01'),
  maxValue: parseDate('2024-12-31')
});

В этом примере:

  • даты до 1 января 2024 недоступны
  • даты после 31 декабря 2024 также недоступны

React Aria автоматически:

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

Визуально такие даты обычно отображаются в календаре как отключённые (disabled).


Ограничение выбора только будущих дат

Частый сценарий — запрет выбора дат в прошлом. Например:

  • бронирование
  • покупка билетов
  • планирование событий

Для этого используется текущая дата как минимальное значение.

Пример

import {today, getLocalTimeZone} from '@internationalized/date';

let state = useDatePickerState({
  minValue: today(getLocalTimeZone())
});

В результате:

  • все даты до текущего дня становятся недоступными
  • текущая дата и будущие даты остаются доступными

Функция today() автоматически учитывает:

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

Это важно для корректной работы международных интерфейсов.


Ограничение выбора только прошедших дат

Обратный сценарий — выбор только прошедших дат. Например:

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

В этом случае текущая дата используется как максимальная.

let state = useDatePickerState({
  maxValue: today(getLocalTimeZone())
});

Теперь пользователь может выбрать:

  • любую дату в прошлом
  • текущий день

Будущие даты становятся недоступными.


Отключение отдельных дат

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

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

Для этого используется функция isDateUnavailable.

Пример

let state = useDatePickerState({
  isDateUnavailable: (date) => {
    return date.day === 13;
  }
});

В данном случае:

  • каждое 13 число месяца становится недоступным

Функция получает объект даты и возвращает:

  • true — дата недоступна
  • false — дата доступна

Отключение выходных дней

Один из распространённых сценариев — запрет выбора выходных.

let state = useDatePickerState({
  isDateUnavailable: (date) => {
    let day = date.toDate(getLocalTimeZone()).getDay();
    return day === 0 || day === 6;
  }
});

Здесь:

  • 0 — воскресенье
  • 6 — суббота

Таким образом:

  • рабочие дни доступны
  • выходные отключены

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

React Aria поддерживает компоненты выбора диапазона дат. В этом случае ограничения применяются сразу к двум значениям:

  • начальной дате
  • конечной дате

Пример

import {useDateRangePickerState} from 'react-stately';

let state = useDateRangePickerState({
  minValue: parseDate('2024-01-01'),
  maxValue: parseDate('2024-12-31')
});

Диапазон может находиться только внутри заданных границ.

Если пользователь пытается выбрать дату за пределами диапазона:

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

Ограничение длины диапазона

Иногда требуется ограничить максимальную длину диапазона. Например:

  • бронирование не более 14 дней
  • отпуск не более 30 дней

Это можно реализовать через isDateUnavailable.

let state = useDateRangePickerState({
  isDateUnavailable: (date, selectedRange) => {
    if (selectedRange?.start) {
      let diff = date.compare(selectedRange.start);
      return diff > 14;
    }
    return false;
  }
});

Здесь:

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

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

Иногда ограничения зависят от выбранной даты.

Например:

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

В таких случаях ограничения вычисляются динамически.

Пример

let state = useDateRangePickerState({
  isDateUnavailable: (date, selectedRange) => {
    if (selectedRange?.start && date.compare(selectedRange.start) < 0) {
      return true;
    }

    return false;
  }
});

В этом случае:

  • конечная дата не может быть раньше начальной

Ограничение месяцев и годов

В календарях React Aria навигация по месяцам также учитывает ограничения.

Если установлены:

minValue
maxValue

то:

  • недопустимые месяцы нельзя открыть
  • кнопки навигации блокируются
  • фокус не перемещается в запрещённые области

Например:

minValue: 2024-05-01

Пользователь не сможет перейти к:

  • январю
  • февралю
  • марту
  • апрелю

Работа с календарными системами

React Aria использует библиотеку @internationalized/date, которая поддерживает:

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

Это означает, что ограничения дат работают корректно для:

  • григорианского календаря
  • исламского календаря
  • японского календаря
  • других систем

Все операции сравнения выполняются через методы объектов даты.

Пример сравнения

date.compare(otherDate)

Метод возвращает:

  • отрицательное число — дата раньше
  • 0 — даты равны
  • положительное число — дата позже

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


Отображение недоступных дат

Когда дата недоступна:

  • она получает состояние aria-disabled
  • визуально отображается как неактивная
  • не участвует в таб-навигации
  • не может быть выбрана клавиатурой

React Aria автоматически управляет этими состояниями.

Пример разметки:

<td aria-disabled="true">

Скринридеры корректно объявляют такие элементы как недоступные.


Валидация ввода даты

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

Например:

<DateField
  minValue={parseDate('2024-01-01')}
  maxValue={parseDate('2024-12-31')}
/>

Если пользователь вводит дату вручную:

  • вне допустимого диапазона
  • или недоступную дату

React Aria:

  • помечает поле как невалидное
  • добавляет aria-invalid
  • предотвращает подтверждение значения

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

Ограничения можно комбинировать.

Пример:

let state = useDatePickerState({
  minValue: today(getLocalTimeZone()),
  maxValue: parseDate('2024-12-31'),
  isDateUnavailable: (date) => {
    let day = date.toDate(getLocalTimeZone()).getDay();
    return day === 0;
  }
});

В этом случае:

  • доступны только будущие даты
  • не позже конца года
  • воскресенье отключено

React Aria объединяет все ограничения и применяет их одновременно.


Ограничения и доступность

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

При наличии ограничений:

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

Это позволяет пользователям с ограниченными возможностями корректно взаимодействовать с календарями и полями дат.


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

Ограничения на даты применяются в различных интерфейсах:

Бронирование

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

Регистрация

  • ограничение даты рождения
  • запрет будущих дат

Планирование

  • события не раньше текущего дня
  • ограничение диапазона проекта

Финансовые операции

  • выбор дат только внутри отчётного периода

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