GrRadio

  • ядро
  • Формы
  • API · 15 props
  • ⌨ клавиши

Берут, когда переключатель стоит отдельно.

import { GrRadio } from '@feugene/granularity/components/GrRadio'

Обзор

Когда брать

  • переключатель стоит отдельно — вариант в строке таблицы, в карточке, в ячейке списка: группы вокруг него нет;
  • раскладку задаёт потребитель — варианты разложены по своей сетке, а не в ряд или столбец;
  • вид кнопки — variant="button" даёт внешность GrButton, оставляя роль radio;
  • подпись сложная — слот принимает разметку: цену, бейдж, иконку.

Когда взять другое

НужноБерите
Вариантов несколько и они рядомGrRadioGroup
Можно выбрать несколькоGrCheckbox
Вариантов 2–5 и они переключают видGrSegmented
Вариантов больше семиGrSelect

Вне GrRadioGroup клавиатурный контракт паттерна radiogroup не работает: стрелки между вариантами водит контейнер. Одиночный GrRadio — это осознанный отказ от него ради своей раскладки, а не упрощение.

Варианты все тона и виды — настоящим компонентом

variant

  • radiobox
  • button

Размеры

  • xs
  • sm
  • md
  • lg

Состояния

  • обычная
  • disabled
  • invalid

Матрица живая: наводите и нажимайте. Состояний наведения, фокуса и нажатия здесь нет — они за браузером, а не за пропом.

Playground контролы выводятся из API, а не пишутся руками

Сцена

Загружается…

0 изменено· состояние — в адресе

Примеры 3 у каждого — живое демо и его код

  • Тарифы карточками

    Отдельные переключатели с одним name собирают группу карточек: клик в любом месте карточки выбирает тариф, переключатель внутри держит клавиатуру, а слот description кладёт пояснение в доступное имя — по нему и выбирают.

    Choose a plan
    €12 / month
    €49 / month
    Custom
  • Вариант доставки со своим полем

    Отдельные переключатели позволяют поместить содержимое между вариантами: пункт выдачи выбирается прямо под своим вариантом, чего список options не умеет. Выпадающий список стоит на месте и включается только со своим вариантом, поэтому форма не прыгает; disabled-вариант продолжает объяснять почему.

    Delivery
  • Период в шапке карточки

    variant="button" меняет рисунок, а не смысл: одно имя, стрелки между вариантами. В шапку карточки подходят тихие кнопки — button-variant="ghost" в покое и заполненная нейтральная при выборе через selected-button-variant и selected-button-tone.

    Revenue€76,900 +6.1%

API порождается из пакета · разойтись не может

Props

PropTypeпо умолчаниюОписание
disabledboolean | undefinedundefinedПереключатель недоступен. Не задан — берётся из группы.
variantGrRadioVariant | undefined"radiobox"Вид: `radiobox` — круг с точкой и подписью, `button` — кнопка в стиле `GrButton`. По умолчанию `radiobox`.
modelValuev-modelGrRadioValue | undefinedundefinedВыбранное значение (`v-model`) у одиночного переключателя. В группе значение ведёт она.
invalidboolean | undefinedfalseВизуальное и ARIA-состояние ошибки. Складывается с `invalid` группы.
requiredboolean | undefinedfalseВыбор обязателен: `aria-required` на переключателе.
size"xs" | "sm" | "md" | "lg" | undefinedundefinedРазмер по шкале кнопок. Не задан — из группы, затем `GrConfigProvider`, иначе `md`.
ariaLabelstring | undefinedundefinedДоступное имя, когда подписи в слоте нет — например, у кнопки из одной иконки.
namestring | undefinedundefinedИмя для нативной формы: выбранный отдаёт значение скрытым полем. Не задано — из группы.
idstring | undefinedundefined`id` элемента с ролью `radio` — для ссылок на него извне.
formstring | undefinedundefined`form` скрытого поля — когда форма, куда уходит значение, стоит не вокруг переключателя.
buttonVariantGrButtonVariant | undefined"outline"Вес невыбранной кнопки в `variant="button"`. По умолчанию `outline`.
buttonTone
"primary" | "neutral" | "success" | "warning" | "danger" | ""primary" | "neutral" | "success" | "warning" | "danger" | "info" | "slate" | "azure" | undefined
"neutral"Тон невыбранной кнопки в `variant="button"`. По умолчанию `neutral`.
selectedButtonVariantGrButtonVariant | undefined"primary"Вес выбранной кнопки в `variant="button"`. По умолчанию `primary`.
selectedButtonTone
"primary" | "neutral" | "success" | "warning" | "danger" | ""primary" | "neutral" | "success" | "warning" | "danger" | "info" | "slate" | "azure" | undefined
"primary"Тон выбранной кнопки в `variant="button"`. По умолчанию `primary`.

Слоты

SlotTypeОписание
defaultanyПодпись переключателя вместо пропа `label`.
descriptionanyПояснение под подписью.

События

EventСигнатураОписание
update:modelValue(event: "update:modelValue", value: GrRadioValue): void—

Типы

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

Импорт
import type { GrRadioProps, GrRadioEmits, GrRadioConfigurableProps } from '@feugene/granularity/components/GrRadio'

Файлdist/types/components/GrRadio/GrRadio.vue.d.ts

GrRadioPropsinterface

GrRadio — одиночный элемент группы выбора. Может работать автономно (через v-model) либо внутри GrRadioGroup (тогда modelValue/disabled/size/name/invalid приходят через inject). Доступным контролом является сам элемент с role="radio": он держит фокус, aria-checked и клавиатуру. Внутрь него ничего интерактивного не вкладывается — role="radio" объявляет потомков презентационными, и вложенный native <input> (даже скрытый и с tabindex="-1") ломает виджет для скринридеров и падает в axe на nested-interactive. Для отправки нативной формой рядом рендерится input[type="hidden"] — он не фокусируется и не является интерактивным.

interface GrRadioProps {
    /** Значение переключателя: он выбран, когда оно совпадает с `modelValue` или значением группы. */
    value: GrRadioValue;
    /** Выбранное значение (`v-model`) у одиночного переключателя. В группе значение ведёт она. */
    modelValue?: GrRadioValue;
    /** Переключатель недоступен. Не задан — берётся из группы. */
    disabled?: boolean;
    /** Визуальное и ARIA-состояние ошибки. Складывается с `invalid` группы. */
    invalid?: boolean;
    /** Имя для нативной формы: выбранный отдаёт значение скрытым полем. Не задано — из группы. */
    name?: string;
    /** Выбор обязателен: `aria-required` на переключателе. */
    required?: boolean;
    /** `form` скрытого поля — когда форма, куда уходит значение, стоит не вокруг переключателя. */
    form?: string;
    /** `id` элемента с ролью `radio` — для ссылок на него извне. */
    id?: string;
    /** Размер по шкале кнопок. Не задан — из группы, затем `GrConfigProvider`, иначе `md`. */
    size?: GrButtonSize;
    /**
     * Вид: `radiobox` — круг с точкой и подписью, `button` — кнопка в стиле `GrButton`.
     * По умолчанию `radiobox`.
     */
    variant?: GrRadioVariant;
    /** Вес невыбранной кнопки в `variant="button"`. По умолчанию `outline`. */
    buttonVariant?: GrButtonVariant;
    /** Тон невыбранной кнопки в `variant="button"`. По умолчанию `neutral`. */
    buttonTone?: GrButtonTone;
    /** Вес выбранной кнопки в `variant="button"`. По умолчанию `primary`. */
    selectedButtonVariant?: GrButtonVariant;
    /** Тон выбранной кнопки в `variant="button"`. По умолчанию `primary`. */
    selectedButtonTone?: GrButtonTone;
    /** Доступное имя, когда подписи в слоте нет — например, у кнопки из одной иконки. */
    ariaLabel?: string;
}
GrRadioEmitsinterface
interface GrRadioEmits {
    /** Выбран этот переключатель (`v-model`). Только при заданном `modelValue`, иначе выбор у группы. */
    (e: 'update:modelValue', value: GrRadioValue): void;
}

Файлdist/types/components/GrRadio/config.d.ts

grRadioConfigconst
const grRadioConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrRadio">;

Файлdist/types/components/GrRadio/defaults.d.ts

GrRadioConfigurablePropsinterface

Пропы GrRadio, настраиваемые глобально через <GrConfigProvider :component-defaults="{ GrRadio: { … } }">. Только оформление — см. GrButton/defaults.ts.

interface GrRadioConfigurableProps {
    size: GrButtonSize;
}

Файлdist/types/components/GrRadio/grRadioStyles.d.ts

GrRadioVarianttype
type GrRadioVariant = 'radiobox' | 'button';

Файлdist/types/components/GrRadio/grRadioGroupContext.d.ts

GrRadioGroupContexttype
type GrRadioGroupContext = {
    modelValue: ComputedRef<GrRadioValue>;
    name: ComputedRef<string | undefined>;
    disabled: ComputedRef<boolean>;
    /**
     * Только для чтения: выбор видно, но он не меняется. У роли `radio` нет
     * `aria-readonly` — состояние объявляет сама группа, переключателю остаётся
     * не прикидываться кликабельным.
     */
    readonly: ComputedRef<boolean>;
    /** Ошибка группы: до вида переключателя она доходит только через контекст. */
    invalid: ComputedRef<boolean>;
    size: ComputedRef<GrButtonSize>;
    setValue: (value: GrRadioValue) => void;

Файлdist/types/components/GrRadio/safelist.d.ts

grRadioSafelistconst

Пуст: собственных составных классов у переключателя нет. radiobox и отключённая кнопка-радио выбирают классы целыми литералами из grRadioStyles.ts и шаблона — granum извлекает их из чанков компонента сам, включая общие. Вид button строит grRadioStyles.ts: grRadioButtonClass через grButtonClass из GrButton/grButtonStyles.ts: матрицу тон × вариант там клеит variantClass, база и размеры лежат литералами. Модуль живёт в чанке GrButton, обход от входа GrRadio на нём останавливается как на ребре, и все эти классы приезжают зависимостью GrButton: литералы её чанка извлекаются у неё, матрицу объявляет grButtonSafelist. Повторять их здесь незачем.

const grRadioSafelist: string[];

Доступность

Паттерн APG

radio

Клавиши

Enter↑ / ←

КлавишаДействие
—Space
Enterвыбрать; ↓/→ — следующий
↑ / ←предыдущий (выбор едет вместе с фокусом, по кругу); Home/End — первый/последний доступный. Группа — одна остановка Tab, отключённые варианты пропускаются

Использование из прозы библиотеки

Клавиатура: группа — одна остановка Tab
КлавишаЧто делает
Tabвходит в группу и выходит из неё; внутри группы остановка ровно одна (roving tabindex)
↓ / →следующий вариант, по кругу
↑ / ←предыдущий вариант, по кругу
Home / Endпервый / последний доступный вариант
Space, Enterвыбрать текущий (для автономного переключателя)

Выбор едет вместе с фокусом — так требует паттерн radiogroup. Отключённые варианты пропускаются: они не участвуют ни в roving tabindex, ни в обходе стрелками.

Состояние живёт в GrRadioGroup (register / rovingValue / moveSelection / selectEdge в контексте), а GrRadio спрашивает у группы, он ли сейчас фокусируемый. Автономный переключатель вне группы остаётся обычной остановкой Tab.

Подпись и описание
vue
<GrRadio value="pro">
  Pro
  <template #description>
    Списывается ежемесячно, можно отменить в любой момент
  </template>
</GrRadio>

Подпись выбранного варианта — --gr-fg, невыбранного — --gr-muted-fg: до этого приглушённой была любая, и выбранный вариант не выделялся текстом вовсе.

Слот #description связывается с переключателем через aria-describedby — иначе для скринридера описания не существует.

Значения

value и modelValue — string | number | boolean (GrRadioValue). Перечисления в реальных формах это обычно id числом или флаг. Объекты сюда не входят намеренно: значение уходит в data-value и в скрытый input нативной формы, а значит должно иметь однозначное строковое представление.

Ошибка и отключённое состояние

invalid можно поставить самому переключателю или всей группе — состояния складываются по «или». Рамка контрола красится --gr-danger, на элемент идёт aria-invalid.

Отключённый переключатель гасится токенами (--gr-muted / --gr-muted-fg), а не прозрачностью: opacity разбавляет выверенные на AA цвета. Кнопочный вариант берёт отключённый вид у самого GrButton — там он вариант-зависимый.

Нативная форма

Рядом с переключателем рендерится input[type="hidden"] — только у выбранного и не-disabled элемента и только при заданном name, как поступил бы нативный radio. Внутрь элемента с role="radio" ничего интерактивного не вкладывается: роль объявляет потомков презентационными, и скрытый <input type="radio"> сломал бы виджет для скринридера (axe: nested-interactive).