@rt-tools/ui-kit-v2 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/rt-tools-ui-kit-v2.mjs +313 -205
- package/fesm2022/rt-tools-ui-kit-v2.mjs.map +1 -1
- package/package.json +1 -1
- package/rt-tools-ui-kit-v2-0.3.0.tgz +0 -0
- package/src/lib/components/CONTEXT.md +49 -0
- package/src/lib/components/aside/CONTEXT.md +51 -0
- package/src/lib/components/aside/_rt-aside-overlay.scss +88 -0
- package/src/lib/components/aside/rt-aside.component.scss +19 -94
- package/src/lib/components/aside-section/CONTEXT.md +26 -0
- package/src/lib/components/autocomplete/CONTEXT.md +58 -0
- package/src/lib/components/bar-list/CONTEXT.md +27 -0
- package/src/lib/components/bar-list/rt-bar-list.component.scss +5 -2
- package/src/lib/components/bottom-sheet/CONTEXT.md +54 -0
- package/src/lib/components/bottom-sheet/rt-bottom-sheet.component.scss +53 -53
- package/src/lib/components/button/CONTEXT.md +53 -0
- package/src/lib/components/button/rt-button.directive.scss +16 -0
- package/src/lib/components/calendar/CONTEXT.md +41 -0
- package/src/lib/components/calendar/rt-calendar.component.scss +96 -99
- package/src/lib/components/card/CONTEXT.md +34 -0
- package/src/lib/components/card/rt-card.component.scss +5 -2
- package/src/lib/components/chat/CONTEXT.md +41 -0
- package/src/lib/components/checkbox/CONTEXT.md +37 -0
- package/src/lib/components/checkbox/rt-checkbox.component.scss +7 -2
- package/src/lib/components/collapsible-text/CONTEXT.md +29 -0
- package/src/lib/components/confirm-popover/CONTEXT.md +47 -0
- package/src/lib/components/container/CONTEXT.md +42 -0
- package/src/lib/components/container/rt-container.component.scss +5 -69
- package/src/lib/components/counter/CONTEXT.md +40 -0
- package/src/lib/components/counter/rt-counter.component.scss +10 -9
- package/src/lib/components/counter-row/CONTEXT.md +24 -0
- package/src/lib/components/counter-row/rt-counter-row.component.scss +23 -22
- package/src/lib/components/date-picker/CONTEXT.md +41 -0
- package/src/lib/components/delta-view/CONTEXT.md +31 -0
- package/src/lib/components/detail-list/CONTEXT.md +29 -0
- package/src/lib/components/dialog/CONTEXT.md +50 -0
- package/src/lib/components/dialog/rt-dialog.component.scss +6 -2
- package/src/lib/components/download-link/CONTEXT.md +26 -0
- package/src/lib/components/empty-state/CONTEXT.md +35 -0
- package/src/lib/components/field/CONTEXT.md +72 -0
- package/src/lib/components/file-card/CONTEXT.md +28 -0
- package/src/lib/components/file-drop/CONTEXT.md +39 -0
- package/src/lib/components/file-input/CONTEXT.md +36 -0
- package/src/lib/components/file-list/CONTEXT.md +15 -0
- package/src/lib/components/filter-control/CONTEXT.md +33 -0
- package/src/lib/components/form-control/CONTEXT.md +50 -0
- package/src/lib/components/header/CONTEXT.md +36 -0
- package/src/lib/components/icon/CONTEXT.md +46 -0
- package/src/lib/components/icon/rt-icon.component.scss +3 -5
- package/src/lib/components/icon-button/CONTEXT.md +49 -0
- package/src/lib/components/icon-button/rt-icon-button.component.scss +14 -1
- package/src/lib/components/info-item/CONTEXT.md +30 -0
- package/src/lib/components/input/CONTEXT.md +47 -0
- package/src/lib/components/input-number/CONTEXT.md +52 -0
- package/src/lib/components/live-badge/CONTEXT.md +31 -0
- package/src/lib/components/live-badge/rt-live-badge.component.scss +5 -2
- package/src/lib/components/logo/CONTEXT.md +42 -0
- package/src/lib/components/logo/rt-logo.component.scss +4 -6
- package/src/lib/components/menu/CONTEXT.md +65 -0
- package/src/lib/components/message/CONTEXT.md +35 -0
- package/src/lib/components/message/rt-message.component.scss +5 -2
- package/src/lib/components/message-composer/CONTEXT.md +46 -0
- package/src/lib/components/money-list/CONTEXT.md +32 -0
- package/src/lib/components/multiselect/CONTEXT.md +43 -0
- package/src/lib/components/night-grid/CONTEXT.md +31 -0
- package/src/lib/components/night-grid/rt-night-grid.component.scss +5 -2
- package/src/lib/components/note/CONTEXT.md +27 -0
- package/src/lib/components/notifications-bell/CONTEXT.md +29 -0
- package/src/lib/components/page-header/CONTEXT.md +33 -0
- package/src/lib/components/pagination/CONTEXT.md +43 -0
- package/src/lib/components/photo-viewer/CONTEXT.md +30 -0
- package/src/lib/components/photo-viewer/rt-photo-viewer.component.scss +95 -96
- package/src/lib/components/popover/CONTEXT.md +54 -0
- package/src/lib/components/rich-editor/CONTEXT.md +36 -0
- package/src/lib/components/section-nav/CONTEXT.md +27 -0
- package/src/lib/components/section-nav/rt-section-nav.component.scss +7 -1
- package/src/lib/components/select/CONTEXT.md +54 -0
- package/src/lib/components/skeleton/CONTEXT.md +41 -0
- package/src/lib/components/skeleton-wrapper/CONTEXT.md +24 -0
- package/src/lib/components/skeleton-wrapper/rt-skeleton-wrapper.component.scss +5 -1
- package/src/lib/components/spinner/CONTEXT.md +39 -0
- package/src/lib/components/split-button/CONTEXT.md +39 -0
- package/src/lib/components/stat-tile/CONTEXT.md +35 -0
- package/src/lib/components/stat-tile/rt-stat-tile.component.scss +5 -2
- package/src/lib/components/stepper/CONTEXT.md +36 -0
- package/src/lib/components/stepper/rt-stepper.component.scss +5 -2
- package/src/lib/components/table/CONTEXT.md +53 -0
- package/src/lib/components/table/settings-aside/rt-table-settings-aside.component.scss +4 -2
- package/src/lib/components/table/settings-panel/rt-table-settings-panel.component.scss +1 -1
- package/src/lib/components/tabs/CONTEXT.md +56 -0
- package/src/lib/components/tag/CONTEXT.md +47 -0
- package/src/lib/components/tag/rt-tag.component.scss +5 -2
- package/src/lib/components/textarea/CONTEXT.md +38 -0
- package/src/lib/components/theme-toggle/CONTEXT.md +28 -0
- package/src/lib/components/theme-toggle/rt-theme-toggle.component.scss +5 -1
- package/src/lib/components/thread-list/CONTEXT.md +42 -0
- package/src/lib/components/timeline/CONTEXT.md +33 -0
- package/src/lib/components/timeline/rt-timeline.component.scss +5 -2
- package/src/lib/components/toast/CONTEXT.md +53 -0
- package/src/lib/components/toggle-button-group/CONTEXT.md +38 -0
- package/src/lib/components/toggle-button-group/rt-toggle-button-group.component.scss +12 -7
- package/src/lib/components/toggle-switch/CONTEXT.md +35 -0
- package/src/lib/components/toggle-switch/rt-toggle-switch.component.scss +7 -2
- package/src/lib/components/toolbar/CONTEXT.md +22 -0
- package/src/lib/components/tooltip/CONTEXT.md +34 -0
- package/src/lib/components/welcome-dialog/CONTEXT.md +20 -0
- package/src/lib/components/welcome-dialog/rt-welcome-dialog.component.scss +6 -2
- package/src/lib/components/workspace/CONTEXT.md +46 -0
- package/src/lib/components/workspace-details/CONTEXT.md +42 -0
- package/styles/tokens.css +14 -0
- package/types/rt-tools-ui-kit-v2.d.ts +50 -29
- package/rt-tools-ui-kit-v2-0.1.0.tgz +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# `rt-file-input`
|
|
2
|
+
|
|
3
|
+
Поле выбора файлов: кнопка, скрытое нативное поле и список выбранного карточками.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-field label="Документы">
|
|
7
|
+
<rt-file-input accept=".pdf,.docx" buttonLabel="Прикрепить" [formControl]="docs" [multiple]="true" />
|
|
8
|
+
</rt-field>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Свои входы поверх общих из [`RtFormControlBase`](../form-control/CONTEXT.md):
|
|
12
|
+
|
|
13
|
+
| вход | тип | умолчание |
|
|
14
|
+
| ------------- | ---------------- | --------------------------- |
|
|
15
|
+
| `multiple` | `boolean` | `false` |
|
|
16
|
+
| `accept` | `string \| null` | `null` |
|
|
17
|
+
| `directory` | `boolean` | `false` |
|
|
18
|
+
| `buttonLabel` | `string` | `''` → `rtKit.uiChooseFile` |
|
|
19
|
+
|
|
20
|
+
Значение — `File[]`.
|
|
21
|
+
|
|
22
|
+
## Главное, что нужно знать
|
|
23
|
+
|
|
24
|
+
**Нативное поле спрятано.** Его нельзя оформить, поэтому выбор открывает своя кнопка, кликая по
|
|
25
|
+
скрытому полю программно.
|
|
26
|
+
|
|
27
|
+
**Без `multiple` берётся только первый файл**, даже если браузер отдал набор: так бывает при
|
|
28
|
+
перетаскивании в одиночное поле.
|
|
29
|
+
|
|
30
|
+
**Удаление файла сбрасывает значение нативного поля.** Иначе повторный выбор того же файла не
|
|
31
|
+
дал бы события `change`.
|
|
32
|
+
|
|
33
|
+
## Рядом
|
|
34
|
+
|
|
35
|
+
- [`rt-file-drop`](../file-drop/CONTEXT.md) — приём файлов перетаскиванием.
|
|
36
|
+
- [`rt-file-card`](../file-card/CONTEXT.md) — чем рисуется каждый выбранный файл.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# `rt-file-list`
|
|
2
|
+
|
|
3
|
+
Раскладка перечня файлов. Входов, выходов и своей разметки нет — только отступы и перенос.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-file-list>
|
|
7
|
+
<rt-file-card *ngFor="…" … />
|
|
8
|
+
</rt-file-list>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Главное, что нужно знать
|
|
12
|
+
|
|
13
|
+
**Это чистая раскладка.** Что показывать — карточки [`rt-file-card`](../file-card/CONTEXT.md),
|
|
14
|
+
ссылки [`rt-download-link`](../download-link/CONTEXT.md) или свои строки — решает потребитель.
|
|
15
|
+
Пустоту и загрузку тоже рисует он: у списка нет ни заглушки, ни состояния.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# `rt-filter-control<T>`
|
|
2
|
+
|
|
3
|
+
Фильтр, который сам выбирает представление по ширине экрана.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-filter-control ariaLabel="Статус" [options]="options" [value]="status()" (valueChange)="status.set($event)" />
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
| вход | тип | умолчание |
|
|
10
|
+
| ------------------------ | ------------------------------------------------ | -------------- |
|
|
11
|
+
| `options` | `ReadonlyArray<{ value, label, icon?, title? }>` | **обязателен** |
|
|
12
|
+
| `value` | `T \| undefined` | `undefined` |
|
|
13
|
+
| `ariaLabel` | `string \| null` | `null` |
|
|
14
|
+
| `placeholder` | `string` | `''` |
|
|
15
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` |
|
|
16
|
+
| `disabled` / `fullWidth` | `boolean` | `false` |
|
|
17
|
+
|
|
18
|
+
Выход: `valueChange`.
|
|
19
|
+
|
|
20
|
+
## Главное, что нужно знать
|
|
21
|
+
|
|
22
|
+
**Одно и то же наполнение рисуется двумя разными контролами.** На широком экране это
|
|
23
|
+
[`rt-toggle-button-group`](../toggle-button-group/CONTEXT.md), на узком (≤1080px, по
|
|
24
|
+
`BreakpointsService.narrow`) — [`rt-select`](../select/CONTEXT.md): сегменты в строку не
|
|
25
|
+
помещаются. Потребитель об этом не думает и передаёт один набор.
|
|
26
|
+
|
|
27
|
+
**Очистка списка выключена**: фильтр всегда в каком-то состоянии, «ничего не выбрано» здесь не
|
|
28
|
+
предусмотрено.
|
|
29
|
+
|
|
30
|
+
## Края
|
|
31
|
+
|
|
32
|
+
- Иконки вариантов доезжают только до сегментов; в списке остаются одни подписи.
|
|
33
|
+
- Отключение доходит до обоих представлений.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# `RtFormControlBase<TValue>`
|
|
2
|
+
|
|
3
|
+
Абстрактная основа всех полей ввода кита. Собственной разметки не имеет и в разметку не ставится —
|
|
4
|
+
от неё наследуются [`rt-input`](../input/CONTEXT.md), [`rt-textarea`](../textarea/CONTEXT.md),
|
|
5
|
+
[`rt-input-number`](../input-number/CONTEXT.md), [`rt-select`](../select/CONTEXT.md),
|
|
6
|
+
[`rt-multiselect`](../multiselect/CONTEXT.md), [`rt-autocomplete`](../autocomplete/CONTEXT.md),
|
|
7
|
+
[`rt-date-picker`](../date-picker/CONTEXT.md).
|
|
8
|
+
|
|
9
|
+
Общие входы, которые получает каждый наследник:
|
|
10
|
+
|
|
11
|
+
| вход | тип | умолчание |
|
|
12
|
+
| ----------- | ---------------------- | --------- |
|
|
13
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` |
|
|
14
|
+
| `disabled` | `boolean` | `false` |
|
|
15
|
+
| `controlId` | `string \| null` | `null` |
|
|
16
|
+
| `ariaLabel` | `string \| null` | `null` |
|
|
17
|
+
| `clearable` | `boolean` | `true` |
|
|
18
|
+
| `bordered` | `boolean` | `true` |
|
|
19
|
+
|
|
20
|
+
## Главное, что нужно знать
|
|
21
|
+
|
|
22
|
+
**`NgControl` инжектится самим полем (`{ self: true, optional: true }`), а `valueAccessor`
|
|
23
|
+
привязывается вручную — провайдера `NG_VALUE_ACCESSOR` здесь нет.** Так поле получает доступ к
|
|
24
|
+
`control.invalid` / `touched` / `errors` (и умеет подсвечивать ошибку и отдавать её текст в
|
|
25
|
+
[`rt-field`](../field/CONTEXT.md)), не создавая цикла самоссылки, на котором Angular падает
|
|
26
|
+
с NG0200.
|
|
27
|
+
|
|
28
|
+
**Подсветка ошибки включается только после касания или правки**: `invalid && (touched || dirty)`.
|
|
29
|
+
Пустое обязательное поле невалидно с самого начала, но краснеть до первого касания не должно.
|
|
30
|
+
|
|
31
|
+
## Что задаёт вмещающее поле, а не потребитель
|
|
32
|
+
|
|
33
|
+
`rt-field` вызывает у контрола три метода — руками их звать не нужно:
|
|
34
|
+
|
|
35
|
+
- `assignControlId(id)` — авто-идентификатор для связки `label[for]`, если у контрола нет своего;
|
|
36
|
+
- `setReadonly(value)` — плоский режим чтения (`isReadonly`), в котором контрол подменяется
|
|
37
|
+
текстом `displayText()`;
|
|
38
|
+
- читает `required()`, `invalid()`, `errors()` — для звёздочки и текста ошибки.
|
|
39
|
+
|
|
40
|
+
## Что обязан реализовать наследник
|
|
41
|
+
|
|
42
|
+
| член | зачем |
|
|
43
|
+
| ------------------- | ----------------------------------------------- |
|
|
44
|
+
| `getEmptyValue()` | пустое значение своего типа: `''`, `null`, `[]` |
|
|
45
|
+
| `hasValue` | есть ли что очищать — гейт видимости крестика |
|
|
46
|
+
| `focusAfterClear()` | куда вернуть фокус после очистки |
|
|
47
|
+
| `displayText` | текст плоского режима чтения |
|
|
48
|
+
|
|
49
|
+
Поля с отдельным состоянием отображения (`rt-autocomplete`, `rt-input-number`) вдобавок
|
|
50
|
+
переопределяют `writeValue` и `clearValue`, синхронизируя свой сигнал отображения.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# `rt-header`
|
|
2
|
+
|
|
3
|
+
Верхняя полоса приложения: кнопка «назад», логотип, приглашение, колокольчик и профиль.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-header showInvite [canGoBack]="canGoBack()" (backClick)="back()" (inviteClick)="invite()" (profileClick)="openProfile()">
|
|
7
|
+
<rt-notifications-bell rtHeaderBell [unread]="hasUnread()" (clicked)="openNotifications()" />
|
|
8
|
+
</rt-header>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
| вход | тип | умолчание |
|
|
12
|
+
| ------------ | --------- | --------- |
|
|
13
|
+
| `canGoBack` | `boolean` | `false` |
|
|
14
|
+
| `showInvite` | `boolean` | `false` |
|
|
15
|
+
|
|
16
|
+
Выходы: `backClick`, `inviteClick`, `profileClick`. Слот `[rtHeaderBell]` — колокольчик.
|
|
17
|
+
|
|
18
|
+
## Главное, что нужно знать
|
|
19
|
+
|
|
20
|
+
**Кнопка «назад» не появляется и не исчезает — она проявляется.** Узел есть в разметке всегда,
|
|
21
|
+
видимость даёт модификатор `rt-header__back--visible`: иначе соседние элементы шапки прыгали бы
|
|
22
|
+
при каждом переходе.
|
|
23
|
+
|
|
24
|
+
**Колокольчик приходит проекцией.** Шапка ничего не знает про уведомления — их состоянием
|
|
25
|
+
владеет приложение.
|
|
26
|
+
|
|
27
|
+
## Края
|
|
28
|
+
|
|
29
|
+
- Кнопки действий подогнаны под высоту шапки инлайновым `--rt-icon-button-size: 35px` — это
|
|
30
|
+
осознанный выход за шкалу размеров.
|
|
31
|
+
- Логотип рисуется вариантом `wordmark` высотой 15px; сами файлы даёт приложение
|
|
32
|
+
(см. [`rt-logo`](../logo/CONTEXT.md)).
|
|
33
|
+
|
|
34
|
+
## Рядом
|
|
35
|
+
|
|
36
|
+
- [`rt-page-header`](../page-header/CONTEXT.md) — вторая полоса, с разделами.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# `rt-icon`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-icon name="check" size="md" color="current" [rotate]="null" />
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
| вход | тип | умолчание |
|
|
8
|
+
| -------- | ----------------------------------------------------------------------------------- | -------------- |
|
|
9
|
+
| `name` | `IRtIcon.Name` — литеральный union из `rt-icon-names.ts` | **обязателен** |
|
|
10
|
+
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl'` | `'md'` |
|
|
11
|
+
| `color` | `'current' \| 'muted' \| 'info' \| 'success' \| 'warning' \| 'danger' \| 'inverse'` | `'current'` |
|
|
12
|
+
| `rotate` | `number \| string \| null` | `null` |
|
|
13
|
+
|
|
14
|
+
Выходов нет. Размер и цвет едут инлайновым стилем host-а (`width`/`height`/`color`), не классом:
|
|
15
|
+
`xs` 12px, `sm` 16px, `md` 20px, `lg` 24px, `xl` 32px, `2xl` 40px.
|
|
16
|
+
|
|
17
|
+
## Главное, что нужно знать
|
|
18
|
+
|
|
19
|
+
**Компонент ничего не грузит.** Он рисует `<use href="#rt-icon-<name>">` — ссылку на symbol
|
|
20
|
+
внутри спрайта, который собирает `RtIconRegistry` и вставляет в `<body>`
|
|
21
|
+
провайдер `provideRtIcons()`. Без этого провайдера разметка верна, а на экране пусто.
|
|
22
|
+
|
|
23
|
+
Приложение обязано опубликовать набор из `assets/icons` пакета по адресу, который знает реестр
|
|
24
|
+
(по умолчанию `/icons`, иначе `provideRtIcons('/my/icons')`).
|
|
25
|
+
|
|
26
|
+
## Как этим пользоваться
|
|
27
|
+
|
|
28
|
+
- Иконка спрятана от скринридера (`aria-hidden="true"`) — подпись даёт вмещающий её контрол.
|
|
29
|
+
Отдельная `rt-icon` без подписи для AT невидима, и это правильно.
|
|
30
|
+
- Цвет по умолчанию наследуется от текста. Явное значение нужно, только когда иконка должна
|
|
31
|
+
отличаться от текста своего контрола.
|
|
32
|
+
- `rotate` принимает строку (`rotate="180"`) — так значение приходит из атрибута разметки.
|
|
33
|
+
Ноль и нечисловая строка гасят поворот: лишний `transform` создавал бы отдельный слой отрисовки.
|
|
34
|
+
|
|
35
|
+
## Края
|
|
36
|
+
|
|
37
|
+
- **Имя без файла роняет весь набор.** Реестр собирает спрайт одним `forkJoin`; один отказ
|
|
38
|
+
гасит иконки во всём приложении. Добавляя имя в `rt-icon-names.ts`, положи SVG рядом.
|
|
39
|
+
- Реестр пропускает загрузку, если спрайт уже в DOM (`#rt-icon-sprite`) — так витрина не гоняет
|
|
40
|
+
сотни запросов между историями.
|
|
41
|
+
- На сервере (SSR) спрайт не грузится: относительных HTTP-запросов к статике там нет, иконки
|
|
42
|
+
дорисуются после гидрации.
|
|
43
|
+
|
|
44
|
+
## Рядом
|
|
45
|
+
|
|
46
|
+
- [`rt-icon-button`](../icon-button/CONTEXT.md) — кликабельная иконка с подписью для AT.
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
/* rt-icon —
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Это исключение из общего правила "обёртывать стили в :host", применимого
|
|
5
|
-
только для Emulated-инкапсуляции. */
|
|
1
|
+
/* rt-icon — правила компонента (ViewEncapsulation.None).
|
|
2
|
+
Селектор bare `.rt-icon` (класс на host) — им же адресуются и программные
|
|
3
|
+
потребители sprite'а, рисующие иконку без компонента. */
|
|
6
4
|
.rt-icon {
|
|
7
5
|
display: inline-flex;
|
|
8
6
|
flex-shrink: 0;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# `rt-icon-button`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-icon-button icon="ico-trash" ariaLabel="Удалить" variant="danger" size="md" (clicked)="remove()" />
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
| вход | тип | умолчание |
|
|
8
|
+
| ----------------------------------------------- | --------------------------------------------------------------------------- | ---------------------------- |
|
|
9
|
+
| `icon` | `IRtIcon.Name` | **обязателен** |
|
|
10
|
+
| `ariaLabel` | `string` | **обязателен** |
|
|
11
|
+
| `variant` | `'primary' \| 'secondary' \| 'ghost' \| 'danger' \| 'success' \| 'warning'` | `'ghost'` |
|
|
12
|
+
| `size` | `'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl'` | `'md'` |
|
|
13
|
+
| `iconSize` | `IRtIcon.Size \| null` | `null` → от размера кнопки |
|
|
14
|
+
| `iconColor` | `IRtIcon.Color` | `'current'` |
|
|
15
|
+
| `shape` | `'circle' \| 'square'` | `'square'` |
|
|
16
|
+
| `type` | `'button' \| 'submit'` | `'button'` |
|
|
17
|
+
| `tooltip` | `string` | `''` (пусто → подсказки нет) |
|
|
18
|
+
| `tabIndex` | `number` | `0` |
|
|
19
|
+
| `loading` / `disabled` / `active` / `indicator` | `boolean` | `false` |
|
|
20
|
+
|
|
21
|
+
| выход | тип |
|
|
22
|
+
| --------- | ------------ |
|
|
23
|
+
| `clicked` | `MouseEvent` |
|
|
24
|
+
|
|
25
|
+
## Главное, что нужно знать
|
|
26
|
+
|
|
27
|
+
**`ariaLabel` обязателен.** У кнопки нет текста, и без подписи она для скринридера безымянна.
|
|
28
|
+
Это единственная причина, по которой вход помечен `required`.
|
|
29
|
+
|
|
30
|
+
**Кнопку компонент рисует внутри себя**, host остаётся обычным элементом. Диаметр держит
|
|
31
|
+
свойство `--rt-icon-button-size`, и объявляет его модификатор размера на самой кнопке —
|
|
32
|
+
поэтому ни класс на host-е, ни инлайн-стиль на нём диаметра не меняют: собственное
|
|
33
|
+
объявление элемента бьёт унаследованное. Размер вне шкалы получается не обходом, а новой
|
|
34
|
+
ступенью здесь же: `sm` 28px, `md` 36px, `lg` 44px, `xl` 52px, `2xl` 60px.
|
|
35
|
+
|
|
36
|
+
## Как этим пользоваться
|
|
37
|
+
|
|
38
|
+
- `loading` = `disabled` плюс кольцо вместо иконки. Клик в этом состоянии наружу не уходит.
|
|
39
|
+
- `active` превращается в `aria-pressed="true"` — для кнопок-переключателей.
|
|
40
|
+
- `tabIndex="-1"` оставляет клик мышью, но убирает кнопку из таб-порядка: так сделаны
|
|
41
|
+
вспомогательные крестики внутри полей.
|
|
42
|
+
- `iconSize` нужен, когда крупная кнопка должна остаться крупной (в неё целятся пальцем), а
|
|
43
|
+
иконка во весь диаметр выглядит тяжеловесно.
|
|
44
|
+
- Модификаторы в камелкейсе конвертируются в дефис: `indicator` → `rt-icon-button--has-indicator`.
|
|
45
|
+
|
|
46
|
+
## Рядом
|
|
47
|
+
|
|
48
|
+
- [`[rtButton]`](../button/CONTEXT.md) — кнопка с подписью.
|
|
49
|
+
- [`[rtTooltip]`](../tooltip/CONTEXT.md) — то, чем рисуется вход `tooltip`.
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/* rt-icon-button — иконочная кнопка в common/ui. ViewEncapsulation.None.
|
|
2
|
+
Хост адресуется по имени элемента, а не по классу блока: класс `.rt-icon-button` висит
|
|
3
|
+
и на `<rt-icon-button>`, и на корне шаблона, поэтому правила блока вложены в хост —
|
|
4
|
+
так они достаются только корню шаблона. */
|
|
5
|
+
|
|
6
|
+
rt-icon-button {
|
|
2
7
|
display: inline-flex;
|
|
3
8
|
|
|
4
9
|
/* Дефолт = md; перезаписывается модификаторами sm/lg ниже либо inline стилем потребителя */
|
|
@@ -30,6 +35,14 @@
|
|
|
30
35
|
--rt-icon-button-size: 44px;
|
|
31
36
|
}
|
|
32
37
|
|
|
38
|
+
&--xl {
|
|
39
|
+
--rt-icon-button-size: 52px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
&--2xl {
|
|
43
|
+
--rt-icon-button-size: 60px;
|
|
44
|
+
}
|
|
45
|
+
|
|
33
46
|
&--circle {
|
|
34
47
|
border-radius: var(--rt-radius-full);
|
|
35
48
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# `rt-info-item`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-info-item label="Тариф" [loading]="!plan()" [grow]="true">{{ plan()?.title }}</rt-info-item>
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
| вход | тип | умолчание |
|
|
8
|
+
| --------- | --------- | -------------- |
|
|
9
|
+
| `label` | `string` | **обязателен** |
|
|
10
|
+
| `loading` | `boolean` | `false` |
|
|
11
|
+
| `grow` | `boolean` | `false` |
|
|
12
|
+
|
|
13
|
+
Значение приходит **проекцией**, а не входом — поэтому внутрь можно положить что угодно:
|
|
14
|
+
текст, `rt-tag`, ссылку, `@if`.
|
|
15
|
+
|
|
16
|
+
## Главное, что нужно знать
|
|
17
|
+
|
|
18
|
+
**Во время загрузки подменяется только значение.** Подпись остаётся на месте: по ней читается,
|
|
19
|
+
чего именно ждут. Заглушка узкая (80px) — под значение, а не под всю строку.
|
|
20
|
+
|
|
21
|
+
## Как этим пользоваться
|
|
22
|
+
|
|
23
|
+
- `grow` растягивает элемент по свободному месту строки. Без него он занимает свою ширину —
|
|
24
|
+
так пары «подпись: значение» выстраиваются в ряд.
|
|
25
|
+
- Пара подпись/значение — самостоятельный блок; для длинного вертикального перечня
|
|
26
|
+
«ключ → значение» есть [`rt-detail-list`](../detail-list/CONTEXT.md).
|
|
27
|
+
|
|
28
|
+
## Рядом
|
|
29
|
+
|
|
30
|
+
- [`rt-skeleton-wrapper`](../skeleton-wrapper/CONTEXT.md) — то, чем нарисована заглушка.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# `rt-input`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-field label="Имя">
|
|
5
|
+
<rt-input placeholder="Как в паспорте" iconLeft="search" [formControl]="name" />
|
|
6
|
+
</rt-field>
|
|
7
|
+
|
|
8
|
+
<rt-input type="password" [formControl]="password" [passwordToggle]="true" />
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Свои входы поверх общих из [`RtFormControlBase`](../form-control/CONTEXT.md)
|
|
12
|
+
(`size`, `disabled`, `controlId`, `ariaLabel`, `clearable`, `bordered`):
|
|
13
|
+
|
|
14
|
+
| вход | тип | умолчание |
|
|
15
|
+
| ------------------------ | ------------------------------------------- | --------- |
|
|
16
|
+
| `type` | `'text' \| 'password' \| 'email' \| 'time'` | `'text'` |
|
|
17
|
+
| `placeholder` | `string` | `''` |
|
|
18
|
+
| `iconLeft` / `iconRight` | `IRtIcon.Name \| null` | `null` |
|
|
19
|
+
| `passwordToggle` | `boolean` | `false` |
|
|
20
|
+
| `autocomplete` | `string \| null` | `null` |
|
|
21
|
+
|
|
22
|
+
Значение — `string`.
|
|
23
|
+
|
|
24
|
+
## Главное, что нужно знать
|
|
25
|
+
|
|
26
|
+
**Крестик очистки и переключатель пароля делят один постфикс.** На поле пароля с включённым
|
|
27
|
+
переключателем крестика не будет: показать оба некуда.
|
|
28
|
+
|
|
29
|
+
Крестик показывается при выполнении трёх условий сразу: `clearable` (по умолчанию включён),
|
|
30
|
+
в поле есть значение, поле не отключено. Из таб-порядка он исключён (`tabindex="-1"`) — до
|
|
31
|
+
вспомогательного аффорданса не должны доходить табом.
|
|
32
|
+
|
|
33
|
+
## Как этим пользоваться
|
|
34
|
+
|
|
35
|
+
- `type="time"` заведён намеренно: браузер сам отдаёт `HH:MM` и раскладку под локаль, а разбор
|
|
36
|
+
строки руками означал бы своё поле ввода времени рядом с китом.
|
|
37
|
+
- Клик по рамке (падинги принадлежат host-у, а не `<input>`) переводит фокус в поле — кроме
|
|
38
|
+
кликов по самому полю и по интерактивному постфиксу.
|
|
39
|
+
- Режим плоского чтения включает **вмещающий [`rt-field`](../field/CONTEXT.md)**, а не вход
|
|
40
|
+
компонента: `[readonly]` стоит на поле-обёртке. Пустое значение в этом режиме рисуется прочерком.
|
|
41
|
+
- Подсветка ошибки (`rt-input--invalid`) включается сама после касания — см. основу полей.
|
|
42
|
+
|
|
43
|
+
## Рядом
|
|
44
|
+
|
|
45
|
+
- [`rt-field`](../field/CONTEXT.md) — подпись, звёздочка, текст ошибки, режим чтения.
|
|
46
|
+
- [`rt-input-number`](../input-number/CONTEXT.md) — для чисел с группировкой разрядов.
|
|
47
|
+
- [`rt-autocomplete`](../autocomplete/CONTEXT.md) — поле с подсказками.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# `rt-input-number`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-field label="Сумма">
|
|
5
|
+
<rt-input-number prefix="₽" [formControl]="amount" [min]="0" [max]="1000000" [maxFractionDigits]="2" />
|
|
6
|
+
</rt-field>
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Свои входы поверх общих из [`RtFormControlBase`](../form-control/CONTEXT.md):
|
|
10
|
+
|
|
11
|
+
| вход | тип | умолчание |
|
|
12
|
+
| ------------------- | ---------------------- | ------------------------------ |
|
|
13
|
+
| `prefix` | `string \| null` | `null` |
|
|
14
|
+
| `iconLeft` | `IRtIcon.Name \| null` | `null` |
|
|
15
|
+
| `placeholder` | `string` | `''` |
|
|
16
|
+
| `min` / `max` | `number \| null` | `null` (сторона не ограничена) |
|
|
17
|
+
| `minFractionDigits` | `number` | `0` |
|
|
18
|
+
| `maxFractionDigits` | `number` | `2` |
|
|
19
|
+
|
|
20
|
+
Значение — `number | null`.
|
|
21
|
+
|
|
22
|
+
## Главное, что нужно знать
|
|
23
|
+
|
|
24
|
+
**Точность фиксирована, а не задана диапазоном.** Итоговое число знаков —
|
|
25
|
+
`max(minFractionDigits, min(maxFractionDigits, 20))`, и одно и то же значение служит и нижней, и
|
|
26
|
+
верхней границей. С умолчаниями это значит: `12` после ухода фокуса становится `12,00`. Целое
|
|
27
|
+
поле — это `maxFractionDigits: 0`.
|
|
28
|
+
|
|
29
|
+
**Разбор и запись зависят от локали (`LOCALE_ID`) и обязаны сходиться.** Разделитель разрядов
|
|
30
|
+
берётся у локали, и разбор снимает именно его: `1 234,56` в `ru`, `1,234.56` в `en-US`.
|
|
31
|
+
Разделителем дробной части считается только тот знак из «`.`», «`,`», которым в этой локали не
|
|
32
|
+
разделяются разряды, — поэтому в `ru` годятся оба, а в `en-US` запятая всегда читается как
|
|
33
|
+
разряды. Локаль-слепой разбор здесь и ломался: безусловная замена первой запятой на точку
|
|
34
|
+
превращала показанное `1,000` в `1`.
|
|
35
|
+
|
|
36
|
+
## Как этим пользоваться
|
|
37
|
+
|
|
38
|
+
- Это `<input type="text" inputmode="decimal">`, а не `type="number"`: у нативного числового поля
|
|
39
|
+
колесо мыши меняет значение, а разделители зависят от браузера.
|
|
40
|
+
- Во время набора разряды группируются на лету, а каретка удерживается на месте (позиция
|
|
41
|
+
пересчитывается по числу значимых символов слева от неё).
|
|
42
|
+
- Нечисловой ввод стрипается из отображения, но **прежнее значение держится до ухода фокуса** —
|
|
43
|
+
наружу оно не уходит. Пустое поле пишет `null`.
|
|
44
|
+
- На уходе фокуса значение зажимается в границы, округляется до точности и переписывается
|
|
45
|
+
отформатированным.
|
|
46
|
+
- **Крестик обнуляет поле, а не опустошает его** (`0,00`): у числового поля ноль осмыслен.
|
|
47
|
+
При нуле крестик не показывается — обнулять больше нечего.
|
|
48
|
+
- Префикс спрятан от скринридера: валюту он прочитает из подписи поля.
|
|
49
|
+
|
|
50
|
+
## Рядом
|
|
51
|
+
|
|
52
|
+
- [`rt-counter`](../counter/CONTEXT.md) — когда число меняют шагами, а не набирают.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# `rt-live-badge`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-live-badge label="Смотрят сейчас" [count]="visitors()" [active]="streamAlive()" />
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
| вход | тип | умолчание |
|
|
8
|
+
| -------- | ---------------- | ---------------------------------------- |
|
|
9
|
+
| `label` | `string` | `''` → переведённое `rtKit.uiLiveOnSite` |
|
|
10
|
+
| `count` | `number \| null` | `null` → прочерк `—` |
|
|
11
|
+
| `active` | `boolean` | `false` |
|
|
12
|
+
|
|
13
|
+
Выходов нет.
|
|
14
|
+
|
|
15
|
+
## Главное, что нужно знать
|
|
16
|
+
|
|
17
|
+
**`active` означает «поток жив», а не «счётчик больше нуля».** Ноль посетителей при живом
|
|
18
|
+
потоке — это данные; оборванный поток — их отсутствие, и выглядеть они должны по-разному.
|
|
19
|
+
Поэтому `[active]="true"` с `[count]="0"` — законное состояние, а `count === null` рисует
|
|
20
|
+
прочерк, а не ноль.
|
|
21
|
+
|
|
22
|
+
## Как этим пользоваться
|
|
23
|
+
|
|
24
|
+
- Пустая подпись равна отсутствию входа: подставляется переведённая формулировка кита.
|
|
25
|
+
Значит `[label]="maybeEmpty()"` безопасно.
|
|
26
|
+
- Живость дублируется атрибутом `data-active` — по нему бейдж находят проверки, не завязываясь
|
|
27
|
+
на BEM-класс.
|
|
28
|
+
|
|
29
|
+
## Рядом
|
|
30
|
+
|
|
31
|
+
- [`rt-tag`](../tag/CONTEXT.md) — статусная пилюля без счётчика.
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
/* rt-live-badge — пилюля живого счётчика в common/ui
|
|
1
|
+
/* rt-live-badge — пилюля живого счётчика в common/ui. ViewEncapsulation.None.
|
|
2
|
+
Хост адресуется по имени элемента, а не по классу блока: класс `.rt-live-badge` висит
|
|
3
|
+
и на `<rt-live-badge>`, и на корне шаблона, поэтому правила блока вложены в хост —
|
|
4
|
+
так они достаются только корню шаблона. */
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
rt-live-badge {
|
|
4
7
|
display: inline-flex;
|
|
5
8
|
|
|
6
9
|
.rt-live-badge {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# `rt-logo`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-logo variant="lockup" ariaLabel="Панель управления" [height]="75" [aspect]="0" />
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
| вход | тип | умолчание |
|
|
8
|
+
| ----------- | ------------------------ | ------------------------ |
|
|
9
|
+
| `variant` | `'wordmark' \| 'lockup'` | `'lockup'` |
|
|
10
|
+
| `height` | `number \| string` (px) | `0` → умолчание варианта |
|
|
11
|
+
| `aspect` | `number \| string` | `0` → умолчание варианта |
|
|
12
|
+
| `ariaLabel` | `string` | `''` |
|
|
13
|
+
|
|
14
|
+
Умолчания: `wordmark` — 51px высотой при отношении 5.4; `lockup` — 75px при 3.6.
|
|
15
|
+
Ширина считается как `round(height × aspect)`.
|
|
16
|
+
|
|
17
|
+
## Главное, что нужно знать
|
|
18
|
+
|
|
19
|
+
**Начертания кит не везёт.** Их даёт приложение четырьмя свойствами оформления на любом предке:
|
|
20
|
+
|
|
21
|
+
```css
|
|
22
|
+
:root {
|
|
23
|
+
--rt-logo-wordmark: url('/logo/wordmark.svg');
|
|
24
|
+
--rt-logo-wordmark-dark: url('/logo/wordmark-inverse.svg');
|
|
25
|
+
--rt-logo-tagline: url('/logo/tagline.svg');
|
|
26
|
+
--rt-logo-tagline-dark: url('/logo/tagline-inverse.svg');
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Пока свойства не заданы, компонент занимает место и не рисует ничего. Это ожидаемое состояние,
|
|
31
|
+
а не сбой.
|
|
32
|
+
|
|
33
|
+
Начертания лежат фоном, а не в `<img>`, потому что у тёмной темы своя пара файлов, и подмена
|
|
34
|
+
`background-image` — единственный способ переключить их одним правилом.
|
|
35
|
+
|
|
36
|
+
## Края
|
|
37
|
+
|
|
38
|
+
- **Ноль в `height`/`aspect` означает «не задано», а не нулевой размер.** Числовой вход иначе не
|
|
39
|
+
отличает одно от другого.
|
|
40
|
+
- `aspect` нужен, если пропорции своего файла не совпадают с умолчанием: ширину host-а кит
|
|
41
|
+
считает сам и чужих пропорций не знает — логотип обрежется или обрастёт пустотой.
|
|
42
|
+
- Подпись для скринридера задаёт приложение: названия продукта кит не знает.
|
|
@@ -8,12 +8,10 @@
|
|
|
8
8
|
* лежат фоном, а не в <img>: у тёмной темы своя пара файлов, и подмена
|
|
9
9
|
* background-image — единственный способ переключить их одним правилом.
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
.rt-logo {
|
|
12
12
|
display: inline-block;
|
|
13
13
|
line-height: 0;
|
|
14
|
-
}
|
|
15
14
|
|
|
16
|
-
.rt-logo {
|
|
17
15
|
&__wordmark,
|
|
18
16
|
&__tagline {
|
|
19
17
|
display: block;
|
|
@@ -48,17 +46,17 @@
|
|
|
48
46
|
/* Композиция lockup: начертание и слоган стоят колонкой и делят высоту host'а.
|
|
49
47
|
Пропорция та же, что у пары в макете: ~66.7% начертанию, ~22.1% слогану,
|
|
50
48
|
остальное — зазор между ними. */
|
|
51
|
-
:
|
|
49
|
+
.rt-logo:has(.rt-logo__tagline) {
|
|
52
50
|
display: flex;
|
|
53
51
|
flex-direction: column;
|
|
54
52
|
align-items: stretch;
|
|
55
53
|
justify-content: space-between;
|
|
56
54
|
}
|
|
57
55
|
|
|
58
|
-
:
|
|
56
|
+
.rt-logo:has(.rt-logo__tagline) .rt-logo__wordmark {
|
|
59
57
|
height: 66.7%;
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
:
|
|
60
|
+
.rt-logo:has(.rt-logo__tagline) .rt-logo__tagline {
|
|
63
61
|
height: 22.1%;
|
|
64
62
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# `rt-menu` + `rt-menu-item`
|
|
2
|
+
|
|
3
|
+
Меню действий: иконочная кнопка, раскрывающая список пунктов.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-menu ariaLabel="Действия со строкой" align="end">
|
|
7
|
+
<rt-menu-item label="Открыть" icon="ico-eye" (selected)="open()" />
|
|
8
|
+
<rt-menu-item
|
|
9
|
+
label="Удалить"
|
|
10
|
+
icon="ico-trash"
|
|
11
|
+
confirmMessage="Удалить запись? Действие необратимо."
|
|
12
|
+
confirmTitle="Удаление"
|
|
13
|
+
[danger]="true"
|
|
14
|
+
(selected)="remove()" />
|
|
15
|
+
</rt-menu>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## `rt-menu`
|
|
19
|
+
|
|
20
|
+
| вход | тип | умолчание |
|
|
21
|
+
| ----------- | ------------------ | ------------------------ |
|
|
22
|
+
| `icon` | `IRtIcon.Name` | `'ellipsis-h'` |
|
|
23
|
+
| `ariaLabel` | `string` | `''` → `rtKit.uiActions` |
|
|
24
|
+
| `align` | `'start' \| 'end'` | `'end'` |
|
|
25
|
+
| `disabled` | `boolean` | `false` |
|
|
26
|
+
|
|
27
|
+
## `rt-menu-item`
|
|
28
|
+
|
|
29
|
+
| вход | тип | умолчание |
|
|
30
|
+
| ------------------------------------- | ------------------------------------ | --------------------------------- |
|
|
31
|
+
| `label` | `string` | `''` |
|
|
32
|
+
| `icon` | `IRtIcon.Name \| null` | `null` |
|
|
33
|
+
| `danger` / `disabled` | `boolean` | `false` |
|
|
34
|
+
| `tooltip` | `string` | — (через `[rtTooltip]` на host-е) |
|
|
35
|
+
| `confirmMessage` | `string` | `''` |
|
|
36
|
+
| `confirmTitle` | `string \| null` | `null` |
|
|
37
|
+
| `confirmLabel` / `confirmCancelLabel` | `string` | `''` → словарь кита |
|
|
38
|
+
| `confirmTone` | `'danger' \| 'warning' \| 'primary'` | `'danger'` |
|
|
39
|
+
|
|
40
|
+
| выход | тип |
|
|
41
|
+
| ---------- | ------ |
|
|
42
|
+
| `selected` | `void` |
|
|
43
|
+
|
|
44
|
+
## Главное, что нужно знать
|
|
45
|
+
|
|
46
|
+
**Пункт не знает о меню.** Выбрав себя, он бросает всплывающее событие `rtMenuSelect`, а панель
|
|
47
|
+
закрывается, поймав его. Благодаря этому пункт остаётся самостоятельным и его можно завернуть
|
|
48
|
+
в свой компонент — лишь бы событие всплывало.
|
|
49
|
+
|
|
50
|
+
**Подтверждение у пункта — диалог, а не поповер.** Панель поповера крепится к пункту, а пункт
|
|
51
|
+
исчезает вместе с закрывшимся меню — держаться панели было бы не на чем. Поэтому
|
|
52
|
+
`confirmMessage` открывает `rt-menu-confirm-dialog`, и `selected` приходит **только** после
|
|
53
|
+
подтверждения.
|
|
54
|
+
|
|
55
|
+
## Края
|
|
56
|
+
|
|
57
|
+
- Отключённый пункт не выбирается и меню не закрывает: он выпадает из таб-порядка (`tabindex`
|
|
58
|
+
снимается) и помечается `aria-disabled`.
|
|
59
|
+
- Enter и Space на пункте работают как клик.
|
|
60
|
+
- Панель объявлена `role="menu"`, пункт — `role="menuitem"`.
|
|
61
|
+
|
|
62
|
+
## Рядом
|
|
63
|
+
|
|
64
|
+
- [`[rtConfirm]`](../confirm-popover/CONTEXT.md) — подтверждение под обычной кнопкой.
|
|
65
|
+
- [`rt-split-button`](../split-button/CONTEXT.md) — кнопка с прикреплённым меню.
|