@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,53 @@
|
|
|
1
|
+
# `[rtButton]`
|
|
2
|
+
|
|
3
|
+
Это **директива**, а не компонент: она вешается на существующий `<button>` или `<a>`.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<button rtButton label="Войти" theme="primary"></button>
|
|
7
|
+
<button rtButton label="Скачать" icon="ico-download" iconPos="left" theme="success"></button>
|
|
8
|
+
<button rtButton icon="ico-pencil" appearance="text"></button>
|
|
9
|
+
<button rtButton label="Сохранение…" [loading]="saving()" [disabled]="saving()"></button>
|
|
10
|
+
<a rtButton label="На главную" [routerLink]="'/'"></a>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| вход | тип | умолчание |
|
|
14
|
+
| ------------- | -------------------------------------------------------------------------- | -------------------------- |
|
|
15
|
+
| `label` | `string \| null` | `null` |
|
|
16
|
+
| `icon` | `string \| null` (имя из набора `rt-icon`) | `null` |
|
|
17
|
+
| `iconPos` | `'left' \| 'right'` | `'left'` |
|
|
18
|
+
| `theme` | `'primary' \| 'secondary' \| 'success' \| 'warning' \| 'danger' \| 'info'` | `'primary'` |
|
|
19
|
+
| `appearance` | `'filled' \| 'outlined' \| 'text'` | `'filled'` |
|
|
20
|
+
| `size` | `'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl'` (30 / 40 / 50 / 60 / 70 px) | `'md'` |
|
|
21
|
+
| `rounded` | `boolean` | `false` |
|
|
22
|
+
| `loading` | `boolean` | `false` |
|
|
23
|
+
| `loadingIcon` | `string \| null` | `null` → встроенное кольцо |
|
|
24
|
+
|
|
25
|
+
Своего выхода нет — клик слушается нативным `(click)` на самом элементе.
|
|
26
|
+
|
|
27
|
+
## Главное, что нужно знать
|
|
28
|
+
|
|
29
|
+
**Содержимое кнопки директива рисует сама** через `Renderer2` — подпись и иконка не проецируются.
|
|
30
|
+
Поэтому `<button rtButton label="…"></button>` пишется с пустым телом: всё, что положить внутрь
|
|
31
|
+
руками, будет соседствовать с дорисованным, а не заменять его.
|
|
32
|
+
|
|
33
|
+
**Класс выводится только на отличие от умолчания.** `theme="primary"`, `appearance="filled"` и
|
|
34
|
+
`size="md"` не дают ни одного класса — стиль по умолчанию живёт на самом блоке `rt-button`.
|
|
35
|
+
|
|
36
|
+
## Края
|
|
37
|
+
|
|
38
|
+
- **Выход из загрузки возвращает содержимое по концу затухания кольца, но не зависит от
|
|
39
|
+
анимации.** Основной путь — `animationend`; поверх него висит страховочный таймаут на ту же
|
|
40
|
+
длительность с запасом, и оба пути идемпотентны. Без страховки кнопка, спрятанная в момент
|
|
41
|
+
снятия `loading` (свёрнутая панель, неактивная вкладка), не получала бы `animationend` вовсе
|
|
42
|
+
и навсегда оставалась голым кольцом.
|
|
43
|
+
- Очистка перед перерисовкой идёт **по классам**, а не по сохранённым ссылкам: после гидратации
|
|
44
|
+
серверной разметки ссылки принадлежат другому экземпляру директивы, и подпись удваивалась бы.
|
|
45
|
+
- Имя иконки типизировано как обычная строка (совместимость со старыми потребителями). Неизвестное
|
|
46
|
+
имя не роняет ничего — `<use>` просто не разрешится, и место останется пустым.
|
|
47
|
+
- Отключение — нативный атрибут `disabled` на самом элементе, у директивы своего входа для этого
|
|
48
|
+
нет.
|
|
49
|
+
|
|
50
|
+
## Рядом
|
|
51
|
+
|
|
52
|
+
- [`rt-icon-button`](../icon-button/CONTEXT.md) — кнопка без подписи, с обязательным `ariaLabel`.
|
|
53
|
+
- [`rt-split-button`](../split-button/CONTEXT.md) — кнопка с прикреплённым меню.
|
|
@@ -225,6 +225,22 @@
|
|
|
225
225
|
--rt-btn-spinner-size: 1.125rem;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
&--xl {
|
|
229
|
+
--rt-btn-height: 60px;
|
|
230
|
+
--rt-btn-padding: 0 1.75rem;
|
|
231
|
+
--rt-btn-font-size: 1.125rem;
|
|
232
|
+
--rt-btn-icon-size: 1.25rem;
|
|
233
|
+
--rt-btn-spinner-size: 1.25rem;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
&--2xl {
|
|
237
|
+
--rt-btn-height: 70px;
|
|
238
|
+
--rt-btn-padding: 0 2rem;
|
|
239
|
+
--rt-btn-font-size: 1.1875rem;
|
|
240
|
+
--rt-btn-icon-size: 1.375rem;
|
|
241
|
+
--rt-btn-spinner-size: 1.375rem;
|
|
242
|
+
}
|
|
243
|
+
|
|
228
244
|
&--icon-only {
|
|
229
245
|
width: var(--rt-btn-height);
|
|
230
246
|
padding: 0;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# `rt-calendar`
|
|
2
|
+
|
|
3
|
+
Календарь-витрина: месяцы, дни с подписями (цена, занятость), стрелки переключения.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-calendar
|
|
7
|
+
prevAriaLabel="Предыдущий месяц"
|
|
8
|
+
[months]="months()"
|
|
9
|
+
[weekdayLabels]="weekdays"
|
|
10
|
+
[canPrev]="canPrev()"
|
|
11
|
+
[canNext]="canNext()"
|
|
12
|
+
(prevMonth)="shift(-1)"
|
|
13
|
+
(dayClick)="pick($event)" />
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
| вход | тип | умолчание |
|
|
17
|
+
| --------------------------------- | ------------------- | -------------- |
|
|
18
|
+
| `months` | `readonly Month[]` | **обязателен** |
|
|
19
|
+
| `weekdayLabels` | `readonly string[]` | **обязателен** |
|
|
20
|
+
| `canPrev` / `canNext` | `boolean` | `false` |
|
|
21
|
+
| `prevAriaLabel` / `nextAriaLabel` | `string` | `''` |
|
|
22
|
+
| `sublabelsLoading` | `boolean` | `false` |
|
|
23
|
+
|
|
24
|
+
Выходы: `prevMonth`, `nextMonth`, `dayClick`. Слот `[calendarHint]` — подсказка в шапке.
|
|
25
|
+
|
|
26
|
+
## Главное, что нужно знать
|
|
27
|
+
|
|
28
|
+
**Календарь ничего не считает.** Раскладку месяца готовит потребитель: сколько пустых клеток в
|
|
29
|
+
начале (`leadingBlanks`), какие дни заняты (`state`), что писать под числом (`sublabel`), какие
|
|
30
|
+
дни недоступны (`disabled`). Компонент только показывает готовое — поэтому он одинаково годится
|
|
31
|
+
и для выбора дат, и для витрины занятости.
|
|
32
|
+
|
|
33
|
+
**Это не поле ввода.** Значение он не хранит и в форму не пишет; для ввода даты есть
|
|
34
|
+
[`rt-date-picker`](../date-picker/CONTEXT.md).
|
|
35
|
+
|
|
36
|
+
## Края
|
|
37
|
+
|
|
38
|
+
- Недоступный день не кликается — `dayClick` для него не поднимается.
|
|
39
|
+
- `sublabelsLoading` подменяет подписи под числами заглушками, оставляя сами числа.
|
|
40
|
+
- Стрелки отключены, пока не разрешены явно: календарь не знает границ диапазона.
|
|
41
|
+
- Подписи стрелок задаёт приложение — своих слов у календаря здесь нет.
|
|
@@ -1,116 +1,115 @@
|
|
|
1
|
-
:
|
|
1
|
+
/* rt-calendar — ViewEncapsulation.None: правила таргетируют BEM-класс блока.
|
|
2
|
+
`.rt-calendar` — сам хост (шаблон начинается с ng-container, класс ставит host). */
|
|
3
|
+
|
|
4
|
+
.rt-calendar {
|
|
2
5
|
display: block;
|
|
3
6
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
7
|
+
&__header {
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
justify-content: space-between;
|
|
11
|
+
margin-bottom: var(--rt-space-3);
|
|
12
|
+
gap: var(--rt-space-3);
|
|
13
|
+
}
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
&__nav {
|
|
16
|
+
flex: 0 0 auto;
|
|
17
|
+
}
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
&__months {
|
|
20
|
+
display: grid;
|
|
21
|
+
gap: var(--rt-space-6);
|
|
22
|
+
grid-template-columns: repeat(2, 1fr);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
&__month-title {
|
|
26
|
+
margin: 0 0 var(--rt-space-2);
|
|
27
|
+
color: var(--rt-color-text-primary);
|
|
28
|
+
font-weight: var(--rt-font-weight-semibold);
|
|
29
|
+
text-align: center;
|
|
30
|
+
|
|
31
|
+
&:first-letter {
|
|
32
|
+
text-transform: uppercase;
|
|
21
33
|
}
|
|
34
|
+
}
|
|
22
35
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
&__grid {
|
|
37
|
+
display: grid;
|
|
38
|
+
gap: var(--rt-space-0-5);
|
|
39
|
+
grid-template-columns: repeat(7, 1fr);
|
|
40
|
+
}
|
|
28
41
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
42
|
+
&__weekday {
|
|
43
|
+
padding: var(--rt-space-1) 0;
|
|
44
|
+
color: var(--rt-color-text-muted);
|
|
45
|
+
font-size: var(--rt-text-xs);
|
|
46
|
+
text-align: center;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&__day {
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-direction: column;
|
|
52
|
+
align-items: center;
|
|
53
|
+
padding: var(--rt-space-1-5) var(--rt-space-0-5);
|
|
54
|
+
border: none;
|
|
55
|
+
border-radius: var(--rt-radius-md);
|
|
56
|
+
background: none;
|
|
57
|
+
color: var(--rt-color-text-primary);
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
font: inherit;
|
|
60
|
+
gap: var(--rt-space-0-5);
|
|
61
|
+
|
|
62
|
+
&:hover:not(:disabled, [data-state='start'], [data-state='end'], [data-state='in-range']) {
|
|
63
|
+
background: var(--rt-color-bg-subtle);
|
|
32
64
|
}
|
|
33
65
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
gap: var(--rt-space-0-5);
|
|
37
|
-
grid-template-columns: repeat(7, 1fr);
|
|
66
|
+
&:disabled {
|
|
67
|
+
cursor: not-allowed;
|
|
38
68
|
}
|
|
39
69
|
|
|
40
|
-
&
|
|
41
|
-
|
|
70
|
+
&[data-state='past'],
|
|
71
|
+
&[data-state='blocked'] {
|
|
42
72
|
color: var(--rt-color-text-muted);
|
|
43
|
-
|
|
44
|
-
text-align: center;
|
|
73
|
+
opacity: 0.45;
|
|
45
74
|
}
|
|
46
75
|
|
|
47
|
-
&
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
padding: var(--rt-space-1-5) var(--rt-space-0-5);
|
|
52
|
-
border: none;
|
|
53
|
-
border-radius: var(--rt-radius-md);
|
|
54
|
-
background: none;
|
|
55
|
-
color: var(--rt-color-text-primary);
|
|
56
|
-
cursor: pointer;
|
|
57
|
-
font: inherit;
|
|
58
|
-
gap: var(--rt-space-0-5);
|
|
59
|
-
|
|
60
|
-
&:hover:not(:disabled, [data-state='start'], [data-state='end'], [data-state='in-range']) {
|
|
61
|
-
background: var(--rt-color-bg-subtle);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
&:disabled {
|
|
65
|
-
cursor: not-allowed;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
&[data-state='past'],
|
|
69
|
-
&[data-state='blocked'] {
|
|
70
|
-
color: var(--rt-color-text-muted);
|
|
71
|
-
opacity: 0.45;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
&[data-state='busy'] {
|
|
75
|
-
color: var(--rt-color-text-muted);
|
|
76
|
-
opacity: 0.45;
|
|
77
|
-
text-decoration: line-through;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
&[data-state='start'],
|
|
81
|
-
&[data-state='end'] {
|
|
82
|
-
background: var(--rt-color-action-primary);
|
|
83
|
-
color: var(--rt-color-action-on-primary);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
&[data-state='in-range'] {
|
|
87
|
-
background: var(--rt-color-action-primary-soft);
|
|
88
|
-
color: var(--rt-color-text-link);
|
|
89
|
-
font-weight: var(--rt-font-weight-semibold);
|
|
90
|
-
}
|
|
76
|
+
&[data-state='busy'] {
|
|
77
|
+
color: var(--rt-color-text-muted);
|
|
78
|
+
opacity: 0.45;
|
|
79
|
+
text-decoration: line-through;
|
|
91
80
|
}
|
|
92
81
|
|
|
93
|
-
&
|
|
94
|
-
|
|
95
|
-
|
|
82
|
+
&[data-state='start'],
|
|
83
|
+
&[data-state='end'] {
|
|
84
|
+
background: var(--rt-color-action-primary);
|
|
85
|
+
color: var(--rt-color-action-on-primary);
|
|
96
86
|
}
|
|
97
87
|
|
|
98
|
-
&
|
|
99
|
-
|
|
100
|
-
|
|
88
|
+
&[data-state='in-range'] {
|
|
89
|
+
background: var(--rt-color-action-primary-soft);
|
|
90
|
+
color: var(--rt-color-text-link);
|
|
91
|
+
font-weight: var(--rt-font-weight-semibold);
|
|
101
92
|
}
|
|
102
93
|
}
|
|
103
94
|
|
|
95
|
+
&__day-num {
|
|
96
|
+
font-size: var(--rt-text-sm);
|
|
97
|
+
font-weight: var(--rt-font-weight-medium);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
&__day-sublabel {
|
|
101
|
+
font-size: var(--rt-text-2xs);
|
|
102
|
+
opacity: 0.75;
|
|
103
|
+
}
|
|
104
|
+
|
|
104
105
|
@media (width <= 640px) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
display: none;
|
|
113
|
-
}
|
|
106
|
+
&__months {
|
|
107
|
+
grid-template-columns: 1fr;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* на мобиле показываем один месяц — навигация листает по одному */
|
|
111
|
+
&__month:last-child {
|
|
112
|
+
display: none;
|
|
114
113
|
}
|
|
115
114
|
}
|
|
116
115
|
|
|
@@ -119,14 +118,12 @@
|
|
|
119
118
|
ширину боковыми отступами клетки и ступенью кегля, а не прячем цену:
|
|
120
119
|
без неё календарь перестаёт быть прайс-листом */
|
|
121
120
|
@media (width <= 374px) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
font-size: var(--rt-text-3xs);
|
|
129
|
-
}
|
|
121
|
+
&__day {
|
|
122
|
+
padding-inline: 0;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
&__day-sublabel {
|
|
126
|
+
font-size: var(--rt-text-3xs);
|
|
130
127
|
}
|
|
131
128
|
}
|
|
132
129
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# `rt-card`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-card header="Тариф" ariaLabel="Открыть тариф" [clickable]="true" (cardClick)="open()">
|
|
5
|
+
<rt-tag rtCardHeader value="Активен" severity="success" />
|
|
6
|
+
<p>Базовый, до 5 пользователей</p>
|
|
7
|
+
<button rtButton rtCardFooter label="Продлить"></button>
|
|
8
|
+
</rt-card>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
| вход | тип | умолчание |
|
|
12
|
+
| ----------- | ---------------- | --------- |
|
|
13
|
+
| `header` | `string \| null` | `null` |
|
|
14
|
+
| `ariaLabel` | `string \| null` | `null` |
|
|
15
|
+
| `clickable` | `boolean` | `false` |
|
|
16
|
+
|
|
17
|
+
| выход | тип |
|
|
18
|
+
| ----------- | ------------ |
|
|
19
|
+
| `cardClick` | `MouseEvent` |
|
|
20
|
+
|
|
21
|
+
Три слота: `[rtCardHeader]` — рядом с заголовком, слот по умолчанию — тело, `[rtCardFooter]` — низ.
|
|
22
|
+
|
|
23
|
+
## Главное, что нужно знать
|
|
24
|
+
|
|
25
|
+
**Без `clickable` карточка не интерактивна вовсе**: ни `role`, ни `tabindex`, ни события. Это
|
|
26
|
+
намеренно — иначе текст внутри нельзя было бы выделить, не «нажав» карточку.
|
|
27
|
+
|
|
28
|
+
С `clickable` карточка объявляется `role="button"`, встаёт в таб-порядок и реагирует на Enter и
|
|
29
|
+
Space. Подпись для скринридера задаётся входом: содержимое карточки именем контрола не служит.
|
|
30
|
+
|
|
31
|
+
## Края
|
|
32
|
+
|
|
33
|
+
- Карточка рисуется тегом `<article>` — она самостоятельный кусок содержимого.
|
|
34
|
+
- Шапка и подвал существуют всегда, даже пустыми; прячут их стили.
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
/* rt-card — контейнер карточки в common/ui
|
|
1
|
+
/* rt-card — контейнер карточки в common/ui. ViewEncapsulation.None.
|
|
2
|
+
Хост адресуется по имени элемента, а не по классу блока: класс `.rt-card` висит
|
|
3
|
+
и на `<rt-card>`, и на корне шаблона, поэтому правила блока вложены в хост —
|
|
4
|
+
так они достаются только корню шаблона. */
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
rt-card {
|
|
4
7
|
display: block;
|
|
5
8
|
|
|
6
9
|
.rt-card {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# `rt-chat`
|
|
2
|
+
|
|
3
|
+
Переписка: лента сообщений, шапка и поле ответа.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-chat
|
|
7
|
+
title="Переписка по заявке"
|
|
8
|
+
showRefresh
|
|
9
|
+
[messages]="messages()"
|
|
10
|
+
[hasThread]="true"
|
|
11
|
+
[canReply]="canReply()"
|
|
12
|
+
[loading]="loading()"
|
|
13
|
+
[attachments]="true"
|
|
14
|
+
(send)="onSend($event)"
|
|
15
|
+
(refresh)="reload()" />
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Основные входы: `messages`, `hasThread`, `canReply`, `replyBlockReason`, `loading`, `fetching`,
|
|
19
|
+
`sending`, `attachments`, `accept`, `formatting`, `title`, `showRefresh`, `showExpand`, `fill`.
|
|
20
|
+
Выходы: `send`, `refresh` и события по сообщениям (повтор, удаление).
|
|
21
|
+
|
|
22
|
+
## Главное, что нужно знать
|
|
23
|
+
|
|
24
|
+
**Без `hasThread` чат рисует только подсказку выбора.** Ни ленты, ни поля ответа не будет —
|
|
25
|
+
это состояние «переписка не выбрана», а не «переписка пустая».
|
|
26
|
+
|
|
27
|
+
**Шапка появляется только вместе с кнопками**: `showHeader = showRefresh || showExpand`.
|
|
28
|
+
Один `title` шапку не создаёт.
|
|
29
|
+
|
|
30
|
+
**Поле ответа гейтится `canReply`.** Когда отвечать нельзя, вместо него рисуется причина
|
|
31
|
+
из `replyBlockReason`.
|
|
32
|
+
|
|
33
|
+
**Удалённое сообщение остаётся в ленте** отдельной пометкой вместо текста: иначе непонятно, на
|
|
34
|
+
что отвечали соседние реплики.
|
|
35
|
+
|
|
36
|
+
## Края
|
|
37
|
+
|
|
38
|
+
- Режим `formatting` подменяет простое поле [`rt-rich-editor`](../rich-editor/CONTEXT.md);
|
|
39
|
+
тот грузит Quill динамическим импортом.
|
|
40
|
+
- Неотправленное сообщение (`status: 'failed'`) получает кнопку повтора.
|
|
41
|
+
- Файлы можно бросать прямо в ленту — чат обёрнут в [`rt-file-drop`](../file-drop/CONTEXT.md).
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# `rt-checkbox`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-checkbox [formControl]="agreeControl">Согласен с условиями</rt-checkbox>
|
|
5
|
+
<rt-checkbox ariaLabel="Выбрать всё" [indeterminate]="someSelected()" [formControl]="allControl" />
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
| вход | тип | умолчание |
|
|
9
|
+
| --------------- | ---------------- | --------- |
|
|
10
|
+
| `inputId` | `string \| null` | `null` |
|
|
11
|
+
| `ariaLabel` | `string \| null` | `null` |
|
|
12
|
+
| `disabled` | `boolean` | `false` |
|
|
13
|
+
| `indeterminate` | `boolean` | `false` |
|
|
14
|
+
|
|
15
|
+
Значение — `boolean` через `ControlValueAccessor`. Подпись приходит проекцией.
|
|
16
|
+
|
|
17
|
+
## Главное, что нужно знать
|
|
18
|
+
|
|
19
|
+
**Это `<button role="checkbox">`, а не `<input type="checkbox">`.** Enter и Space обрабатывает
|
|
20
|
+
браузер сам, поэтому собственных обработчиков клавиатуры здесь нет. `aria-checked` принимает три
|
|
21
|
+
значения: `true`, `false`, `mixed`.
|
|
22
|
+
|
|
23
|
+
**`indeterminate` — чисто визуальный вход, компонент его не сбрасывает.** Клик по смешанному
|
|
24
|
+
состоянию разрешается в `true` (контракт трёхпозиционного чекбокса) и поднимает `true` наружу,
|
|
25
|
+
но прочерк на экране останется, пока вход держит его сверху. Снимать вход — дело потребителя.
|
|
26
|
+
|
|
27
|
+
## Как этим пользоваться
|
|
28
|
+
|
|
29
|
+
- Отключение приходит двумя путями — вход `[disabled]` и `FormControl.disable()`, — и оба пишут
|
|
30
|
+
в один внутренний сигнал, поэтому ведут себя одинаково.
|
|
31
|
+
- Проекция не видна скринридеру как имя контрола: если подпись важна для AT, задай `ariaLabel`.
|
|
32
|
+
- `inputId` кладётся на кнопку — по нему связывается внешний `<label for>`.
|
|
33
|
+
|
|
34
|
+
## Рядом
|
|
35
|
+
|
|
36
|
+
- [`rt-toggle-switch`](../toggle-switch/CONTEXT.md) — то же булево значение, но для «включено /
|
|
37
|
+
выключено», а не «отмечено».
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
/* rt-checkbox — чекбокс в common/ui. ViewEncapsulation.None.
|
|
2
|
+
Хост адресуется по имени элемента, а не по классу блока: класс `.rt-checkbox` висит
|
|
3
|
+
и на `<rt-checkbox>`, и на корне шаблона, поэтому правила блока вложены в хост —
|
|
4
|
+
так они достаются только корню шаблона. */
|
|
5
|
+
|
|
6
|
+
rt-checkbox {
|
|
2
7
|
display: inline-block;
|
|
3
8
|
}
|
|
4
9
|
|
|
5
|
-
.rt-checkbox {
|
|
10
|
+
rt-checkbox .rt-checkbox {
|
|
6
11
|
display: inline-flex;
|
|
7
12
|
align-items: center;
|
|
8
13
|
padding: 0;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# `rt-collapsible-text`
|
|
2
|
+
|
|
3
|
+
Длинный текст, свёрнутый до нескольких строк, с кнопкой «ещё».
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-collapsible-text [paragraphs]="description()" [clampLines]="4" />
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
| вход | тип | умолчание |
|
|
10
|
+
| ------------ | ------------------- | -------------- |
|
|
11
|
+
| `paragraphs` | `readonly string[]` | **обязателен** |
|
|
12
|
+
| `clampLines` | `number \| string` | `6` |
|
|
13
|
+
|
|
14
|
+
Выходов нет.
|
|
15
|
+
|
|
16
|
+
## Главное, что нужно знать
|
|
17
|
+
|
|
18
|
+
**Кнопка «ещё» появляется по замеру, а не по длине текста.** После отрисовки компонент сравнивает
|
|
19
|
+
`scrollHeight` с `clientHeight` и следит за изменением ширины через `ResizeObserver`: тот же текст
|
|
20
|
+
в узкой колонке переполняет обрезку, а в широкой — нет.
|
|
21
|
+
|
|
22
|
+
Отсюда следствие для тестов: в среде без раскладки (jsdom) высоты нулевые, переполнения нет, и
|
|
23
|
+
кнопки не будет — это верное поведение, а не сбой.
|
|
24
|
+
|
|
25
|
+
## Края
|
|
26
|
+
|
|
27
|
+
- Текст приходит **массивом абзацев**, а не одной строкой: разбивать HTML компонент не умеет.
|
|
28
|
+
- Разворот снимает и класс обрезки, и ограничение по строкам; свёрнутое состояние возвращается
|
|
29
|
+
повторным нажатием.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# `[rtConfirm]` + `rt-confirm-popover`
|
|
2
|
+
|
|
3
|
+
Подтверждение прямо под кнопкой — вместо модального диалога.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-icon-button
|
|
7
|
+
icon="ico-trash"
|
|
8
|
+
variant="danger"
|
|
9
|
+
ariaLabel="Отклонить"
|
|
10
|
+
rtConfirmTitle="Отклонить заявку"
|
|
11
|
+
rtConfirmLabel="Отклонить"
|
|
12
|
+
[rtConfirm]="'Отклонить заявку? Оператор получит уведомление.'"
|
|
13
|
+
(confirmed)="reject()" />
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
| вход (алиас) | тип | умолчание |
|
|
17
|
+
| ---------------------- | ------------------------------------ | ------------------------ |
|
|
18
|
+
| `rtConfirm` | `string` | `''` |
|
|
19
|
+
| `rtConfirmTitle` | `string \| null` | `null` |
|
|
20
|
+
| `rtConfirmLabel` | `string` | `''` → `rtKit.uiConfirm` |
|
|
21
|
+
| `rtConfirmCancelLabel` | `string` | `''` → `rtKit.uiCancel` |
|
|
22
|
+
| `rtConfirmTone` | `'danger' \| 'warning' \| 'primary'` | `'danger'` |
|
|
23
|
+
| `rtConfirmDisabled` | `boolean` | `false` |
|
|
24
|
+
|
|
25
|
+
| выход | тип |
|
|
26
|
+
| ----------- | ------ |
|
|
27
|
+
| `confirmed` | `void` |
|
|
28
|
+
|
|
29
|
+
## Главное, что нужно знать
|
|
30
|
+
|
|
31
|
+
**Событие приходит только на подтверждение.** Отмена, Escape и клик мимо просто закрывают панель
|
|
32
|
+
и молчат — отдельного «отменено» нет.
|
|
33
|
+
|
|
34
|
+
**Пустой вопрос не открывает ничего.** Строка из одних пробелов равна отсутствию: подтверждать
|
|
35
|
+
нечего. Значит `[rtConfirm]="maybeEmpty()"` — законный способ выключить подтверждение.
|
|
36
|
+
|
|
37
|
+
## Как этим пользоваться
|
|
38
|
+
|
|
39
|
+
- Тон по умолчанию красный: подтверждают обычно удаление.
|
|
40
|
+
- Панель прижата к правому краю host-а и переворачивается вверх у нижней границы — так она
|
|
41
|
+
раскрывается внутрь асайда, а не за его край.
|
|
42
|
+
- Компонент панели (`rt-confirm-popover`) создаётся директивой; в разметку его не ставят.
|
|
43
|
+
|
|
44
|
+
## Рядом
|
|
45
|
+
|
|
46
|
+
- [`rt-menu-item`](../menu/CONTEXT.md) — у пункта меню своё подтверждение, но **диалогом**, а не
|
|
47
|
+
поповером: пункт исчезает вместе с меню, и панели было бы не на чем держаться.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# `rt-container`
|
|
2
|
+
|
|
3
|
+
Каркас страницы: шапка, боковое меню, полосы панели инструментов, содержимое и правая
|
|
4
|
+
выезжающая панель. Зоны объявляются директивами на `ng-template`.
|
|
5
|
+
|
|
6
|
+
```html
|
|
7
|
+
<rt-container height="viewport" mobileLeftNav="bottom">
|
|
8
|
+
<ng-template rtContainerHeader><rt-header /></ng-template>
|
|
9
|
+
<ng-template rtContainerLeftSidenav><rt-section-nav … /></ng-template>
|
|
10
|
+
<ng-template rtContainerToolbarLeft><rt-filter-control … /></ng-template>
|
|
11
|
+
<ng-template rtContainerContent><router-outlet /></ng-template>
|
|
12
|
+
<ng-template rtContainerRightSidenav><app-details /></ng-template>
|
|
13
|
+
</rt-container>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
| вход | тип | умолчание |
|
|
17
|
+
| --------------- | ---------------------- | --------- |
|
|
18
|
+
| `mobileLeftNav` | `'keep' \| 'bottom'` | `'keep'` |
|
|
19
|
+
| `height` | `'auto' \| 'viewport'` | `'auto'` |
|
|
20
|
+
|
|
21
|
+
Выходы: `backdropClick`, `rightOpened`, `rightClosed`. Публичное API: `openRight()`,
|
|
22
|
+
`closeRight()`, `rightOpen()`, `rightOverlayReady()`.
|
|
23
|
+
|
|
24
|
+
## Главное, что нужно знать
|
|
25
|
+
|
|
26
|
+
**Необъявленная зона не создаёт пустого узла** — каркас состоит ровно из того, что передали.
|
|
27
|
+
Зоны рисуются смысловыми тегами: `<header>`, `<aside>`, `<main>`.
|
|
28
|
+
|
|
29
|
+
**Правая панель существует, только когда её зона объявлена.** Оверлей под неё создаётся заранее
|
|
30
|
+
(`rightOverlayReady()`), а `openRight()` без объявленной зоны — no-op.
|
|
31
|
+
|
|
32
|
+
**`rightOpened` / `rightClosed` привязаны к `transitionend` панели.** Там, где анимаций нет
|
|
33
|
+
(тест, `prefers-reduced-motion`), события не придут, а `rightOpen()` уже верен — смотреть надо
|
|
34
|
+
на него.
|
|
35
|
+
|
|
36
|
+
**Стопка уведомлений встроена в каркас** (`rt-toaster` внизу по центру): заводить её отдельно
|
|
37
|
+
не нужно.
|
|
38
|
+
|
|
39
|
+
## Рядом
|
|
40
|
+
|
|
41
|
+
- [`rt-toaster`](../toast/CONTEXT.md) — та самая стопка.
|
|
42
|
+
- [`rt-workspace`](../workspace/CONTEXT.md) — трёхколоночная раскладка внутри содержимого.
|