Примитивы
14
Select
Селект на настоящем листбоксе: группы, описания и опции, которые вы рисуете сами, — нативный <select> не умеет ничего из этого, поэтому в библиотеке именно такой. Триггер — <button role="combobox">, и фокус его не покидает: курсор по списку виртуальный, поэтому нечего запирать и нечего возвращать.
чистая разметка — библиотеки на странице нет
отрендерил aura-react
отрендерил aura-react
отрендерил aura-react
отрендерил aura-react
- Apple
- CherryIn season
Код
index.html
<div class="aura-field-group">
<label class="aura-label" id="s-label" for="s">Fruit</label>
<button id="s" type="button" class="aura-combobox" role="combobox" aria-haspopup="listbox"
aria-expanded="true" aria-controls="s-listbox" aria-labelledby="s-label s"
aria-activedescendant="s-opt-0" data-state="open">
<span class="aura-combobox__value">Apple</span>
<span class="aura-combobox__chevron" aria-hidden="true"></span>
</button>
</div>
<!-- Здесь нет data-side. Этот атрибут пишет place(), и именно .aura-popup[data-side] делает панель
position: fixed — статически привязываться не к чему, поэтому фиксированная панель просто
схлопнулась бы в угол окна. Без него панель остаётся в потоке и показывает, как выглядит
список; в настоящем Select place() выдаёт и атрибут, и отступы в том же кадре, в котором панель
монтируется. -->
<div class="aura-popup" data-state="open">
<ul id="s-listbox" class="aura-listbox" role="listbox" aria-labelledby="s-label">
<li id="s-opt-0" class="aura-listbox__option" role="option" aria-selected="true" data-active="true">
<span class="aura-listbox__label">Apple</span>
</li>
<li id="s-opt-1" class="aura-listbox__option" role="option" aria-selected="false" data-active="false">
<span class="aura-listbox__label">Cherry<span class="aura-listbox__description">In season</span></span>
</li>
</ul>
</div>Demo.tsx
import { Select, Stack } from '@ilomee/aura-react';
const OPTIONS = [
{ value: 'apple', label: 'Apple', group: 'Pome' },
{ value: 'pear', label: 'Pear', group: 'Pome' },
{ value: 'cherry', label: 'Cherry', description: 'In season', group: 'Stone' },
{ value: 'durian', label: 'Durian', description: 'Out of stock', group: 'Stone', disabled: true },
];
/**
* `minWidth` — так же, как в демо Field: панель превью сжимается по содержимому, и комбобокс
* шириной со свой плейсхолдер менял бы размер, стоит выбрать что-то длиннее. В форме об этом
* заботится `inline-size: 100%` самого контрола, но у превью нет формы, которую можно заполнить.
*/
export default function Demo() {
return (
<Stack style={{ minWidth: 260 }}>
<Select label="Fruit" placeholder="Pick one" options={OPTIONS} />
</Stack>
);
}Demo.vue
<script setup lang="ts">
import { Select } from '@ilomee/aura-vue';
const fruit = ref('');
</script>
<template>
<Select v-model="fruit" label="Fruit" placeholder="Pick one" :options="options" />
</template>Demo.svelte
<script lang="ts">
import { Select } from '@ilomee/aura-svelte';
let fruit = $state('');
</script>
<Select bind:value={fruit} label="Fruit" placeholder="Pick one" {options} />Используется как aura-select.
demo.component.ts
import { AuraSelect } from '@ilomee/aura-angular';
@Component({
standalone: true,
imports: [AuraSelect],
template: `<aura-select label="Fruit" placeholder="Pick one" [options]="options" [(value)]="fruit" />`,
})
export class DemoComponent {
fruit = '';
}Пропсы
У чистой разметки нет пропсов — классы ниже и есть весь API. Четыре пакета не делают ничего сверх того, что проставляют вам те же самые классы.
| Имя | Тип | По умолчанию | Примечания |
|---|---|---|---|
| options | SelectOption[] | null | — | Nullable — рисует пустой список, а не бросает. group и description — строки, icon — ReactNode. |
| valueдвусторонний | string | — | |
| defaultValue | string | '' | |
| onValueChangeсобытие | (value: string) => void | — | |
| placeholder | string | — | |
| hint | string | — | Постоянная подсказка под контролом, показывается всегда. |
| error | string | — | Что не так прямо сейчас. Ставит контролу aria-invalid и добавляет сообщение в его aria-describedby — после подсказки. Без живого региона: role="alert" объявляет себя при монтировании, поэтому форма с тремя ошибками, отрисованная на сервере, перебила бы читателя трижды ещё до первого поля. |
| emptyLabel | string | 'No results' | |
| openдвусторонний | boolean | — | Неконтролируемо — через defaultOpen. |
| onOpenChangeсобытие | (open: boolean) => void | — | |
| renderOption | (option, state) => ReactNode | — | Заменяет содержимое строки — но не сам <li>. Роль, id, aria-selected и класс остаются за Aura, поэтому хук не может сломать доступность. state — это { index, active, selected, disabled }. |
| Имя | Тип | По умолчанию | Примечания |
|---|---|---|---|
| options | SelectOption[] | null | — | |
| modelValueдвусторонний | string | — | |
| update:modelValueсобытие | (value: string) => void | — | |
| placeholder | string | — | |
| hint | string | — | Постоянная подсказка под контролом, показывается всегда. |
| error | string | — | Что не так прямо сейчас. Ставит контролу aria-invalid и добавляет сообщение в его aria-describedby — после подсказки. Без живого региона: role="alert" объявляет себя при монтировании, поэтому форма с тремя ошибками, отрисованная на сервере, перебила бы читателя трижды ещё до первого поля. |
| emptyLabel | string | 'No results' | |
| openдвусторонний | boolean | — | |
| optionслот | slot({ option, index, active, selected, disabled }) | — | Люк во Vue — scoped slot, а не проп renderOption, как в React: полезная нагрузка та же, идиома у каждого своя. Он заполняет строку опции; сам <li> остаётся за Aura. |
| Имя | Тип | По умолчанию | Примечания |
|---|---|---|---|
| options | SelectOption[] | null | — | |
| valueдвусторонний | string | — | Bindable — поэтому defaultValue нет: один проп покрывает оба режима там, где React требует пары. |
| onchangeсобытие | (value: string) => void | — | |
| placeholder | string | — | |
| hint | string | — | Постоянная подсказка под контролом, показывается всегда. |
| error | string | — | Что не так прямо сейчас. Ставит контролу aria-invalid и добавляет сообщение в его aria-describedby — после подсказки. Без живого региона: role="alert" объявляет себя при монтировании, поэтому форма с тремя ошибками, отрисованная на сервере, перебила бы читателя трижды ещё до первого поля. |
| emptyLabel | string | 'No results' | |
| openдвусторонний | boolean | — | |
| optionслот | Snippet<[SelectOption, AuraOptionRenderState]> | — |
| Имя | Тип | По умолчанию | Примечания |
|---|---|---|---|
| options | SelectOption[] | null | — | |
| valueдвусторонний | string | — | |
| valueChangeсобытие | (value: string) => void | — | |
| placeholder | string | — | |
| hint | string | — | Постоянная подсказка под контролом, показывается всегда. |
| error | string | — | Что не так прямо сейчас. Ставит контролу aria-invalid и добавляет сообщение в его aria-describedby — после подсказки. Без живого региона: role="alert" объявляет себя при монтировании, поэтому форма с тремя ошибками, отрисованная на сервере, перебила бы читателя трижды ещё до первого поля. |
| emptyLabel | string | 'No results' | |
| openдвусторонний | boolean | — | |
| optionTemplate | TemplateRef | — | Связанный TemplateRef, а не запрос через <ng-content> — идиома, которую уже использует AuraDataTable: шаблон объявляется там, где его область видимости. Контекст: let-option let-state="state". Он заполняет строку опции; сам <li> остаётся за Aura. |
Какие классы отдаёт
Все фреймворки отдают одни и те же классы — именно поэтому четыре библиотеки выглядят одинаково. Их можно использовать напрямую, вообще без библиотеки компонентов.
| Класс | Роль | Примечания |
|---|---|---|
| .aura-combobox | база | Обязательный. Без него модификатор не работает. |
| .aura-combobox__value | элемент | |
| .aura-combobox__chevron | элемент | |
| .aura-popup | база | Обязательный. Без него модификатор не работает. |
| .aura-listbox | база | Обязательный. Без него модификатор не работает. |
| .aura-listbox__option | элемент |
Токены и правила
components.css- рамка
- var(--field-border) · var(--field-radius) — min-block-size, not height
- слой панели
- var(--z-dropdown) — under a modal, on purpose
- зазор до якоря
- var(--popover-offset)
- панель
- var(--shadow-3) · max-block-size: min(320px, 60vh)