@rt-tools/ui-kit-v2 0.1.0 → 0.4.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/README.md +25 -16
- package/fesm2022/rt-tools-ui-kit-v2.mjs +786 -1504
- package/fesm2022/rt-tools-ui-kit-v2.mjs.map +1 -1
- package/package.json +1 -2
- package/rt-tools-ui-kit-v2-0.4.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 +20 -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 +13 -4
- 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/footer/rt-dialog-footer.component.scss +20 -9
- package/src/lib/components/dialog/header/rt-dialog-header.component.scss +27 -15
- 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/download-link/rt-download-link.component.scss +13 -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/split-button/rt-split-button.component.scss +13 -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 +13 -4
- 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/src/styles/_semantic.scss +11 -2
- package/styles/tokens.css +26 -1
- package/types/rt-tools-ui-kit-v2.d.ts +365 -87
- package/rt-tools-ui-kit-v2-0.1.0.tgz +0 -0
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
/* rt-stat-tile — KPI-плитка в common/ui
|
|
1
|
+
/* rt-stat-tile — KPI-плитка в common/ui. ViewEncapsulation.None.
|
|
2
|
+
Хост адресуется по имени элемента, а не по классу блока: класс `.rt-stat-tile` висит
|
|
3
|
+
и на `<rt-stat-tile>`, и на корне шаблона, поэтому правила блока вложены в хост —
|
|
4
|
+
так они достаются только корню шаблона.
|
|
2
5
|
|
|
3
6
|
Рамку и фон плитка не рисует: плитки стоят рядом внутри одной карточки, а
|
|
4
7
|
разделители между ними — забота контейнера. */
|
|
5
8
|
|
|
6
|
-
|
|
9
|
+
rt-stat-tile {
|
|
7
10
|
display: block;
|
|
8
11
|
|
|
9
12
|
.rt-stat-tile {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# `rt-stepper`
|
|
2
|
+
|
|
3
|
+
Полоса прогресса по шагам: точки, подпись текущего шага и его описание.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-stepper [steps]="[{ label: 'Заявка', description: 'Заполните данные' }, …]" [currentIndex]="1" />
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
| вход | тип | умолчание |
|
|
10
|
+
| -------------- | ----------------------------------- | --------- |
|
|
11
|
+
| `steps` | `readonly { label, description }[]` | `[]` |
|
|
12
|
+
| `currentIndex` | `number \| string` | `0` |
|
|
13
|
+
|
|
14
|
+
Выходов нет — шаги переключает потребитель.
|
|
15
|
+
|
|
16
|
+
## Главное, что нужно знать
|
|
17
|
+
|
|
18
|
+
**Заполнение считается по промежуткам, а не по шагам.** Первый шаг даёт 0 %, последний — 100 %:
|
|
19
|
+
при трёх шагах середина это 50 %. Единственный шаг всегда даёт 0 % — делить не на что.
|
|
20
|
+
|
|
21
|
+
Номер шага за пределами набора зажимается **один раз, до всего остального**: и полоса, и якорь
|
|
22
|
+
подписи, и объявление для скринридера читают уже зажатый индекс. Иначе показ и объявление
|
|
23
|
+
расходятся — полоса упирается в 100 %, а на прогрессбаре оказывается `aria-valuenow` больше
|
|
24
|
+
`aria-valuemax`, и подпись шага пропадает. Пустой набор объявляется нулём.
|
|
25
|
+
|
|
26
|
+
## Края
|
|
27
|
+
|
|
28
|
+
- Описание разбивается на абзацы по пустой строке (`\n\n`); пустое описание не создаёт абзаца.
|
|
29
|
+
- Точки спрятаны от скринридера — прогресс он читает с самой полосы: `role="progressbar"`,
|
|
30
|
+
`aria-valuemin/max/now` и `aria-valuetext` с подписью текущего шага.
|
|
31
|
+
- Подпись текущего шага прижимается к началу, центру или концу полосы в зависимости от того,
|
|
32
|
+
где стоит шаг.
|
|
33
|
+
|
|
34
|
+
## Рядом
|
|
35
|
+
|
|
36
|
+
- [`rt-timeline`](../timeline/CONTEXT.md) — журнал событий, а не линейный прогресс.
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
/* rt-stepper — линейный прогресс-степпер в common/ui
|
|
1
|
+
/* rt-stepper — линейный прогресс-степпер в common/ui. ViewEncapsulation.None.
|
|
2
|
+
Хост адресуется по имени элемента, а не по классу блока: класс `.rt-stepper` висит
|
|
3
|
+
и на `<rt-stepper>`, и на корне шаблона, поэтому правила блока вложены в хост —
|
|
4
|
+
так они достаются только корню шаблона. */
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
rt-stepper {
|
|
4
7
|
display: block;
|
|
5
8
|
|
|
6
9
|
.rt-stepper {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# `rt-table`
|
|
2
|
+
|
|
3
|
+
Таблица поверх CDK Table: колонки объявляются директивами CDK, а кит добавляет состояния,
|
|
4
|
+
карточки для узкого экрана, меню строки и настройку колонок.
|
|
5
|
+
|
|
6
|
+
```html
|
|
7
|
+
<table rt-table tableId="tours" ariaLabel="Туры" [dataSource]="rows()" [columns]="columns" [loading]="loading()" [columnsConfig]="config">
|
|
8
|
+
<ng-container cdkColumnDef="title">
|
|
9
|
+
<th *cdkHeaderCellDef cdk-header-cell>Название</th>
|
|
10
|
+
<td *cdkCellDef="let row" cdk-cell>{{ row.title }}</td>
|
|
11
|
+
</ng-container>
|
|
12
|
+
<tr *cdkHeaderRowDef="columns" cdk-header-row></tr>
|
|
13
|
+
<tr *cdkRowDef="let row; columns: columns" cdk-row></tr>
|
|
14
|
+
</table>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
| вход | тип | умолчание |
|
|
18
|
+
| ----------------------------------- | ----------------------------- | ------------- |
|
|
19
|
+
| `columns` | `ReadonlyArray<string>` | `[]` |
|
|
20
|
+
| `columnsConfig` | `ReadonlyArray<ColumnConfig>` | `[]` |
|
|
21
|
+
| `tableId` | `string \| null` | `null` |
|
|
22
|
+
| `density` | `'default' \| 'compact'` | `'default'` |
|
|
23
|
+
| `cards` | `boolean` | `true` |
|
|
24
|
+
| `clickable` | `boolean` | `false` |
|
|
25
|
+
| `loading` / `fetching` | `boolean` | `false` |
|
|
26
|
+
| `skeletonRows` | `number` | 5 |
|
|
27
|
+
| `emptyMessage` / `emptyDescription` | `string` / `string \| null` | `''` / `null` |
|
|
28
|
+
| `emptyIcon` | `IRtIcon.Name \| null` | `'inbox'` |
|
|
29
|
+
| `showRowActions` | `boolean` | `false` |
|
|
30
|
+
| `sort` | `ISortModel<string> \| null` | `null` |
|
|
31
|
+
|
|
32
|
+
Выход: `sortChange`.
|
|
33
|
+
|
|
34
|
+
## Главное, что нужно знать
|
|
35
|
+
|
|
36
|
+
**`loading` и `fetching` — разные состояния.** Первая загрузка (`loading` при пустых данных)
|
|
37
|
+
подменяет строки заглушками; догрузка (`fetching`) оставляет уже показанное на месте — иначе
|
|
38
|
+
таблица мигала бы при каждой смене страницы.
|
|
39
|
+
|
|
40
|
+
**Карточки — это другая разметка, а не перестроенная стилями таблица.** Компонент сам решает по
|
|
41
|
+
ширине вьюпорта (`BreakpointsService.narrow`), что показать. Подписью в карточке служит **ключ
|
|
42
|
+
колонки**: заголовки живут в шаблонах ячеек CDK, куда карточка не дотягивается. Осмысленные
|
|
43
|
+
подписи даёт `columnsConfig`.
|
|
44
|
+
|
|
45
|
+
**Настройка колонок требует и `columnsConfig`, и `tableId`.** Выбор хранится в IndexedDB, и без
|
|
46
|
+
идентификатора таблицы его некуда положить — поэтому `canConfigure()` останется `false`.
|
|
47
|
+
|
|
48
|
+
## Края
|
|
49
|
+
|
|
50
|
+
- Пустой набор рисует заглушку с переведённым `uiNoData`; свой текст перебивает её.
|
|
51
|
+
- Колонка `rtRowActions` добавляется китом сама, когда включён `showRowActions`, и рисует
|
|
52
|
+
[`rt-menu`](../menu/CONTEXT.md) в строке.
|
|
53
|
+
- Спеки логики лежат рядом: сортировка и предикат действий строки проверены отдельно.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/* rt-table-settings-aside — ViewEncapsulation.None (common/ui). Своей раскладки нет:
|
|
2
|
-
chrome даёт rt-aside, тело — rt-table-settings-panel.
|
|
3
|
-
|
|
2
|
+
chrome даёт rt-aside, тело — rt-table-settings-panel. `.rt-table-settings-aside` —
|
|
3
|
+
сам хост, и он не должен добавлять бокс между асайдом и его местом в overlay. */
|
|
4
|
+
|
|
5
|
+
.rt-table-settings-aside {
|
|
4
6
|
display: contents;
|
|
5
7
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# `rt-tabs` + `[rtTab]`
|
|
2
|
+
|
|
3
|
+
Вкладки объявляются директивой на `<ng-template>` — содержимое неактивной вкладки не создаётся.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-tabs stretch [activeId]="tab()" (activeIdChange)="tab.set($event)">
|
|
7
|
+
<ng-template rtTab="main" label="Основное">…</ng-template>
|
|
8
|
+
<ng-template rtTab="extra" label="Дополнительно" [badge]="3">…</ng-template>
|
|
9
|
+
<ng-template rtTab="docs" label="Документы" icon="file" [invalid]="hasErrors()">…</ng-template>
|
|
10
|
+
</rt-tabs>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## `rt-tabs`
|
|
14
|
+
|
|
15
|
+
| вход | тип | умолчание |
|
|
16
|
+
| ------------------- | ---------------------------- | -------------- |
|
|
17
|
+
| `activeId` | `IRtTabs.Id \| null` | `null` |
|
|
18
|
+
| `direction` | `'horizontal' \| 'vertical'` | `'horizontal'` |
|
|
19
|
+
| `stretch` | `boolean` | `false` |
|
|
20
|
+
| `contentScrollable` | `boolean` | `true` |
|
|
21
|
+
|
|
22
|
+
Выход: `activeIdChange: IRtTabs.Id`.
|
|
23
|
+
|
|
24
|
+
## `[rtTab]`
|
|
25
|
+
|
|
26
|
+
| вход | тип | умолчание |
|
|
27
|
+
| --------------------------------- | --------------------------------------------- | --------------------- |
|
|
28
|
+
| `rtTab` | `IRtTabs.Id` — идентификатор вкладки | `''` |
|
|
29
|
+
| `label` | `string` | `''` |
|
|
30
|
+
| `titleTemplate` | `TemplateRef \| null` | `null` |
|
|
31
|
+
| `icon` / `iconColor` | `IRtIcon.Name \| null` / `IRtTabs.TitleColor` | `null` / `'current'` |
|
|
32
|
+
| `badge` | `string \| number \| null` | `null` |
|
|
33
|
+
| `disabled` / `hidden` / `invalid` | `boolean` | `false` |
|
|
34
|
+
| `invalidMessage` | `string` | `''` → `uiTabInvalid` |
|
|
35
|
+
|
|
36
|
+
## Главное, что нужно знать
|
|
37
|
+
|
|
38
|
+
**Вход `activeId` необязателен.** Без него компонент помнит выбор сам — управляемый режим нужен
|
|
39
|
+
только когда вкладка лежит в адресе страницы.
|
|
40
|
+
|
|
41
|
+
**Идентификатор, которого нет, откатывается на первую доступную вкладку.** То же с отключённой:
|
|
42
|
+
активной она стать не может. Так вкладка из старой ссылки не оставляет пустой экран.
|
|
43
|
+
|
|
44
|
+
## Клавиатура и доступность
|
|
45
|
+
|
|
46
|
+
- Стрелки (влево/вправо, а в вертикальной раскладке вверх/вниз) перешагивают отключённые вкладки
|
|
47
|
+
и заворачиваются по кругу; `Home`/`End` — к первой и последней.
|
|
48
|
+
- В таб-порядке стоит **только активная** вкладка (`tabindex="0"`), остальные достаются стрелками.
|
|
49
|
+
- Панель и вкладка связаны в обе стороны: `aria-controls` ↔ `aria-labelledby`.
|
|
50
|
+
|
|
51
|
+
## Края
|
|
52
|
+
|
|
53
|
+
- `hidden` убирает вкладку из шапки целиком; `disabled` оставляет её видимой, но некликабельной.
|
|
54
|
+
- У невалидной вкладки появляется подсказка с текстом `invalidMessage`.
|
|
55
|
+
- Стрелки прокрутки шапки есть в разметке всегда — показывает их модификатор `--visible`,
|
|
56
|
+
который выставляется по замеру ширины после отрисовки.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# `rt-tag`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-tag value="Активен" severity="success" shape="pill" appearance="solid" icon="check" [closable]="true" (closed)="onClosed($event)" />
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
| вход | тип | умолчание |
|
|
8
|
+
| ------------------ | -------------------------------------------------------------------------- | -------------- |
|
|
9
|
+
| `value` | `string` | **обязателен** |
|
|
10
|
+
| `severity` | `'info' \| 'success' \| 'warning' \| 'danger' \| 'secondary' \| 'neutral'` | `'neutral'` |
|
|
11
|
+
| `shape` | `'pill' \| 'square'` | `'pill'` |
|
|
12
|
+
| `appearance` | `'solid' \| 'outlined'` | `'solid'` |
|
|
13
|
+
| `radius` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'full' \| null` | `null` |
|
|
14
|
+
| `icon` / `iconEnd` | `IRtIcon.Name \| null` | `null` |
|
|
15
|
+
| `closable` | `boolean` | `false` |
|
|
16
|
+
|
|
17
|
+
| выход | тип |
|
|
18
|
+
| -------- | ------------ |
|
|
19
|
+
| `closed` | `MouseEvent` |
|
|
20
|
+
|
|
21
|
+
## Главное, что нужно знать
|
|
22
|
+
|
|
23
|
+
**Крестик не закрывает тег.** `closed` только сообщает о клике — убрать тег из списка обязан
|
|
24
|
+
потребитель. Компонент состояния не держит.
|
|
25
|
+
|
|
26
|
+
Клик по крестику **не всплывает** (`stopPropagation`): пилюля целиком часто сама кликабельна,
|
|
27
|
+
и без этого одно нажатие срабатывало бы дважды.
|
|
28
|
+
|
|
29
|
+
## Как этим пользоваться
|
|
30
|
+
|
|
31
|
+
- `radius` перебивает скругление, заданное формой. Без него класс скругления не выводится
|
|
32
|
+
вовсе — радиус берётся из `shape`.
|
|
33
|
+
- Палитра дублируется атрибутом `data-severity` — по нему тег находят проверки и внешние стили,
|
|
34
|
+
не завязываясь на BEM-класс.
|
|
35
|
+
- Подпись крестика для скринридера берётся из словаря кита (`uiRemove`). Без
|
|
36
|
+
`provideRtKitLabels()` там будет пусто.
|
|
37
|
+
|
|
38
|
+
## Края
|
|
39
|
+
|
|
40
|
+
- `value` — обязательный вход: тег без текста не бывает. Иконка без текста — это
|
|
41
|
+
[`rt-icon-button`](../icon-button/CONTEXT.md), а не тег.
|
|
42
|
+
- Модификаторы выводятся BEM-директивами: `rt-tag--severity--success`, `rt-tag--shape--square`,
|
|
43
|
+
булев `closable` даёт `rt-tag--closable` без значения.
|
|
44
|
+
|
|
45
|
+
## Рядом
|
|
46
|
+
|
|
47
|
+
- [`rt-live-badge`](../live-badge/CONTEXT.md) — пилюля живого счётчика, а не статуса.
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
/* rt-tag — status pill в common/ui
|
|
1
|
+
/* rt-tag — status pill в common/ui. ViewEncapsulation.None.
|
|
2
|
+
Хост адресуется по имени элемента, а не по классу блока: класс `.rt-tag` висит
|
|
3
|
+
и на `<rt-tag>`, и на корне шаблона, поэтому правила блока вложены в хост —
|
|
4
|
+
так они достаются только корню шаблона. */
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
rt-tag {
|
|
4
7
|
display: inline-flex;
|
|
5
8
|
|
|
6
9
|
.rt-tag {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# `rt-textarea`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-field label="Комментарий">
|
|
5
|
+
<rt-textarea resize="vertical" placeholder="Что уточнить" [formControl]="comment" [rows]="5" />
|
|
6
|
+
</rt-field>
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Свои входы поверх общих из [`RtFormControlBase`](../form-control/CONTEXT.md):
|
|
10
|
+
|
|
11
|
+
| вход | тип | умолчание |
|
|
12
|
+
| ------------- | ---------------------- | ------------ |
|
|
13
|
+
| `placeholder` | `string` | `''` |
|
|
14
|
+
| `rows` | `number \| string` | `3` |
|
|
15
|
+
| `resize` | `'none' \| 'vertical'` | `'vertical'` |
|
|
16
|
+
| `readonly` | `boolean` | `false` |
|
|
17
|
+
|
|
18
|
+
Значение — `string`.
|
|
19
|
+
|
|
20
|
+
## Главное, что нужно знать
|
|
21
|
+
|
|
22
|
+
**Крестик очистки не рисуется.** Вход `clearable` наследуется от общей основы полей, но разметка
|
|
23
|
+
многострочного поля его не использует: в многострочном вводе он нетипичен.
|
|
24
|
+
|
|
25
|
+
**Два разных «только для чтения»:**
|
|
26
|
+
|
|
27
|
+
- свой вход `[readonly]` оставляет `<textarea>` на месте и запрещает правку (нативный `readOnly`);
|
|
28
|
+
- режим от вмещающего [`rt-field`](../field/CONTEXT.md) подменяет контрол плоским текстом.
|
|
29
|
+
|
|
30
|
+
## Края
|
|
31
|
+
|
|
32
|
+
- `font-family: inherit` прописан явно: без него `<textarea>` падает на моноширинный шрифт
|
|
33
|
+
браузера.
|
|
34
|
+
- Горизонтальное растягивание не предусмотрено — только `none` и `vertical`.
|
|
35
|
+
|
|
36
|
+
## Рядом
|
|
37
|
+
|
|
38
|
+
- [`rt-rich-editor`](../rich-editor/CONTEXT.md) — когда нужна разметка внутри текста.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# `rt-theme-toggle`
|
|
2
|
+
|
|
3
|
+
Переключатель светлой и тёмной темы.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-theme-toggle appearance="icon" />
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
| вход | тип | умолчание |
|
|
10
|
+
| ------------ | -------------------- | --------- |
|
|
11
|
+
| `appearance` | `'icon' \| 'switch'` | `'icon'` |
|
|
12
|
+
|
|
13
|
+
Выходов нет.
|
|
14
|
+
|
|
15
|
+
## Главное, что нужно знать
|
|
16
|
+
|
|
17
|
+
**Своего состояния у контрола нет — тему держит `ThemeService`.**
|
|
18
|
+
Он пишет `data-theme` на `<html>` и сохраняет выбор в хранилище устройства, поэтому тема
|
|
19
|
+
переживает перезагрузку, а два переключателя на странице всегда согласованы.
|
|
20
|
+
|
|
21
|
+
**Иконка показывает, куда переключит**, а не текущее состояние: в светлой теме это луна.
|
|
22
|
+
В виде кнопки текущая тёмная тема помечается `aria-pressed="true"`.
|
|
23
|
+
|
|
24
|
+
## Края
|
|
25
|
+
|
|
26
|
+
- Подпись берётся из словаря кита (`themeToggleLabel`) и вычисляется один раз при создании.
|
|
27
|
+
- В тестах между случаями надо чистить хранилище: иначе выбор предыдущего теста становится
|
|
28
|
+
начальным состоянием следующего.
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
:
|
|
1
|
+
/* rt-theme-toggle — ViewEncapsulation.None: правило таргетирует BEM-класс блока.
|
|
2
|
+
`.rt-theme-toggle` — сам хост: своей разметки блока у компонента нет, внутри
|
|
3
|
+
только rt-toggle-switch либо rt-icon-button. */
|
|
4
|
+
|
|
5
|
+
.rt-theme-toggle {
|
|
2
6
|
display: inline-flex;
|
|
3
7
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# `rt-thread-list`
|
|
2
|
+
|
|
3
|
+
Список переписок/заявок с поиском, фильтрами и бесконечной прокруткой. Строку рисует шаблон
|
|
4
|
+
потребителя.
|
|
5
|
+
|
|
6
|
+
```html
|
|
7
|
+
<rt-thread-list
|
|
8
|
+
[rows]="rows()"
|
|
9
|
+
[activeId]="activeId()"
|
|
10
|
+
[loading]="loading()"
|
|
11
|
+
[hasMore]="hasMore()"
|
|
12
|
+
(selectRow)="open($event)"
|
|
13
|
+
(openInNewTab)="openInTab($event)"
|
|
14
|
+
(searchChange)="search($event)"
|
|
15
|
+
(loadMore)="next()">
|
|
16
|
+
<ng-template rtThreadListRow let-row>…</ng-template>
|
|
17
|
+
<ng-template rtThreadListFilters>…</ng-template>
|
|
18
|
+
</rt-thread-list>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Входы: `rows`, `activeId`, `searchPlaceholder`, `emptyText`, `loading`, `fetching`, `hasMore`,
|
|
22
|
+
`filtersActive`. Выходы: `selectRow`, `openInNewTab`, `searchChange`, `loadMore`.
|
|
23
|
+
|
|
24
|
+
Строка обязана иметь `id`, `hasUnread` и (необязательно) `overdue` — это всё, что список о ней знает.
|
|
25
|
+
|
|
26
|
+
## Главное, что нужно знать
|
|
27
|
+
|
|
28
|
+
**Ctrl/Cmd+клик просит открыть в новой вкладке, а не выбирает строку.** Список — это навигация,
|
|
29
|
+
и привычка «Ctrl+клик открывает рядом» должна работать.
|
|
30
|
+
|
|
31
|
+
**`loading` подменяет строки заглушками только при пустом списке.** Загрузка поверх уже
|
|
32
|
+
показанных строк их не трогает — иначе список мигал бы при каждой смене фильтра.
|
|
33
|
+
|
|
34
|
+
**Поиск отдаёт значение с задержкой** и без повторов (`searchDebounce` + `distinctUntilChanged`),
|
|
35
|
+
уже обрезанное по краям.
|
|
36
|
+
|
|
37
|
+
## Края
|
|
38
|
+
|
|
39
|
+
- Якорь догрузки (`rtInfiniteScroll`) появляется только при `hasMore`.
|
|
40
|
+
- Кнопка фильтров рисуется, только если объявлен шаблон `[rtThreadListFilters]`;
|
|
41
|
+
`filtersActive` рисует на ней точку.
|
|
42
|
+
- Собственное поле поиска можно заменить шаблоном `[rtThreadListSearch]`.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# `rt-timeline`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-timeline
|
|
5
|
+
[steps]="[
|
|
6
|
+
{ label: 'Заявка создана', meta: '12 марта, 10:00', actor: 'Иванов', status: 'complete' },
|
|
7
|
+
{ label: 'На проверке', meta: '12 марта, 12:30', status: 'current' },
|
|
8
|
+
{ label: 'Решение', status: 'pending' },
|
|
9
|
+
]" />
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
| вход | тип | умолчание |
|
|
13
|
+
| ------- | --------------------------------------------- | --------- |
|
|
14
|
+
| `steps` | `readonly { label, meta?, actor?, status }[]` | `[]` |
|
|
15
|
+
|
|
16
|
+
`status` — `'complete' \| 'current' \| 'pending'`. Выходов нет: лента только показывает.
|
|
17
|
+
|
|
18
|
+
## Главное, что нужно знать
|
|
19
|
+
|
|
20
|
+
**Порядок шагов — часть смысла**, поэтому лента рисуется тегом `<ol>`, а не набором блоков.
|
|
21
|
+
|
|
22
|
+
Соединительная линия рисуется **между** пунктами и не рисуется после последнего: хвост под
|
|
23
|
+
последним шагом читался бы как «продолжение есть».
|
|
24
|
+
|
|
25
|
+
## Края
|
|
26
|
+
|
|
27
|
+
- `meta` и `actor` необязательны и рисуются по отдельности: исполнитель без времени допустим.
|
|
28
|
+
- Состояние дублируется атрибутом `data-status` — по нему шаг находят проверки.
|
|
29
|
+
- Пустой набор рисует пустой `<ol>`, а не заглушку: текст «событий нет» подставляет потребитель.
|
|
30
|
+
|
|
31
|
+
## Рядом
|
|
32
|
+
|
|
33
|
+
- [`rt-stepper`](../stepper/CONTEXT.md) — линейный прогресс по шагам, а не журнал событий.
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
/* rt-timeline — вертикальный таймлайн-степпер в common/ui
|
|
1
|
+
/* rt-timeline — вертикальный таймлайн-степпер в common/ui. ViewEncapsulation.None.
|
|
2
|
+
Хост адресуется по имени элемента, а не по классу блока: класс `.rt-timeline` висит
|
|
3
|
+
и на `<rt-timeline>`, и на корне шаблона, поэтому правила блока вложены в хост —
|
|
4
|
+
так они достаются только корню шаблона. */
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
rt-timeline {
|
|
4
7
|
display: block;
|
|
5
8
|
|
|
6
9
|
.rt-timeline {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# `rt-toaster` + `rt-toast`
|
|
2
|
+
|
|
3
|
+
Стопка всплывающих уведомлений. В разметку ставится **один** `rt-toaster` — обычно в корне
|
|
4
|
+
приложения; отдельные `rt-toast` он создаёт сам.
|
|
5
|
+
|
|
6
|
+
```html
|
|
7
|
+
<!-- один раз на приложение -->
|
|
8
|
+
<rt-toaster position="bottom-right" [duration]="4000" [visibleToasts]="3" />
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
readonly #notifications: NotificationBus = inject(NotificationBus);
|
|
13
|
+
|
|
14
|
+
this.#notifications.success('Сохранено');
|
|
15
|
+
this.#notifications.error('Не удалось сохранить', 'danger', {
|
|
16
|
+
description: 'Проверьте соединение',
|
|
17
|
+
action: { label: 'Повторить', handler: (): void => this.retry() },
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
| вход `rt-toaster` | тип | умолчание |
|
|
22
|
+
| ----------------- | ------------------------------------------------------------------------------------------------- | ---------------- |
|
|
23
|
+
| `position` | `'top-left' \| 'top-center' \| 'top-right' \| 'bottom-left' \| 'bottom-center' \| 'bottom-right'` | `'bottom-right'` |
|
|
24
|
+
| `duration` | `number \| string` (мс) | `4000` |
|
|
25
|
+
| `visibleToasts` | `number \| string` | `3` |
|
|
26
|
+
| `expand` | `boolean` | `false` |
|
|
27
|
+
|
|
28
|
+
## Главное, что нужно знать
|
|
29
|
+
|
|
30
|
+
**Тосты показываются не входом, а шиной.** Стопка подписана на
|
|
31
|
+
`NotificationBus` и других способов показать тост
|
|
32
|
+
нет. Шина — `providedIn: 'root'`, поэтому её достаточно инжектировать где угодно.
|
|
33
|
+
|
|
34
|
+
Четыре метода шины задают палитру и иконку: `success` → `check-circle`, `info` → `info-circle`,
|
|
35
|
+
`warning` → `exclamation-circle`, `error` → `times-circle`.
|
|
36
|
+
|
|
37
|
+
## Как этим пользоваться
|
|
38
|
+
|
|
39
|
+
- Новый тост встаёт **первым** — стопка растёт сверху.
|
|
40
|
+
- Наведение на стопку разворачивает её и приостанавливает исчезновение; нажатая кнопка мыши
|
|
41
|
+
удерживает развёрнутое состояние до отпускания.
|
|
42
|
+
- `action` и `secondaryAction` рисуются кнопками внутри тоста и зовут переданные обработчики.
|
|
43
|
+
- `meta` — приписка над сообщением (номер заявки), `description` — вторая строка под ним.
|
|
44
|
+
- Положение раскладывается на два модификатора: `rt-toaster--y--bottom` и `rt-toaster--x--right`.
|
|
45
|
+
|
|
46
|
+
## Доступность
|
|
47
|
+
|
|
48
|
+
Стопка — `role="region"` с переведённой подписью, каждый тост — `role="status"` с
|
|
49
|
+
`aria-atomic="true"`: скринридер читает уведомление целиком, а не по изменившимся кускам.
|
|
50
|
+
|
|
51
|
+
## Рядом
|
|
52
|
+
|
|
53
|
+
- [`rt-message`](../message/CONTEXT.md) — постоянное сообщение внутри страницы, а не всплывающее.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# `rt-toggle-button-group<T>`
|
|
2
|
+
|
|
3
|
+
Группа сегментированных кнопок: один выбранный вариант из нескольких.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-toggle-button-group
|
|
7
|
+
ariaLabel="Период"
|
|
8
|
+
[options]="[{ value: 'day', label: 'День' }, { value: 'week', label: 'Неделя' }]"
|
|
9
|
+
[value]="period()"
|
|
10
|
+
(valueChange)="period.set($event)" />
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| вход | тип | умолчание |
|
|
14
|
+
| ------------------------ | ------------------------------------------------ | -------------- |
|
|
15
|
+
| `options` | `ReadonlyArray<{ value, label, icon?, title? }>` | **обязателен** |
|
|
16
|
+
| `value` | `T \| undefined` | `undefined` |
|
|
17
|
+
| `ariaLabel` | `string \| null` | `null` |
|
|
18
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` |
|
|
19
|
+
| `disabled` / `fullWidth` | `boolean` | `false` |
|
|
20
|
+
|
|
21
|
+
Выход: `valueChange`.
|
|
22
|
+
|
|
23
|
+
## Главное, что нужно знать
|
|
24
|
+
|
|
25
|
+
**Группа не держит состояния.** Нажатие только просит сменить значение; подсветка сдвинется,
|
|
26
|
+
когда потребитель вернёт новое `value`. Повторное нажатие по выбранному тоже поднимает событие —
|
|
27
|
+
снимать выбор группа не умеет.
|
|
28
|
+
|
|
29
|
+
## Края
|
|
30
|
+
|
|
31
|
+
- Крайние кнопки помечены модификаторами `--first` / `--last`: по ним скругляются углы группы.
|
|
32
|
+
- `title` варианта превращается во всплывающую подсказку.
|
|
33
|
+
- Размер иконки выводится из размера группы: `sm` → 12px, `md`/`lg` → 16px.
|
|
34
|
+
|
|
35
|
+
## Рядом
|
|
36
|
+
|
|
37
|
+
- [`rt-filter-control`](../filter-control/CONTEXT.md) — та же группа, но с автоматическим
|
|
38
|
+
переключением на список на узком экране.
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
/* rt-toggle-button-group — segmented control.
|
|
2
|
-
|
|
1
|
+
/* rt-toggle-button-group — segmented control. ViewEncapsulation.None.
|
|
2
|
+
Хост адресуется по имени элемента, а не по классу блока: класс
|
|
3
|
+
`.rt-toggle-button-group` висит и на `<rt-toggle-button-group>`, и на корне
|
|
4
|
+
шаблона, поэтому правила блока вложены в хост — так они достаются только
|
|
5
|
+
корню шаблона.
|
|
3
6
|
Размеры — три тира sm/md/lg, общая высота совпадает с rt-button
|
|
4
7
|
(sm 30 / md 40 / lg 50px).
|
|
5
8
|
Цвета — только семантические --rt-* токены. */
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
rt-toggle-button-group {
|
|
8
11
|
display: inline-flex;
|
|
9
12
|
|
|
10
13
|
/* Локальные overridable custom properties — позволяют потребителю
|
|
@@ -114,7 +117,7 @@
|
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
/* full-width: группа растягивается на контейнер, опции делят ширину поровну. */
|
|
117
|
-
|
|
120
|
+
rt-toggle-button-group.rt-toggle-button-group--full-width {
|
|
118
121
|
display: flex;
|
|
119
122
|
width: 100%;
|
|
120
123
|
|
|
@@ -131,8 +134,10 @@
|
|
|
131
134
|
|
|
132
135
|
/* Dark theme: `--rt-color-bg-surface-subtle` совпадает с `--rt-color-bg-surface`
|
|
133
136
|
в палитре (см. styles/_theme-dark.scss), поэтому track теряет контраст с
|
|
134
|
-
активным сегментом. Локально перекрашиваем track в более тёмный neutral.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
+
активным сегментом. Локально перекрашиваем track в более тёмный neutral.
|
|
138
|
+
Раньше правило было записано через `:host-context`, который под None не
|
|
139
|
+
совпадает ни с чем: тему задаёт предок, им же и адресуемся. */
|
|
140
|
+
:root[data-theme='dark'] rt-toggle-button-group,
|
|
141
|
+
html.rt-theme-dark rt-toggle-button-group {
|
|
137
142
|
--rt-toggle-button-group-track-bg: var(--rt-neutral-900);
|
|
138
143
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# `rt-toggle-switch`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-toggle-switch ariaLabel="Тёмная тема" size="md" iconOff="ico-sun" iconOn="ico-moon" [formControl]="darkTheme" />
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
| вход | тип | умолчание |
|
|
8
|
+
| -------------------- | ---------------------- | --------- |
|
|
9
|
+
| `inputId` | `string \| null` | `null` |
|
|
10
|
+
| `ariaLabel` | `string \| null` | `null` |
|
|
11
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` |
|
|
12
|
+
| `iconOff` / `iconOn` | `IRtIcon.Name \| null` | `null` |
|
|
13
|
+
| `disabled` | `boolean` | `false` |
|
|
14
|
+
|
|
15
|
+
Значение — `boolean` через `ControlValueAccessor`. Своей подписи на экране нет.
|
|
16
|
+
|
|
17
|
+
## Главное, что нужно знать
|
|
18
|
+
|
|
19
|
+
**`role="switch"`, а не `checkbox`.** Скринридер произносит «switch off/on» вместо «checkbox
|
|
20
|
+
unchecked» — для тумблера это точнее. Своего текста у контрола нет, поэтому подпись задаётся
|
|
21
|
+
входом `ariaLabel` или внешним `<label for>` через `inputId`.
|
|
22
|
+
|
|
23
|
+
## Как этим пользоваться
|
|
24
|
+
|
|
25
|
+
- Умолчание размера — `sm`: он совпадает по высоте с плотными контролами формы, где тумблер
|
|
26
|
+
стоит в одном ряду с текстом.
|
|
27
|
+
- Достаточно одной из двух иконок, чтобы включился режим с иконками
|
|
28
|
+
(`rt-toggle-switch--with-icons`). Бегунок непрозрачен и наезжает на иконку текущего состояния,
|
|
29
|
+
так что видна всегда иконка того состояния, куда переключится контрол.
|
|
30
|
+
- Отключение приходит двумя путями — вход и `FormControl.disable()` — и оба пишут в один сигнал.
|
|
31
|
+
|
|
32
|
+
## Рядом
|
|
33
|
+
|
|
34
|
+
- [`rt-checkbox`](../checkbox/CONTEXT.md) — для «отмечено», а не «включено».
|
|
35
|
+
- [`rt-theme-toggle`](../theme-toggle/CONTEXT.md) — готовый переключатель темы поверх этого.
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
/* rt-toggle-switch — переключатель в common/ui. ViewEncapsulation.None.
|
|
2
|
+
Хост адресуется по имени элемента, а не по классу блока: класс `.rt-toggle-switch` висит
|
|
3
|
+
и на `<rt-toggle-switch>`, и на корне шаблона, поэтому правила блока вложены в хост —
|
|
4
|
+
так они достаются только корню шаблона. */
|
|
5
|
+
|
|
6
|
+
rt-toggle-switch {
|
|
2
7
|
display: inline-flex;
|
|
3
8
|
}
|
|
4
9
|
|
|
5
|
-
.rt-toggle-switch {
|
|
10
|
+
rt-toggle-switch .rt-toggle-switch {
|
|
6
11
|
/* Геометрия размера. База — `sm`; модификаторы перекрывают только числа,
|
|
7
12
|
правила ниже к размерам не привязаны. `thumb-travel` держим явным
|
|
8
13
|
числом, а не calc из ширины: каждое значение сверяется с макетом. */
|
|
@@ -58,7 +63,11 @@
|
|
|
58
63
|
box-shadow var(--rt-duration-fast) ease;
|
|
59
64
|
}
|
|
60
65
|
|
|
61
|
-
|
|
66
|
+
/* Элемент адресуется классом, а не вторым `&`: родитель здесь составной
|
|
67
|
+
(`rt-toggle-switch .rt-toggle-switch`), и второй `&` развернулся бы в него целиком —
|
|
68
|
+
то есть в селектор, требующий вложенного переключателя. Включённое состояние
|
|
69
|
+
из-за этого не применялось: трек оставался серым, бегунок — слева. */
|
|
70
|
+
&--on .rt-toggle-switch__track {
|
|
62
71
|
background: var(--rt-toggle-color-track-on);
|
|
63
72
|
box-shadow: none;
|
|
64
73
|
}
|
|
@@ -92,7 +101,7 @@
|
|
|
92
101
|
transition: transform var(--rt-duration-fast) ease;
|
|
93
102
|
}
|
|
94
103
|
|
|
95
|
-
&--on
|
|
104
|
+
&--on .rt-toggle-switch__thumb {
|
|
96
105
|
transform: translateX(var(--rt-toggle-thumb-travel));
|
|
97
106
|
}
|
|
98
107
|
}
|