@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,3 +1,8 @@
|
|
|
1
|
+
/* Панель и backdrop правой sidenav — тот же overlay, что у rt-aside, и правила у
|
|
2
|
+
них общие. Контейнер подключает их сам: rt-aside tree-shake'тся, когда его не
|
|
3
|
+
взял ни один маршрут, а панель контейнер держит в persistent-режиме. */
|
|
4
|
+
@use '../aside/rt-aside-overlay';
|
|
5
|
+
|
|
1
6
|
.rt-container {
|
|
2
7
|
display: grid;
|
|
3
8
|
min-height: 100%;
|
|
@@ -85,70 +90,7 @@
|
|
|
85
90
|
}
|
|
86
91
|
}
|
|
87
92
|
|
|
88
|
-
/* Backdrop + overlay панели CDK Overlay (persistent attach режим).
|
|
89
|
-
Cc-container владеет CDK Overlay для правой sidenav — стили глобальные
|
|
90
|
-
(ViewEncapsulation.None), живут тут чтобы не зависеть от подгрузки rt-aside
|
|
91
|
-
компонента (он tree-shake'тся когда не used active route'ом).
|
|
92
|
-
|
|
93
|
-
Двойной селектор `.cdk-overlay-backdrop.rt-aside-backdrop` нужен для specificity:
|
|
94
|
-
CDK Overlay добавляет `.cdk-overlay-backdrop-showing { opacity: 1 }` сразу при
|
|
95
|
-
attach; при persistent overlay backdrop должен оставаться скрытым пока aside
|
|
96
|
-
не открыт. Два класса > одиночный — наш opacity:0 побеждает. */
|
|
97
|
-
.cdk-overlay-backdrop.rt-aside-backdrop {
|
|
98
|
-
/* stylelint-disable-next-line property-no-vendor-prefix -- Safari < 18 */
|
|
99
|
-
-webkit-backdrop-filter: blur(5px);
|
|
100
|
-
backdrop-filter: blur(5px);
|
|
101
|
-
background: rgb(0 0 0 / 45%);
|
|
102
|
-
opacity: 0;
|
|
103
|
-
pointer-events: none;
|
|
104
|
-
transition: opacity var(--rt-duration-base) ease;
|
|
105
|
-
|
|
106
|
-
&.rt-aside-backdrop--visible {
|
|
107
|
-
opacity: 1;
|
|
108
|
-
pointer-events: auto;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
.rt-aside-overlay {
|
|
113
|
-
height: 100dvh;
|
|
114
|
-
transition: transform var(--rt-duration-base) ease;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/* Закрытая панель нажатий не ловит. На узком экране она шириной во весь вьюпорт,
|
|
118
|
-
и пока её сдвиг за край экрана не досчитан, она перекрывает страницу целиком:
|
|
119
|
-
выглядит это нормально, а нажатия уходят в пустоту. Селектор из двух классов —
|
|
120
|
-
чтобы правило не зависело от порядка, в котором браузер получил стили панели и
|
|
121
|
-
стили контейнера. */
|
|
122
|
-
.rt-aside-overlay:not(.rt-aside-overlay--open) {
|
|
123
|
-
pointer-events: none;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/* Стартовое положение за краем экрана не анимируется: переход из него начинается,
|
|
127
|
-
как только вьюпорт задаёт панели ширину, и всю анимацию держит панель на месте. */
|
|
128
|
-
.rt-aside-overlay.rt-aside-overlay--entering {
|
|
129
|
-
transition: none;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.rt-aside-overlay--position-right.rt-aside-overlay--entering,
|
|
133
|
-
.rt-aside-overlay--position-right.rt-aside-overlay--leaving {
|
|
134
|
-
transform: translateX(100%);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.rt-aside-overlay--position-left.rt-aside-overlay--entering,
|
|
138
|
-
.rt-aside-overlay--position-left.rt-aside-overlay--leaving {
|
|
139
|
-
transform: translateX(-100%);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
.rt-aside-overlay--position-right.rt-aside-overlay--open,
|
|
143
|
-
.rt-aside-overlay--position-left.rt-aside-overlay--open {
|
|
144
|
-
transform: translateX(0);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
93
|
@media (width <= 768px) {
|
|
148
|
-
.rt-aside-overlay {
|
|
149
|
-
width: 100dvw;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
94
|
// Opt-in: левый sidenav сворачивается в нижний ряд grid'а (таб-бар).
|
|
153
95
|
// Контент занимает всю ширину и скроллится в своём `1fr` над баром —
|
|
154
96
|
// бар остаётся видимым без position:fixed и без привязки к его высоте.
|
|
@@ -167,9 +109,3 @@
|
|
|
167
109
|
}
|
|
168
110
|
}
|
|
169
111
|
}
|
|
170
|
-
|
|
171
|
-
@media (prefers-reduced-motion: reduce) {
|
|
172
|
-
.rt-aside-overlay {
|
|
173
|
-
transition: none;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# `rt-counter`
|
|
2
|
+
|
|
3
|
+
Счётчик «− N +» для целых величин: гости, места, единицы.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-counter ariaLabel="Гостей" [formControl]="guests" [min]="1" [max]="4" [step]="1" />
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
| вход | тип | умолчание |
|
|
10
|
+
| --------------------------------- | ------------------ | ----------------------------- |
|
|
11
|
+
| `ariaLabel` | `string \| null` | `null` |
|
|
12
|
+
| `min` | `number \| string` | `0` |
|
|
13
|
+
| `max` | `number \| string` | `Number.MAX_SAFE_INTEGER` |
|
|
14
|
+
| `step` | `number \| string` | `1` |
|
|
15
|
+
| `decreaseLabel` / `increaseLabel` | `string` | `''` → переведённые умолчания |
|
|
16
|
+
| `disabled` | `boolean` | `false` |
|
|
17
|
+
|
|
18
|
+
Значение — `number` через `ControlValueAccessor`.
|
|
19
|
+
|
|
20
|
+
## Главное, что нужно знать
|
|
21
|
+
|
|
22
|
+
**Значение с клавиатуры не вводится — только шагами.** Поэтому здесь нет ни маски, ни дробной
|
|
23
|
+
части, ни кнопки очистки. Если число нужно набирать, это
|
|
24
|
+
[`rt-input-number`](../input-number/CONTEXT.md), а не счётчик.
|
|
25
|
+
|
|
26
|
+
**Границы держатся и при записи из формы.** `control.setValue(99)` при `max=4` даст 4, а `null` —
|
|
27
|
+
нижнюю границу. Иначе кнопки блокировались бы по состоянию, которого на экране нет.
|
|
28
|
+
|
|
29
|
+
## Как этим пользоваться
|
|
30
|
+
|
|
31
|
+
- Шаг, упирающийся в границу, не перепрыгивает её, а останавливается на ней; если значение при
|
|
32
|
+
этом не изменилось, наружу не уходит ни `change`, ни `touched`.
|
|
33
|
+
- Подписи кнопок для скринридера берутся из словаря кита и перекрываются поштучно — форма знает,
|
|
34
|
+
что именно считает («Убрать гостя» вместо «Уменьшить»).
|
|
35
|
+
- Host объявлен `role="group"` с `aria-label`, значение помечено `aria-live="polite"`: скринридер
|
|
36
|
+
называет назначение счётчика и проговаривает новое число.
|
|
37
|
+
|
|
38
|
+
## Рядом
|
|
39
|
+
|
|
40
|
+
- [`rt-input-number`](../input-number/CONTEXT.md) — когда число набирают.
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
:
|
|
1
|
+
/* rt-counter — ViewEncapsulation.None: правила таргетируют BEM-класс блока.
|
|
2
|
+
`.rt-counter` — сам хост (шаблон начинается с ng-container, класс ставит host). */
|
|
3
|
+
|
|
4
|
+
.rt-counter {
|
|
2
5
|
display: inline-flex;
|
|
3
6
|
align-items: center;
|
|
4
7
|
gap: var(--rt-counter-gap);
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
text-align: center;
|
|
13
|
-
}
|
|
9
|
+
&__value {
|
|
10
|
+
min-width: var(--rt-counter-value-min-width);
|
|
11
|
+
color: var(--rt-counter-value-color);
|
|
12
|
+
font-size: var(--rt-counter-value-font-size);
|
|
13
|
+
font-variant-numeric: tabular-nums;
|
|
14
|
+
text-align: center;
|
|
14
15
|
}
|
|
15
16
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# `rt-counter-row`
|
|
2
|
+
|
|
3
|
+
Строка «подпись + контрол» для настроек и форм бронирования.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-counter-row label="Гостей" hint="Не больше четырёх">
|
|
7
|
+
<rt-counter [formControl]="guests" [max]="4" />
|
|
8
|
+
</rt-counter-row>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
| вход | тип | умолчание |
|
|
12
|
+
| ------- | -------- | -------------- |
|
|
13
|
+
| `label` | `string` | **обязателен** |
|
|
14
|
+
| `hint` | `string` | `''` |
|
|
15
|
+
|
|
16
|
+
Контрол приходит проекцией. Выходов нет.
|
|
17
|
+
|
|
18
|
+
## Главное, что нужно знать
|
|
19
|
+
|
|
20
|
+
**Строка не знает, что именно считают.** Внутрь кладут [`rt-counter`](../counter/CONTEXT.md),
|
|
21
|
+
[`rt-toggle-switch`](../toggle-switch/CONTEXT.md), [`rt-select`](../select/CONTEXT.md) — что
|
|
22
|
+
угодно. Отсюда и имя входа: строка отвечает только за подпись и раскладку.
|
|
23
|
+
|
|
24
|
+
Пустое пояснение равно отсутствию входа — `[hint]="maybeEmpty()"` безопасно.
|
|
@@ -1,33 +1,34 @@
|
|
|
1
|
-
:
|
|
1
|
+
/* rt-counter-row — ViewEncapsulation.None: правила таргетируют BEM-класс блока.
|
|
2
|
+
`.rt-counter-row` — сам хост (шаблон начинается с ng-container, класс ставит host). */
|
|
3
|
+
|
|
4
|
+
.rt-counter-row {
|
|
2
5
|
display: flex;
|
|
3
6
|
align-items: center;
|
|
4
7
|
justify-content: space-between;
|
|
5
8
|
padding: var(--rt-counter-row-padding-y) 0;
|
|
6
9
|
gap: var(--rt-counter-row-gap);
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
text-align: start;
|
|
14
|
-
}
|
|
11
|
+
// Линия разделяет соседние строки, а не обводит каждую: рамка на всех дала бы
|
|
12
|
+
// лишние границы сверху у первой и снизу у последней
|
|
13
|
+
&:not(:first-of-type) {
|
|
14
|
+
border-top: var(--rt-border-width-thin) solid var(--rt-counter-row-color-border);
|
|
15
|
+
}
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
&__text {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
gap: var(--rt-space-2xs);
|
|
21
|
+
text-align: start;
|
|
22
|
+
}
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
&__label {
|
|
25
|
+
color: var(--rt-counter-row-label-color);
|
|
26
|
+
font-size: var(--rt-counter-row-label-font-size);
|
|
27
|
+
font-weight: var(--rt-font-weight-medium);
|
|
26
28
|
}
|
|
27
|
-
}
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
:
|
|
32
|
-
|
|
30
|
+
&__hint {
|
|
31
|
+
color: var(--rt-counter-row-hint-color);
|
|
32
|
+
font-size: var(--rt-counter-row-hint-font-size);
|
|
33
|
+
}
|
|
33
34
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# `rt-date-picker`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-field label="Дата заезда">
|
|
5
|
+
<rt-date-picker type="date" min="2026-01-01" max="2026-12-31" [formControl]="checkIn" />
|
|
6
|
+
</rt-field>
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Свои входы поверх общих из [`RtFormControlBase`](../form-control/CONTEXT.md):
|
|
10
|
+
|
|
11
|
+
| вход | тип | умолчание |
|
|
12
|
+
| ------------- | -------------------------------------- | --------- |
|
|
13
|
+
| `type` | `'date' \| 'time' \| 'datetime-local'` | `'date'` |
|
|
14
|
+
| `min` / `max` | `string \| null` (ISO) | `null` |
|
|
15
|
+
|
|
16
|
+
Значение — **строка ISO**, ровно та, что отдаёт нативное поле (`2026-03-15`, `09:30`,
|
|
17
|
+
`2026-03-15T09:30`).
|
|
18
|
+
|
|
19
|
+
## Главное, что нужно знать
|
|
20
|
+
|
|
21
|
+
**Своего календаря кит не везёт — рисует его браузер.** Отсюда всё остальное: раскладка,
|
|
22
|
+
формат ввода и клавиатура всегда совпадают с системными, а вид поля на разных платформах
|
|
23
|
+
отличается. Если нужен единый календарь во всех браузерах, этот компонент не подойдёт.
|
|
24
|
+
|
|
25
|
+
**Значение — строка, а не `Date`.** Разбор и часовые пояса остаются на стороне потребителя;
|
|
26
|
+
компонент не трогает ни то, ни другое.
|
|
27
|
+
|
|
28
|
+
## Края
|
|
29
|
+
|
|
30
|
+
- В плоском режиме чтения (включает [`rt-field`](../field/CONTEXT.md)) значение переписывается
|
|
31
|
+
на язык интерфейса через `Intl.DateTimeFormat`: `2026-03-15` → `15 мар. 2026 г.` под `ru`.
|
|
32
|
+
- Нераспознанная строка в этом режиме показывается **как есть**, а не исчезает: значение может
|
|
33
|
+
прийти из формы, минуя нативное поле.
|
|
34
|
+
- Пустое значение рисуется прочерком.
|
|
35
|
+
- `min`/`max` уезжают на нативное поле; проверку выхода за границы делает браузер, отдельного
|
|
36
|
+
валидатора здесь нет.
|
|
37
|
+
|
|
38
|
+
## Рядом
|
|
39
|
+
|
|
40
|
+
- [`rt-input`](../input/CONTEXT.md) с `type="time"` — когда нужно только время без обёртки.
|
|
41
|
+
- [`rt-calendar`](../calendar/CONTEXT.md) — календарь-витрина, а не поле ввода.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# `rt-delta-view`
|
|
2
|
+
|
|
3
|
+
Только показ содержимого редактора: превращает модель Quill в разметку.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-delta-view [delta]="message.delta" />
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
| вход | тип | умолчание |
|
|
10
|
+
| ------- | --------------------- | --------- |
|
|
11
|
+
| `delta` | `IQuillDelta \| null` | `null` |
|
|
12
|
+
|
|
13
|
+
Выходов нет. Своего шаблона у компонента нет — разметка строится в DOM напрямую.
|
|
14
|
+
|
|
15
|
+
## Главное, что нужно знать
|
|
16
|
+
|
|
17
|
+
**Разметка собирается вручную, а не через `innerHTML`.** Каждый узел создаётся отдельно, поэтому
|
|
18
|
+
в вывод не попадёт ничего, кроме поддержанного набора: жирный, курсив, подчёркнутый,
|
|
19
|
+
зачёркнутый, списки и блок кода. Это и есть защита — чужой HTML из модели не отрисуется.
|
|
20
|
+
|
|
21
|
+
**Соседние строки списка склеиваются в один `<ul>`/`<ol>`.** Каждая строка приходит своей
|
|
22
|
+
операцией; без склейки получился бы список из одного пункта на строку.
|
|
23
|
+
|
|
24
|
+
## Края
|
|
25
|
+
|
|
26
|
+
- `null` очищает разметку целиком — состояние «нечего показывать».
|
|
27
|
+
- Смена значения перерисовывает содержимое полностью, а не дописывает.
|
|
28
|
+
|
|
29
|
+
## Рядом
|
|
30
|
+
|
|
31
|
+
- [`rt-rich-editor`](../rich-editor/CONTEXT.md) — редактор, который эту модель производит.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# `rt-detail-list` + `rt-detail-row`
|
|
2
|
+
|
|
3
|
+
Вертикальный перечень «ключ → значение» для асайдов и карточек.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<rt-detail-list>
|
|
7
|
+
<rt-detail-row label="Тариф">Базовый</rt-detail-row>
|
|
8
|
+
<rt-detail-row label="Владелец" [loading]="!user()">{{ user()?.name }}</rt-detail-row>
|
|
9
|
+
</rt-detail-list>
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
`rt-detail-list` — чистая раскладка, входов нет.
|
|
13
|
+
|
|
14
|
+
| вход `rt-detail-row` | тип | умолчание |
|
|
15
|
+
| -------------------- | --------- | --------- |
|
|
16
|
+
| `label` | `string` | `''` |
|
|
17
|
+
| `loading` | `boolean` | `false` |
|
|
18
|
+
|
|
19
|
+
Значение приходит проекцией.
|
|
20
|
+
|
|
21
|
+
## Главное, что нужно знать
|
|
22
|
+
|
|
23
|
+
**Во время загрузки подменяется только значение** — подпись остаётся, по ней читается, чего ждут.
|
|
24
|
+
Заглушка узкая (180 px), под значение, а не под всю строку.
|
|
25
|
+
|
|
26
|
+
## Рядом
|
|
27
|
+
|
|
28
|
+
- [`rt-info-item`](../info-item/CONTEXT.md) — одиночная пара «подпись: значение» в строку.
|
|
29
|
+
- [`rt-money-list`](../money-list/CONTEXT.md) — то же, но для сумм с итогом.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# `rt-dialog` + `RtDialogService`
|
|
2
|
+
|
|
3
|
+
Модальное окно по центру. Открывается **сервисом**, а не разметкой.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
const ref: RtDialogRef<boolean> = this.#dialog.open(ConfirmComponent, { data: { id }, closeOnEscape: true });
|
|
7
|
+
ref.afterClosed().subscribe((ok?: boolean): void => …);
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```html
|
|
11
|
+
<rt-dialog width="360px" ariaLabel="Удаление">
|
|
12
|
+
<rt-dialog-header title="Удалить запись?" />
|
|
13
|
+
<p>Действие необратимо.</p>
|
|
14
|
+
<rt-dialog-footer>…</rt-dialog-footer>
|
|
15
|
+
</rt-dialog>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
| вход `rt-dialog` | тип | умолчание |
|
|
19
|
+
| ---------------- | ---------------------- | ------------------------------------- |
|
|
20
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` |
|
|
21
|
+
| `width` | `string \| null` | `null` (свойство `--rt-dialog-width`) |
|
|
22
|
+
| `ariaLabel` | `string \| null` | `null` |
|
|
23
|
+
|
|
24
|
+
| вход `rt-dialog-header` | тип | умолчание |
|
|
25
|
+
| ----------------------- | --------- | -------------- |
|
|
26
|
+
| `title` | `string` | **обязателен** |
|
|
27
|
+
| `closable` | `boolean` | `true` |
|
|
28
|
+
|
|
29
|
+
| настройка `open()` | умолчание |
|
|
30
|
+
| ---------------------------------------- | ------------------------------------------ |
|
|
31
|
+
| `data` | `null` — доезжает токеном `RT_DIALOG_DATA` |
|
|
32
|
+
| `closeOnBackdropClick` / `closeOnEscape` | `true` |
|
|
33
|
+
| `backdropClass` / `panelClass` | `rt-dialog-backdrop` / `rt-dialog-overlay` |
|
|
34
|
+
|
|
35
|
+
## Главное, что нужно знать
|
|
36
|
+
|
|
37
|
+
**Прокрутка страницы блокируется на время диалога** (`scrollStrategies.block()`), а окно
|
|
38
|
+
объявлено `role="dialog"` с `aria-modal="true"`.
|
|
39
|
+
|
|
40
|
+
`RtDialogRef.disableClose` держит окно и под кликом по подложке, и под Escape — ставится самим
|
|
41
|
+
содержимым, когда закрывать нельзя.
|
|
42
|
+
|
|
43
|
+
**Крестик в шапке ищет `RtDialogRef` необязательным инжектом.** Поэтому `rt-dialog-header` можно
|
|
44
|
+
поставить и вне диалога — нажатие просто ничего не сделает, а не упадёт.
|
|
45
|
+
|
|
46
|
+
## Рядом
|
|
47
|
+
|
|
48
|
+
- [`rt-aside`](../aside/CONTEXT.md) — немодальная панель сбоку.
|
|
49
|
+
- [`[rtConfirm]`](../confirm-popover/CONTEXT.md) — подтверждение без модалки.
|
|
50
|
+
- [`rt-welcome-dialog`](../welcome-dialog/CONTEXT.md) — готовое приветственное окно.
|
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
/* rt-dialog-footer — footer-слот модалки. ViewEncapsulation.None.
|
|
1
|
+
/* rt-dialog-footer — footer-слот модалки. ViewEncapsulation.None.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
Хост адресуется по имени элемента, а не по классу блока: класс
|
|
4
|
+
`.rt-dialog-footer` висит и на `<rt-dialog-footer>`, и на `<footer>` внутри
|
|
5
|
+
него. Правило на голом классе доставалось обоим — кнопки прижимались к правому
|
|
6
|
+
краю не окна, а строки по содержимому, и разделитель рисовался дважды. Заодно
|
|
7
|
+
`:empty` теперь спрашивается у `<footer>`, а не у хоста: у хоста внутри всегда
|
|
8
|
+
лежит `<footer>`, и пустым он не бывал никогда. */
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
rt-dialog-footer {
|
|
11
|
+
display: contents;
|
|
12
|
+
|
|
13
|
+
.rt-dialog-footer {
|
|
14
|
+
display: flex;
|
|
15
|
+
justify-content: flex-end;
|
|
16
|
+
padding: var(--rt-space-lg);
|
|
17
|
+
border-block-start: 1px solid var(--rt-color-border-subtle);
|
|
18
|
+
gap: var(--rt-space-sm);
|
|
19
|
+
|
|
20
|
+
&:empty {
|
|
21
|
+
display: none;
|
|
22
|
+
}
|
|
12
23
|
}
|
|
13
24
|
}
|
|
@@ -1,21 +1,33 @@
|
|
|
1
1
|
/* rt-dialog-header — заголовок модалки. ViewEncapsulation.None.
|
|
2
|
-
Селекторы префиксованы `.rt-dialog-header`.
|
|
2
|
+
Селекторы префиксованы `.rt-dialog-header`.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
Хост адресуется по имени элемента, а не по классу блока: класс
|
|
5
|
+
`.rt-dialog-header` висит и на `<rt-dialog-header>`, и на `<header>` внутри
|
|
6
|
+
него. Правило на голом классе доставалось обоим — хост становился flex-строкой,
|
|
7
|
+
а `<header>` внутри неё сжимался по содержимому: заголовок и крестик стояли
|
|
8
|
+
вплотную вместо краёв окна, и разделитель рисовался дважды, двумя линиями
|
|
9
|
+
разной длины. Хост боксом не становится: строка шапки должна быть прямым
|
|
10
|
+
flex-ребёнком рамки окна. */
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
rt-dialog-header {
|
|
13
|
+
display: contents;
|
|
14
|
+
|
|
15
|
+
.rt-dialog-header {
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
justify-content: space-between;
|
|
19
|
+
padding: var(--rt-space-lg);
|
|
20
|
+
border-block-end: 1px solid var(--rt-color-border-subtle);
|
|
21
|
+
gap: var(--rt-space-sm);
|
|
22
|
+
|
|
23
|
+
&__title {
|
|
24
|
+
margin: 0;
|
|
25
|
+
color: var(--rt-color-text-primary);
|
|
26
|
+
font-size: var(--rt-text-xl);
|
|
27
|
+
}
|
|
17
28
|
|
|
18
|
-
|
|
19
|
-
|
|
29
|
+
&__close {
|
|
30
|
+
margin-inline-start: auto;
|
|
31
|
+
}
|
|
20
32
|
}
|
|
21
33
|
}
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
ViewEncapsulation.None — селекторы префиксованы `.rt-dialog`.
|
|
3
3
|
Видимость / backdrop / focus-trap обеспечивает CDK Overlay через RtDialogService. */
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/* Хост адресуется по имени элемента, а не по классу блока: класс `.rt-dialog`
|
|
6
|
+
висит и на `<rt-dialog>`, и на рамке внутри него, поэтому правила рамки
|
|
7
|
+
вложены в хост — так они достаются только рамке. Сам хост боксом не
|
|
8
|
+
становится: рамка должна быть прямым flex-ребёнком overlay-панели. */
|
|
9
|
+
rt-dialog {
|
|
6
10
|
display: contents;
|
|
7
11
|
}
|
|
8
12
|
|
|
@@ -18,7 +22,7 @@
|
|
|
18
22
|
--rt-dialog-backdrop-blur: 5px;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
.rt-dialog {
|
|
25
|
+
rt-dialog .rt-dialog {
|
|
22
26
|
display: flex;
|
|
23
27
|
overflow: hidden;
|
|
24
28
|
width: var(--rt-dialog-width, var(--rt-dialog-width-md));
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# `rt-download-link`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-download-link label="Договор.pdf" (downloadClick)="save(file)" />
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
| вход | тип | умолчание |
|
|
8
|
+
| ------- | -------- | -------------- |
|
|
9
|
+
| `label` | `string` | **обязателен** |
|
|
10
|
+
|
|
11
|
+
| выход | тип |
|
|
12
|
+
| --------------- | ------ |
|
|
13
|
+
| `downloadClick` | `void` |
|
|
14
|
+
|
|
15
|
+
## Главное, что нужно знать
|
|
16
|
+
|
|
17
|
+
**Это кнопка, а не ссылка.** Адреса компонент не знает и ничего не скачивает — качает
|
|
18
|
+
потребитель по событию. Так работают и подписанные ссылки с коротким сроком жизни, и выгрузки,
|
|
19
|
+
которые сначала надо построить.
|
|
20
|
+
|
|
21
|
+
## Как этим пользоваться
|
|
22
|
+
|
|
23
|
+
- Подпись для скринридера собирается из переведённой формулировки и имени файла
|
|
24
|
+
(`uiDownloadFile` с подстановкой `{{name}}`) — на экране видно имя, а вслух читается
|
|
25
|
+
«Download: Договор.pdf».
|
|
26
|
+
- Для перечня вложений есть [`rt-file-list`](../file-list/CONTEXT.md); эта кнопка — одиночная.
|
|
@@ -14,6 +14,19 @@
|
|
|
14
14
|
font-weight: var(--rt-font-weight-medium);
|
|
15
15
|
gap: var(--rt-space-sm);
|
|
16
16
|
line-height: var(--rt-leading-snug);
|
|
17
|
+
|
|
18
|
+
&:hover {
|
|
19
|
+
text-decoration: underline;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Своей рамки у кнопки нет — она нарисована как ссылка, и умолчательный контур
|
|
23
|
+
браузер снимает вместе с рамкой. Без этого правила место фокуса с клавиатуры
|
|
24
|
+
не видно вовсе: кнопка выглядит одинаково выбранной и невыбранной. */
|
|
25
|
+
&:focus-visible {
|
|
26
|
+
border-radius: var(--rt-radius-sm);
|
|
27
|
+
box-shadow: var(--rt-shadow-focus-ring);
|
|
28
|
+
outline: none;
|
|
29
|
+
}
|
|
17
30
|
}
|
|
18
31
|
|
|
19
32
|
&__icon {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# `rt-empty-state`
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<rt-empty-state icon="folder" title="Файлов нет" description="Прикрепите первый документ">
|
|
5
|
+
<img rtEmptyIllustration src="/img/empty.svg" alt="" />
|
|
6
|
+
<button rtButton (click)="attach()">Прикрепить</button>
|
|
7
|
+
</rt-empty-state>
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
| вход | тип | умолчание |
|
|
11
|
+
| ------------- | ---------------------- | --------- |
|
|
12
|
+
| `icon` | `IRtIcon.Name \| null` | `null` |
|
|
13
|
+
| `title` | `string` | `''` |
|
|
14
|
+
| `description` | `string \| null` | `null` |
|
|
15
|
+
|
|
16
|
+
Выходов нет. Два слота проекции:
|
|
17
|
+
|
|
18
|
+
- `[rtEmptyIllustration]` — картинка над иконкой;
|
|
19
|
+
- слот по умолчанию — действие (кнопка).
|
|
20
|
+
|
|
21
|
+
## Главное, что нужно знать
|
|
22
|
+
|
|
23
|
+
**Каждая часть необязательна.** Пустой `title` не создаёт пустой строки в раскладке, `null`-иконка
|
|
24
|
+
и `null`-описание просто не рисуются. Заглушка без единого входа — законное состояние: остаётся
|
|
25
|
+
только слот действия.
|
|
26
|
+
|
|
27
|
+
## Как этим пользоваться
|
|
28
|
+
|
|
29
|
+
- Иконка рисуется крупной (`xl`, 32px) и приглушённой: это заглушка, а не аффорданс. Кликабельным
|
|
30
|
+
должно быть проецируемое действие.
|
|
31
|
+
- Слот действия существует всегда, даже пустым; прячет его `:empty` в стилях.
|
|
32
|
+
|
|
33
|
+
## Рядом
|
|
34
|
+
|
|
35
|
+
- [`rt-note`](../note/CONTEXT.md) — когда объяснить нужно, а места под заглушку нет.
|