@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.
Files changed (111) hide show
  1. package/fesm2022/rt-tools-ui-kit-v2.mjs +313 -205
  2. package/fesm2022/rt-tools-ui-kit-v2.mjs.map +1 -1
  3. package/package.json +1 -1
  4. package/rt-tools-ui-kit-v2-0.3.0.tgz +0 -0
  5. package/src/lib/components/CONTEXT.md +49 -0
  6. package/src/lib/components/aside/CONTEXT.md +51 -0
  7. package/src/lib/components/aside/_rt-aside-overlay.scss +88 -0
  8. package/src/lib/components/aside/rt-aside.component.scss +19 -94
  9. package/src/lib/components/aside-section/CONTEXT.md +26 -0
  10. package/src/lib/components/autocomplete/CONTEXT.md +58 -0
  11. package/src/lib/components/bar-list/CONTEXT.md +27 -0
  12. package/src/lib/components/bar-list/rt-bar-list.component.scss +5 -2
  13. package/src/lib/components/bottom-sheet/CONTEXT.md +54 -0
  14. package/src/lib/components/bottom-sheet/rt-bottom-sheet.component.scss +53 -53
  15. package/src/lib/components/button/CONTEXT.md +53 -0
  16. package/src/lib/components/button/rt-button.directive.scss +16 -0
  17. package/src/lib/components/calendar/CONTEXT.md +41 -0
  18. package/src/lib/components/calendar/rt-calendar.component.scss +96 -99
  19. package/src/lib/components/card/CONTEXT.md +34 -0
  20. package/src/lib/components/card/rt-card.component.scss +5 -2
  21. package/src/lib/components/chat/CONTEXT.md +41 -0
  22. package/src/lib/components/checkbox/CONTEXT.md +37 -0
  23. package/src/lib/components/checkbox/rt-checkbox.component.scss +7 -2
  24. package/src/lib/components/collapsible-text/CONTEXT.md +29 -0
  25. package/src/lib/components/confirm-popover/CONTEXT.md +47 -0
  26. package/src/lib/components/container/CONTEXT.md +42 -0
  27. package/src/lib/components/container/rt-container.component.scss +5 -69
  28. package/src/lib/components/counter/CONTEXT.md +40 -0
  29. package/src/lib/components/counter/rt-counter.component.scss +10 -9
  30. package/src/lib/components/counter-row/CONTEXT.md +24 -0
  31. package/src/lib/components/counter-row/rt-counter-row.component.scss +23 -22
  32. package/src/lib/components/date-picker/CONTEXT.md +41 -0
  33. package/src/lib/components/delta-view/CONTEXT.md +31 -0
  34. package/src/lib/components/detail-list/CONTEXT.md +29 -0
  35. package/src/lib/components/dialog/CONTEXT.md +50 -0
  36. package/src/lib/components/dialog/rt-dialog.component.scss +6 -2
  37. package/src/lib/components/download-link/CONTEXT.md +26 -0
  38. package/src/lib/components/empty-state/CONTEXT.md +35 -0
  39. package/src/lib/components/field/CONTEXT.md +72 -0
  40. package/src/lib/components/file-card/CONTEXT.md +28 -0
  41. package/src/lib/components/file-drop/CONTEXT.md +39 -0
  42. package/src/lib/components/file-input/CONTEXT.md +36 -0
  43. package/src/lib/components/file-list/CONTEXT.md +15 -0
  44. package/src/lib/components/filter-control/CONTEXT.md +33 -0
  45. package/src/lib/components/form-control/CONTEXT.md +50 -0
  46. package/src/lib/components/header/CONTEXT.md +36 -0
  47. package/src/lib/components/icon/CONTEXT.md +46 -0
  48. package/src/lib/components/icon/rt-icon.component.scss +3 -5
  49. package/src/lib/components/icon-button/CONTEXT.md +49 -0
  50. package/src/lib/components/icon-button/rt-icon-button.component.scss +14 -1
  51. package/src/lib/components/info-item/CONTEXT.md +30 -0
  52. package/src/lib/components/input/CONTEXT.md +47 -0
  53. package/src/lib/components/input-number/CONTEXT.md +52 -0
  54. package/src/lib/components/live-badge/CONTEXT.md +31 -0
  55. package/src/lib/components/live-badge/rt-live-badge.component.scss +5 -2
  56. package/src/lib/components/logo/CONTEXT.md +42 -0
  57. package/src/lib/components/logo/rt-logo.component.scss +4 -6
  58. package/src/lib/components/menu/CONTEXT.md +65 -0
  59. package/src/lib/components/message/CONTEXT.md +35 -0
  60. package/src/lib/components/message/rt-message.component.scss +5 -2
  61. package/src/lib/components/message-composer/CONTEXT.md +46 -0
  62. package/src/lib/components/money-list/CONTEXT.md +32 -0
  63. package/src/lib/components/multiselect/CONTEXT.md +43 -0
  64. package/src/lib/components/night-grid/CONTEXT.md +31 -0
  65. package/src/lib/components/night-grid/rt-night-grid.component.scss +5 -2
  66. package/src/lib/components/note/CONTEXT.md +27 -0
  67. package/src/lib/components/notifications-bell/CONTEXT.md +29 -0
  68. package/src/lib/components/page-header/CONTEXT.md +33 -0
  69. package/src/lib/components/pagination/CONTEXT.md +43 -0
  70. package/src/lib/components/photo-viewer/CONTEXT.md +30 -0
  71. package/src/lib/components/photo-viewer/rt-photo-viewer.component.scss +95 -96
  72. package/src/lib/components/popover/CONTEXT.md +54 -0
  73. package/src/lib/components/rich-editor/CONTEXT.md +36 -0
  74. package/src/lib/components/section-nav/CONTEXT.md +27 -0
  75. package/src/lib/components/section-nav/rt-section-nav.component.scss +7 -1
  76. package/src/lib/components/select/CONTEXT.md +54 -0
  77. package/src/lib/components/skeleton/CONTEXT.md +41 -0
  78. package/src/lib/components/skeleton-wrapper/CONTEXT.md +24 -0
  79. package/src/lib/components/skeleton-wrapper/rt-skeleton-wrapper.component.scss +5 -1
  80. package/src/lib/components/spinner/CONTEXT.md +39 -0
  81. package/src/lib/components/split-button/CONTEXT.md +39 -0
  82. package/src/lib/components/stat-tile/CONTEXT.md +35 -0
  83. package/src/lib/components/stat-tile/rt-stat-tile.component.scss +5 -2
  84. package/src/lib/components/stepper/CONTEXT.md +36 -0
  85. package/src/lib/components/stepper/rt-stepper.component.scss +5 -2
  86. package/src/lib/components/table/CONTEXT.md +53 -0
  87. package/src/lib/components/table/settings-aside/rt-table-settings-aside.component.scss +4 -2
  88. package/src/lib/components/table/settings-panel/rt-table-settings-panel.component.scss +1 -1
  89. package/src/lib/components/tabs/CONTEXT.md +56 -0
  90. package/src/lib/components/tag/CONTEXT.md +47 -0
  91. package/src/lib/components/tag/rt-tag.component.scss +5 -2
  92. package/src/lib/components/textarea/CONTEXT.md +38 -0
  93. package/src/lib/components/theme-toggle/CONTEXT.md +28 -0
  94. package/src/lib/components/theme-toggle/rt-theme-toggle.component.scss +5 -1
  95. package/src/lib/components/thread-list/CONTEXT.md +42 -0
  96. package/src/lib/components/timeline/CONTEXT.md +33 -0
  97. package/src/lib/components/timeline/rt-timeline.component.scss +5 -2
  98. package/src/lib/components/toast/CONTEXT.md +53 -0
  99. package/src/lib/components/toggle-button-group/CONTEXT.md +38 -0
  100. package/src/lib/components/toggle-button-group/rt-toggle-button-group.component.scss +12 -7
  101. package/src/lib/components/toggle-switch/CONTEXT.md +35 -0
  102. package/src/lib/components/toggle-switch/rt-toggle-switch.component.scss +7 -2
  103. package/src/lib/components/toolbar/CONTEXT.md +22 -0
  104. package/src/lib/components/tooltip/CONTEXT.md +34 -0
  105. package/src/lib/components/welcome-dialog/CONTEXT.md +20 -0
  106. package/src/lib/components/welcome-dialog/rt-welcome-dialog.component.scss +6 -2
  107. package/src/lib/components/workspace/CONTEXT.md +46 -0
  108. package/src/lib/components/workspace-details/CONTEXT.md +42 -0
  109. package/styles/tokens.css +14 -0
  110. package/types/rt-tools-ui-kit-v2.d.ts +50 -29
  111. 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
- :host {
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
- .rt-counter {
7
- &__value {
8
- min-width: var(--rt-counter-value-min-width);
9
- color: var(--rt-counter-value-color);
10
- font-size: var(--rt-counter-value-font-size);
11
- font-variant-numeric: tabular-nums;
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
- :host {
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
- .rt-counter-row {
9
- &__text {
10
- display: flex;
11
- flex-direction: column;
12
- gap: var(--rt-space-2xs);
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
- &__label {
17
- color: var(--rt-counter-row-label-color);
18
- font-size: var(--rt-counter-row-label-font-size);
19
- font-weight: var(--rt-font-weight-medium);
20
- }
17
+ &__text {
18
+ display: flex;
19
+ flex-direction: column;
20
+ gap: var(--rt-space-2xs);
21
+ text-align: start;
22
+ }
21
23
 
22
- &__hint {
23
- color: var(--rt-counter-row-hint-color);
24
- font-size: var(--rt-counter-row-hint-font-size);
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
- :host(:not(:first-of-type)) {
32
- border-top: var(--rt-border-width-thin) solid var(--rt-counter-row-color-border);
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) — готовое приветственное окно.
@@ -2,7 +2,11 @@
2
2
  ViewEncapsulation.None — селекторы префиксованы `.rt-dialog`.
3
3
  Видимость / backdrop / focus-trap обеспечивает CDK Overlay через RtDialogService. */
4
4
 
5
- :host {
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
+ (`rtKit.uiDownloadFile` с подстановкой `{{name}}`) — на экране видно имя, а вслух читается
25
+ «Download: Договор.pdf».
26
+ - Для перечня вложений есть [`rt-file-list`](../file-list/CONTEXT.md); эта кнопка — одиночная.
@@ -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) — когда объяснить нужно, а места под заглушку нет.
@@ -0,0 +1,72 @@
1
+ # `rt-field`
2
+
3
+ Обёртка единой анатомии поля: подпись сверху, контрол посередине, подсказка или ошибка снизу.
4
+
5
+ ```html
6
+ <rt-field label="Имя" hint="Как в паспорте" help="Совпадает с документом" [errors]="{ required: 'Без имени не пустим' }">
7
+ <rt-input [formControl]="name" />
8
+ </rt-field>
9
+
10
+ <rt-field label="Комментарий">
11
+ <rt-textarea [formControl]="comment" />
12
+ <span rtFieldHint>
13
+ Не короче
14
+ <strong>трёх</strong>
15
+ символов
16
+ </span>
17
+ </rt-field>
18
+ ```
19
+
20
+ | вход | тип | умолчание |
21
+ | ------------------ | ------------------------------ | --------- |
22
+ | `label` | `string` | `''` |
23
+ | `hint` | `string` | `''` |
24
+ | `help` | `string` | `''` |
25
+ | `errors` | `Record<validatorKey, string>` | `{}` |
26
+ | `readonly` | `boolean` | `false` |
27
+ | `loading` | `boolean` | `false` |
28
+ | `hideRequiredMark` | `boolean` | `false` |
29
+ | `reserveHintSpace` | `boolean` | `false` |
30
+
31
+ ## Главное, что нужно знать
32
+
33
+ **Поле само находит контрол** через `contentChild(RtFormControlBase)` — достаточно положить любой
34
+ контрол кита внутрь. Дальше оно:
35
+
36
+ - выдаёт контролу авто-идентификатор (`rt-field-N`) и связывает с ним `<label for>`, если у
37
+ контрола нет своего `controlId`;
38
+ - рисует «\*», если у контрола есть валидатор обязательности (обнаруживается сам, входа для этого
39
+ нет);
40
+ - показывает текст ошибки и включает режим плоского чтения.
41
+
42
+ Всё это работает только для контролов кита. Обычный `<input>` внутрь класть можно, но подписи,
43
+ звёздочки и ошибки он не получит.
44
+
45
+ ## Ошибки
46
+
47
+ Показывается **одно** сообщение — первого сработавшего валидатора. Текст берётся так:
48
+
49
+ 1. `errors[имяВалидатора]`, переданный формой;
50
+ 2. переведённое умолчание кита (`required`, `email`, `minlength`, `maxlength`, `min`, `max`,
51
+ `pattern`);
52
+ 3. пусто, если валидатор незнаком и своего текста нет.
53
+
54
+ Ошибка появляется только после касания или правки контрола — это решает сам контрол, а не поле.
55
+
56
+ ## Подсказка
57
+
58
+ - Строковый `[hint]` — обычный случай.
59
+ - Проекция `[rtFieldHint]` — когда подсказке нужна разметка; она **важнее** строкового входа.
60
+ - Без резерва подсказка уступает место ошибке. С `reserveHintSpace` она остаётся в потоке, но
61
+ прячется от скринридера (`aria-hidden`), а высота строки перестаёт скакать при появлении ошибки.
62
+
63
+ ## Края
64
+
65
+ - `hideRequiredMark` нужен формам, где обязательны все поля и звёздочка становится шумом.
66
+ - `loading` оборачивает **только зону контрола** в заглушку; подпись и подсказка остаются
67
+ видимыми — по ним читается, чего ждут.
68
+ - Текст пояснения (`help`) живёт в поповере и до наведения не отрисован.
69
+
70
+ ## Рядом
71
+
72
+ - [`RtFormControlBase`](../form-control/CONTEXT.md) — что именно поле читает у контрола.
@@ -0,0 +1,28 @@
1
+ # `rt-file-card`
2
+
3
+ Карточка файла: иконка по типу, имя, размер и необязательные действия.
4
+
5
+ ```html
6
+ <rt-file-card name="Договор.pdf" size="md" showDownload showRemove [sizeBytes]="204800" (download)="save()" (removed)="detach()" />
7
+ ```
8
+
9
+ | вход | тип | умолчание |
10
+ | -------------------------------------------- | ---------------------- | -------------- |
11
+ | `name` | `string` | **обязателен** |
12
+ | `sizeBytes` | `number \| null` | `null` |
13
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` |
14
+ | `showDownload` / `showRename` / `showRemove` | `boolean` | `false` |
15
+ | `disabled` | `boolean` | `false` |
16
+
17
+ Выходы: `download`, `rename`, `removed`.
18
+
19
+ ## Главное, что нужно знать
20
+
21
+ **Иконка и заголовок выводятся из имени файла.** Расширение выбирает иконку (незнакомое даёт
22
+ общую), а заголовок — это имя без расширения с заменёнными `_` и `-` на пробелы. Отдельных
23
+ входов для них нет.
24
+
25
+ **Длинное имя обрезается в середине**, чтобы расширение осталось видно: `очень-длинн...pdf`.
26
+
27
+ **Без входов действий карточка немая** — ни одной кнопки. Она бывает просто вложением, которое
28
+ показывают.
@@ -0,0 +1,39 @@
1
+ # `rt-file-drop`
2
+
3
+ Область приёма файлов перетаскиванием. Оборачивает любое содержимое и показывает подсказку
4
+ поверх него.
5
+
6
+ ```html
7
+ <rt-file-drop accept=".pdf" [zones]="zones" (filesDropped)="attach($event)" (zoneFilesDropped)="attachTo($event)">
8
+ <app-form />
9
+ </rt-file-drop>
10
+ ```
11
+
12
+ | вход | тип | умолчание |
13
+ | -------------- | ----------------- | -------------------------------- |
14
+ | `disabled` | `boolean` | `false` |
15
+ | `overlayLabel` | `string` | `''` → `rtKit.uiFileDropOverlay` |
16
+ | `zones` | `readonly Zone[]` | `[]` |
17
+ | `accept` | `string` | `''` |
18
+
19
+ Выходы: `filesDropped` (`File[]`), `zoneFilesDropped` (`{ zoneId, files }`), `draggingChange`.
20
+
21
+ ## Главное, что нужно знать
22
+
23
+ **С зонами общее событие сброса не поднимается** — зона обязана быть названа. Без зон, наоборот,
24
+ приходит только `filesDropped`.
25
+
26
+ **Уход курсора гасит подсказку не сразу** (80 мс). Без задержки рамка мерцала бы при переходе
27
+ между дочерними элементами области.
28
+
29
+ **Файлы не того типа отсекаются по `accept`**, и пустой после фильтра сброс события не поднимает.
30
+
31
+ ## Края
32
+
33
+ - Зона выбирается долей от высоты области. Пока высоты нет (первый кадр, скрытая вкладка),
34
+ выбор детерминирован — первая зона, а не отказ: файл терять нельзя.
35
+ - Отключённая область не показывает подсказку и не принимает файлы.
36
+
37
+ ## Рядом
38
+
39
+ - [`rt-file-input`](../file-input/CONTEXT.md) — выбор файлов кнопкой.