Состояния pressed и disabled

React Aria предоставляет мощный инструмент для создания доступных и интерактивных компонентов, управляя их состояниями без необходимости вручную отслеживать низкоуровневые события. Два ключевых состояния, с которыми часто приходится работать при разработке кнопок, чекбоксов, переключателей и других интерактивных элементов, — это pressed и disabled.


Состояние pressed

Состояние pressed отражает факт активного взаимодействия пользователя с элементом. Оно особенно актуально для кнопок и элементов, поддерживающих режим «toggle» (переключатель). React Aria предоставляет хук usePress, который упрощает обработку всех событий нажатия и касания, а также управления фокусом и доступностью.

Основные особенности usePress:
  • Поддержка всех типов устройств: мышь, клавиатура, сенсорные экраны.
  • Обработка событий onPressStart, onPressEnd, onPress и onPressChange.
  • Генерация корректных атрибутов ARIA для взаимодействия с вспомогательными технологиями.

Пример использования usePress для кнопки:

import { usePress } from '@react-aria/interactions';
import { useRef, useState } from 'react';

function PressableButton(props) {
  const ref = useRef();
  const [isPressed, setIsPressed] = useState(false);
  const { pressProps } = usePress({
    onPressStart: () => setIsPressed(true),
    onPressEnd: () => setIsPressed(false),
    onPress: () => console.log('Кнопка нажата')
  }, ref);

  return (
    <button
      {...pressProps}
      ref={ref}
      style={{
        background: isPressed ? 'darkblue' : 'blue',
        color: 'white',
        padding: '10px 20px',
        border: 'none',
        borderRadius: '4px',
        cursor: 'pointer'
      }}
    >
      {isPressed ? 'Нажата' : 'Нажми меня'}
    </button>
  );
}

В этом примере isPressed автоматически обновляется при взаимодействии пользователя, и элемент получает визуальное подтверждение нажатия.

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


Состояние disabled

Состояние disabled делает элемент недоступным для взаимодействия. В React Aria оно реализуется через хуки, такие как useButton или useToggleButton, которые автоматически добавляют атрибут aria-disabled и предотвращают вызов событий нажатия.

Пример кнопки с состоянием disabled:
import { useButton } from '@react-aria/button';
import { useRef } from 'react';

function DisabledButton({ isDisabled }) {
  const ref = useRef();
  const { buttonProps } = useButton({ isDisabled }, ref);

  return (
    <button
      {...buttonProps}
      ref={ref}
      style={{
        background: isDisabled ? 'gray' : 'green',
        color: 'white',
        padding: '10px 20px',
        border: 'none',
        borderRadius: '4px',
        cursor: isDisabled ? 'not-allowed' : 'pointer'
      }}
    >
      {isDisabled ? 'Отключено' : 'Активно'}
    </button>
  );
}

Здесь useButton автоматически управляет:

  • Блокировкой кликов и клавиатурных событий.
  • Установкой aria-disabled для поддержки вспомогательных технологий.
  • Сохранением правильного фокуса и визуального состояния.

Комбинирование состояний pressed и disabled

При разработке интерактивных элементов часто требуется учитывать оба состояния одновременно. React Aria позволяет комбинировать хуки usePress и useButton для обработки этих случаев без конфликтов.

import { useButton } from '@react-aria/button';
import { usePress } from '@react-aria/interactions';
import { useRef, useState } from 'react';

function InteractiveButton({ isDisabled }) {
  const ref = useRef();
  const [isPressed, setIsPressed] = useState(false);

  const { buttonProps } = useButton({ isDisabled }, ref);
  const { pressProps } = usePress({
    onPressStart: () => setIsPressed(true),
    onPressEnd: () => setIsPressed(false)
  }, ref);

  return (
    <button
      {...buttonProps}
      {...pressProps}
      ref={ref}
      style={{
        background: isDisabled ? 'gray' : isPressed ? 'darkred' : 'red',
        color: 'white',
        padding: '10px 20px',
        border: 'none',
        borderRadius: '4px',
        cursor: isDisabled ? 'not-allowed' : 'pointer'
      }}
    >
      {isDisabled ? 'Отключено' : isPressed ? 'Нажато' : 'Нажми меня'}
    </button>
  );
}

В этой конструкции:

  • isDisabled полностью блокирует взаимодействие и визуально сигнализирует пользователю, что кнопка неактивна.
  • isPressed отслеживает активное взаимодействие при условии, что элемент доступен.
  • Комбинация buttonProps и pressProps гарантирует корректную обработку всех событий и доступность.

Важные нюансы и рекомендации

  1. Использование ARIA-атрибутов: Всегда доверять хукам React Aria для установки aria-pressed и aria-disabled вместо ручного добавления атрибутов, чтобы обеспечить совместимость со скринридерами.
  2. Визуальные состояния: Состояния pressed и disabled должны быть отчетливо видимы для всех пользователей. Контраст, тень или цвет — эффективные способы сигнализировать состояние.
  3. События клавиатуры: React Aria автоматически обрабатывает нажатия клавиш Enter и Space, поэтому элементы остаются доступными при использовании клавиатуры.
  4. Комбинированные компоненты: Для сложных компонентов, например, кастомных чекбоксов или переключателей, usePress и useButton можно использовать совместно для синхронизации состояний и управления доступностью.

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