@rt-tools/ui-kit-v2 0.11.0 → 0.12.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 (165) hide show
  1. package/README.md +110 -5
  2. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +947 -0
  3. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -0
  4. package/fesm2022/rt-tools-ui-kit-v2.mjs +11327 -8086
  5. package/fesm2022/rt-tools-ui-kit-v2.mjs.map +1 -1
  6. package/package.json +8 -1
  7. package/rt-tools-ui-kit-v2-0.12.0.tgz +0 -0
  8. package/src/assets/fonts/montserrat-cyrillic-ext-wght-italic.woff2 +0 -0
  9. package/src/assets/fonts/montserrat-cyrillic-ext-wght-normal.woff2 +0 -0
  10. package/src/assets/fonts/montserrat-cyrillic-wght-italic.woff2 +0 -0
  11. package/src/assets/fonts/montserrat-cyrillic-wght-normal.woff2 +0 -0
  12. package/src/assets/fonts/montserrat-latin-ext-wght-italic.woff2 +0 -0
  13. package/src/assets/fonts/montserrat-latin-ext-wght-normal.woff2 +0 -0
  14. package/src/assets/fonts/montserrat-latin-wght-italic.woff2 +0 -0
  15. package/src/assets/fonts/montserrat-latin-wght-normal.woff2 +0 -0
  16. package/src/assets/icons/arrows.svg +9 -0
  17. package/src/assets/icons/equals.svg +8 -0
  18. package/src/assets/icons/user-off.svg +16 -0
  19. package/src/assets/icons-material/NOTICE.md +6 -2
  20. package/src/assets/icons-material/arrow-left.fill.svg +3 -0
  21. package/src/assets/icons-material/arrow-left.svg +1 -1
  22. package/src/assets/icons-material/arrow-right.fill.svg +3 -0
  23. package/src/assets/icons-material/arrow-right.svg +3 -0
  24. package/src/assets/icons-material/arrows.fill.svg +5 -0
  25. package/src/assets/icons-material/arrows.svg +5 -0
  26. package/src/assets/icons-material/ban.fill.svg +5 -0
  27. package/src/assets/icons-material/ban.svg +1 -1
  28. package/src/assets/icons-material/bars.fill.svg +3 -0
  29. package/src/assets/icons-material/bars.svg +1 -1
  30. package/src/assets/icons-material/check.fill.svg +3 -0
  31. package/src/assets/icons-material/check.svg +1 -1
  32. package/src/assets/icons-material/chevron-down.fill.svg +3 -0
  33. package/src/assets/icons-material/chevron-down.svg +1 -1
  34. package/src/assets/icons-material/chevron-left.fill.svg +3 -0
  35. package/src/assets/icons-material/chevron-left.svg +1 -1
  36. package/src/assets/icons-material/chevron-right.fill.svg +3 -0
  37. package/src/assets/icons-material/chevron-right.svg +1 -1
  38. package/src/assets/icons-material/close.fill.svg +3 -0
  39. package/src/assets/icons-material/close.svg +1 -1
  40. package/src/assets/icons-material/cloud-upload.fill.svg +5 -0
  41. package/src/assets/icons-material/cloud-upload.svg +1 -1
  42. package/src/assets/icons-material/cog.fill.svg +5 -0
  43. package/src/assets/icons-material/cog.svg +1 -1
  44. package/src/assets/icons-material/copy.fill.svg +5 -0
  45. package/src/assets/icons-material/copy.svg +1 -1
  46. package/src/assets/icons-material/ellipsis-h.fill.svg +5 -0
  47. package/src/assets/icons-material/ellipsis-h.svg +1 -1
  48. package/src/assets/icons-material/ellipsis-v.fill.svg +5 -0
  49. package/src/assets/icons-material/ellipsis-v.svg +1 -1
  50. package/src/assets/icons-material/email.fill.svg +5 -0
  51. package/src/assets/icons-material/email.svg +5 -0
  52. package/src/assets/icons-material/equals.fill.svg +3 -0
  53. package/src/assets/icons-material/equals.svg +3 -0
  54. package/src/assets/icons-material/external-link.fill.svg +5 -0
  55. package/src/assets/icons-material/external-link.svg +1 -1
  56. package/src/assets/icons-material/eye-slash.fill.svg +5 -0
  57. package/src/assets/icons-material/eye-slash.svg +1 -1
  58. package/src/assets/icons-material/eye.fill.svg +5 -0
  59. package/src/assets/icons-material/eye.svg +1 -1
  60. package/src/assets/icons-material/ico-download.fill.svg +5 -0
  61. package/src/assets/icons-material/ico-download.svg +1 -1
  62. package/src/assets/icons-material/ico-plus.fill.svg +3 -0
  63. package/src/assets/icons-material/ico-plus.svg +1 -1
  64. package/src/assets/icons-material/info-circle.fill.svg +5 -0
  65. package/src/assets/icons-material/info-circle.svg +1 -1
  66. package/src/assets/icons-material/pencil.fill.svg +5 -0
  67. package/src/assets/icons-material/pencil.svg +1 -1
  68. package/src/assets/icons-material/refresh.fill.svg +5 -0
  69. package/src/assets/icons-material/refresh.svg +1 -1
  70. package/src/assets/icons-material/search.fill.svg +5 -0
  71. package/src/assets/icons-material/search.svg +1 -1
  72. package/src/assets/icons-material/star.fill.svg +3 -0
  73. package/src/assets/icons-material/star.svg +1 -1
  74. package/src/assets/icons-material/sync.fill.svg +5 -0
  75. package/src/assets/icons-material/sync.svg +1 -1
  76. package/src/assets/icons-material/table.fill.svg +5 -0
  77. package/src/assets/icons-material/table.svg +1 -1
  78. package/src/assets/icons-material/tag.fill.svg +5 -0
  79. package/src/assets/icons-material/tag.svg +1 -1
  80. package/src/assets/icons-material/trash.fill.svg +5 -0
  81. package/src/assets/icons-material/trash.svg +1 -1
  82. package/src/assets/icons-material/user-off.fill.svg +5 -0
  83. package/src/assets/icons-material/user-off.svg +5 -0
  84. package/src/assets/icons-material/user.fill.svg +5 -0
  85. package/src/assets/icons-material/user.svg +1 -1
  86. package/src/lib/components/CONTEXT.md +3 -0
  87. package/src/lib/components/action-bar/CONTEXT.md +53 -0
  88. package/src/lib/components/action-bar/rt-action-bar-holder.component.scss +70 -0
  89. package/src/lib/components/action-bar/rt-action-bar.component.scss +122 -0
  90. package/src/lib/components/aside/CONTEXT.md +5 -1
  91. package/src/lib/components/aside/_rt-aside-overlay.scss +9 -0
  92. package/src/lib/components/aside/header/rt-aside-header.component.scss +18 -2
  93. package/src/lib/components/aside/rt-aside.component.scss +5 -1
  94. package/src/lib/components/autocomplete/CONTEXT.md +2 -1
  95. package/src/lib/components/button/CONTEXT.md +11 -11
  96. package/src/lib/components/checkbox/rt-checkbox.component.scss +5 -1
  97. package/src/lib/components/data-list/CONTEXT.md +122 -0
  98. package/src/lib/components/data-list/rt-data-list.component.scss +57 -0
  99. package/src/lib/components/data-list/settings/rt-data-list-settings-aside.component.scss +256 -0
  100. package/src/lib/components/data-list/toolbar/rt-data-list-toolbar.component.scss +128 -0
  101. package/src/lib/components/data-table/CONTEXT.md +102 -0
  102. package/src/lib/components/data-table/cell/rt-data-table-cell.component.scss +115 -0
  103. package/src/lib/components/data-table/filter-cell/rt-data-table-filter-cell.component.scss +51 -0
  104. package/src/lib/components/data-table/header-cell/rt-data-table-header-cell.component.scss +111 -0
  105. package/src/lib/components/data-table/rt-data-table.component.scss +263 -0
  106. package/src/lib/components/date-picker/rt-date-picker.component.scss +13 -0
  107. package/src/lib/components/delta-view/CONTEXT.md +1 -1
  108. package/src/lib/components/dialog/CONTEXT.md +4 -0
  109. package/src/lib/components/dynamic-list/CONTEXT.md +90 -0
  110. package/src/lib/components/dynamic-list/rt-dynamic-list.component.scss +123 -0
  111. package/src/lib/components/empty-state/CONTEXT.md +5 -0
  112. package/src/lib/components/empty-state/rt-empty-state.component.scss +19 -5
  113. package/src/lib/components/file-card/rt-file-card.component.scss +14 -0
  114. package/src/lib/components/file-drop/rt-file-drop.component.scss +44 -8
  115. package/src/lib/components/icon-button/rt-icon-button.component.scss +28 -4
  116. package/src/lib/components/input/rt-input.component.scss +25 -1
  117. package/src/lib/components/input-number/rt-input-number.component.scss +13 -0
  118. package/src/lib/components/menu/CONTEXT.md +10 -0
  119. package/src/lib/components/menu/rt-menu-item.component.scss +35 -3
  120. package/src/lib/components/menu/rt-menu.component.scss +15 -5
  121. package/src/lib/components/multiselect/CONTEXT.md +3 -0
  122. package/src/lib/components/multiselect/rt-multiselect.component.scss +58 -1
  123. package/src/lib/components/pagination/CONTEXT.md +4 -1
  124. package/src/lib/components/pagination/rt-pagination.component.scss +43 -13
  125. package/src/lib/components/radio-button/CONTEXT.md +42 -0
  126. package/src/lib/components/radio-button/rt-radio-button.component.scss +105 -0
  127. package/src/lib/components/scroll-area/CONTEXT.md +52 -0
  128. package/src/lib/components/scroll-area/rt-scroll-area.component.scss +69 -0
  129. package/src/lib/components/select/CONTEXT.md +8 -1
  130. package/src/lib/components/select/rt-select.component.scss +110 -7
  131. package/src/lib/components/table/CONTEXT.md +46 -1
  132. package/src/lib/components/table/filter-header/CONTEXT.md +64 -0
  133. package/src/lib/components/table/filter-header/rt-table-filter-header.component.scss +76 -0
  134. package/src/lib/components/table/rt-table.component.scss +32 -1
  135. package/src/lib/components/tag/CONTEXT.md +9 -0
  136. package/src/lib/components/tag/rt-tag.component.scss +38 -0
  137. package/src/lib/components/textarea/CONTEXT.md +1 -1
  138. package/src/lib/components/toolbar/CONTEXT.md +3 -0
  139. package/src/lib/components/toolbar/rt-toolbar.component.scss +9 -2
  140. package/src/lib/components/tooltip/CONTEXT.md +4 -4
  141. package/src/lib/components/tooltip/rt-tooltip.component.scss +2 -2
  142. package/src/{lib → rich-editor/lib}/components/chat/CONTEXT.md +1 -1
  143. package/src/{lib → rich-editor/lib}/components/message-composer/CONTEXT.md +5 -1
  144. package/src/{lib → rich-editor/lib}/components/message-composer/rt-message-composer.component.scss +9 -0
  145. package/src/{lib → rich-editor/lib}/components/rich-editor/CONTEXT.md +2 -2
  146. package/src/styles/_coarse-pointer.scss +15 -0
  147. package/src/styles/_coexist.scss +28 -0
  148. package/src/styles/_fonts.scss +99 -0
  149. package/src/styles/_index.scss +13 -0
  150. package/src/styles/_preset-material.scss +201 -26
  151. package/src/styles/_primitives.scss +41 -5
  152. package/src/styles/_scrollbar.scss +16 -12
  153. package/src/styles/_semantic-lists.scss +89 -0
  154. package/src/styles/_semantic.scss +80 -9
  155. package/src/styles/_theme-dark.scss +36 -0
  156. package/src/styles/_theme-scope.scss +111 -0
  157. package/src/styles/tokens.coexist.scss +3 -0
  158. package/src/styles/tokens.package.scss +6 -0
  159. package/styles/tokens.coexist.css +27 -0
  160. package/styles/tokens.css +5443 -43
  161. package/types/rt-tools-ui-kit-v2-rich-editor.d.ts +529 -0
  162. package/types/rt-tools-ui-kit-v2.d.ts +2744 -1367
  163. package/rt-tools-ui-kit-v2-0.11.0.tgz +0 -0
  164. /package/src/{lib → rich-editor/lib}/components/chat/rt-chat.component.scss +0 -0
  165. /package/src/{lib → rich-editor/lib}/components/rich-editor/rt-rich-editor.component.scss +0 -0
@@ -0,0 +1,529 @@
1
+ import * as i0 from '@angular/core';
2
+ import { AfterViewInit, OnDestroy, Signal, ElementRef, WritableSignal, InputSignal, OutputEmitterRef, InputSignalWithTransform, TemplateRef, PipeTransform } from '@angular/core';
3
+ import { RtFormControlBase, IQuillDelta, TRtKitLabelMap } from '@rt-tools/ui-kit-v2';
4
+ import { BooleanInput, NumberInput } from '@angular/cdk/coercion';
5
+ import { FormGroup, FormControl } from '@angular/forms';
6
+
7
+ /**
8
+ * Режим тулбара: `full` — весь whitelist, `minimal` — жирный + два списка
9
+ * (G113: чаты оператора/админа/саппорта).
10
+ */
11
+ type TRtRichEditorToolbar = 'full' | 'minimal';
12
+ /**
13
+ * Rich-редактор чата на Quill. Наследует CVA-ядро input-семейства
14
+ * (`RtFormControlBase`) — плагается в reactive-форму. Quill грузится **лениво**
15
+ * (`import("quill")` в `ngAfterViewInit`), тип импортируется `import type` и в
16
+ * бандл не попадает. Значение CVA — `IQuillDelta` (или `null`, когда пусто).
17
+ */
18
+ declare class RtRichEditorComponent extends RtFormControlBase<IQuillDelta | null> implements AfterViewInit, OnDestroy {
19
+ #private;
20
+ protected readonly editorHost: Signal<ElementRef<HTMLElement>>;
21
+ /**
22
+ * Смонтирована ли начинка редактора. Пока нет, хост несёт признак недостроенности: съёмке
23
+ * витрины и любой другой проверке снаружи иначе нечем отличить недостроенный редактор от
24
+ * достроенного пустого — оба выглядят пустым местом.
25
+ */
26
+ protected readonly mounted: WritableSignal<boolean>;
27
+ protected readonly hasValue: Signal<boolean>;
28
+ readonly placeholder: InputSignal<string>;
29
+ /** Набор кнопок форматирования; сужает и принимаемые форматы (вставка из буфера). */
30
+ readonly toolbar: InputSignal<TRtRichEditorToolbar>;
31
+ /** Enter без Shift в редакторе — сигнал отправки (Shift+Enter остаётся переносом). */
32
+ readonly enterPressed: OutputEmitterRef<void>;
33
+ readonly displayText: Signal<string>;
34
+ ngAfterViewInit(): Promise<void>;
35
+ ngOnDestroy(): void;
36
+ writeValue(value: IQuillDelta | null): void;
37
+ setDisabledState(isDisabled: boolean): void;
38
+ protected getEmptyValue(): IQuillDelta | null;
39
+ protected focusAfterClear(): void;
40
+ static ɵfac: i0.ɵɵFactoryDeclaration<RtRichEditorComponent, never>;
41
+ static ɵcmp: i0.ɵɵComponentDeclaration<RtRichEditorComponent, "rt-rich-editor", never, { "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "toolbar": { "alias": "toolbar"; "required": false; "isSignal": true; }; }, { "enterPressed": "enterPressed"; }, never, never, true, never>;
42
+ }
43
+
44
+ /**
45
+ * Модель `<rt-message-composer>`: один корневой неймспейс с
46
+ * префиксом `I`.
47
+ */
48
+ declare namespace IRtMessageComposer {
49
+ /** Полезная нагрузка отправки: текст сообщения + прикреплённые файлы. */
50
+ interface SubmitPayload {
51
+ text: string;
52
+ files: File[];
53
+ /**
54
+ * Rich-тело строкой JSON (при `formatting`-режиме) — взаимоисключимо с
55
+ * непустым `text`. `null`/отсутствует ⇒ отправляется plain-текст.
56
+ */
57
+ delta?: string | null;
58
+ }
59
+ }
60
+
61
+ interface IComposerFormShape {
62
+ message: FormControl<string>;
63
+ files: FormControl<File[]>;
64
+ delta: FormControl<IQuillDelta | null>;
65
+ }
66
+ /**
67
+ * Композер сообщения: авто-растущая textarea сверху, под разделительной линией —
68
+ * ряд контролов (слева — прикрепить файл, справа — отправить). Презентационный,
69
+ * без data-access: наружу торчит только `submitted` с текстом и файлами.
70
+ *
71
+ * Отправка — `Enter` (без Shift) или клик по иконке; `Shift+Enter` — перенос
72
+ * строки. Кнопка отправки активна, пока есть непустой текст или вложения.
73
+ */
74
+ declare class RtMessageComposerComponent {
75
+ #private;
76
+ protected readonly t: Signal<TRtKitLabelMap>;
77
+ protected readonly placeholderText: Signal<string>;
78
+ protected readonly fileInput: Signal<ElementRef<HTMLInputElement> | undefined>;
79
+ protected readonly form: FormGroup<IComposerFormShape>;
80
+ protected readonly files: Signal<readonly File[]>;
81
+ /** Отправка доступна: есть контент (текст/delta/файлы), композер не занят. */
82
+ protected readonly canSend: Signal<boolean>;
83
+ /** Текст-подсказка textarea. Пусто — берётся переведённое умолчание. */
84
+ readonly placeholder: InputSignal<string>;
85
+ /** Фильтр типов для file-input (`.pdf,.doc,...`). */
86
+ readonly accept: InputSignal<string>;
87
+ /** Показывать кнопку вложения и список файлов. */
88
+ readonly attachments: InputSignalWithTransform<boolean, BooleanInput>;
89
+ /** Отправка в процессе — блокирует submit и показывает её недоступность. */
90
+ readonly sending: InputSignalWithTransform<boolean, BooleanInput>;
91
+ /** Полная блокировка композера. */
92
+ readonly disabled: InputSignalWithTransform<boolean, BooleanInput>;
93
+ /** Rich-режим: вместо textarea — Quill-редактор, отправка уходит delta-каналом. */
94
+ readonly formatting: InputSignalWithTransform<boolean, BooleanInput>;
95
+ /** Набор кнопок rich-редактора (проброс в `rt-rich-editor`). */
96
+ readonly toolbar: InputSignal<TRtRichEditorToolbar>;
97
+ /** Минимальная высота textarea в строках. */
98
+ readonly minRows: InputSignalWithTransform<number, NumberInput>;
99
+ /** Максимальная высота textarea в строках (дальше — скролл). */
100
+ readonly maxRows: InputSignalWithTransform<number, NumberInput>;
101
+ /**
102
+ * Файлы, выброшенные drag-n-drop'ом в обёртку чата: пробрасываются сюда как
103
+ * вложения. Каждый физический drop даёт новый массив, поэтому effect
104
+ * перезапускается на каждое перетаскивание; файлы добавляются к уже выбранным.
105
+ */
106
+ readonly droppedFiles: InputSignal<File[] | null>;
107
+ /** Отправка сообщения: текст + файлы (последние — только при `attachments`). */
108
+ readonly submitted: OutputEmitterRef<IRtMessageComposer.SubmitPayload>;
109
+ constructor();
110
+ protected onKeydown(event: KeyboardEvent): void;
111
+ protected openPicker(): void;
112
+ protected onFilesPicked(event: Event): void;
113
+ protected removeFile(index: number): void;
114
+ protected submit(): void;
115
+ static ɵfac: i0.ɵɵFactoryDeclaration<RtMessageComposerComponent, never>;
116
+ static ɵcmp: i0.ɵɵComponentDeclaration<RtMessageComposerComponent, "rt-message-composer", never, { "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "accept": { "alias": "accept"; "required": false; "isSignal": true; }; "attachments": { "alias": "attachments"; "required": false; "isSignal": true; }; "sending": { "alias": "sending"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "formatting": { "alias": "formatting"; "required": false; "isSignal": true; }; "toolbar": { "alias": "toolbar"; "required": false; "isSignal": true; }; "minRows": { "alias": "minRows"; "required": false; "isSignal": true; }; "maxRows": { "alias": "maxRows"; "required": false; "isSignal": true; }; "droppedFiles": { "alias": "droppedFiles"; "required": false; "isSignal": true; }; }, { "submitted": "submitted"; }, never, never, true, never>;
117
+ }
118
+
119
+ /**
120
+ * Модель общего чат-компонента `rt-chat`: один корневой неймспейс
121
+ * с префиксом `I`. Доменные стора маппят свою модель сообщения в `Message`, а сам
122
+ * `rt-chat` ничего не знает о доменных сущностях — только рисует тред и композер.
123
+ */
124
+ /**
125
+ * Судьба своего сообщения в ленте: пока летит на сервер — часы, после записи —
126
+ * двойной чек, после того как собеседник открыл переписку — тот же чек синим,
127
+ * при обрыве — «!» с повтором. Входящие сообщения статуса не имеют.
128
+ */
129
+ declare enum ERtChatMessageStatus {
130
+ Sending = "sending",
131
+ Sent = "sent",
132
+ Read = "read",
133
+ Failed = "failed"
134
+ }
135
+ declare namespace IRtChat {
136
+ /** Алиас enum'а для доменных мапперов, работающих через неймспейс. */
137
+ type MessageStatus = ERtChatMessageStatus;
138
+ /**
139
+ * Есть ли у реплики доступные действия потребителя — гейт кнопки «…» у неё.
140
+ *
141
+ * Признак приходит предикатом, а не полем модели: действие, которого кит не знает,
142
+ * его моделью и не описывается.
143
+ */
144
+ type MessageActionsPredicate = (message: Message) => boolean;
145
+ /** Вложение сообщения — домен маппит свой ресурс, скачивание по `public_id`. */
146
+ interface Attachment {
147
+ id: number;
148
+ /** Обезличенное имя для карточки файла. */
149
+ name: string;
150
+ /** Непрозрачный идентификатор файла — консюмер качает blob по нему. */
151
+ publicId: string;
152
+ }
153
+ /**
154
+ * Одно сообщение треда в уже подготовленной для отображения форме: автор —
155
+ * готовый лейбл (домен решает, как его собрать), `own`/`system` — флаги
156
+ * выравнивания и стиля, дата — ISO-строка (форматируется `DatePipe` внутри).
157
+ */
158
+ interface Message {
159
+ /** Идентификатор сообщения — ключ `@for track`. */
160
+ id: number | string;
161
+ /** Клиентский временный id оптимистичного сообщения (до ответа сервера). */
162
+ tempId?: string;
163
+ /**
164
+ * Статус своего сообщения. Отсутствует у входящих: чужому сообщению
165
+ * нечего сообщать о доставке.
166
+ */
167
+ status?: MessageStatus;
168
+ /** Готовый лейбл автора (доменный authorLabel). */
169
+ author: string;
170
+ /** Сообщение текущего пользователя — выравнивается вправо, акцентный фон. */
171
+ own: boolean;
172
+ /** Признак системного сообщения (рендерится обычным входящим пузырём). */
173
+ system?: boolean;
174
+ text: string;
175
+ /**
176
+ * Rich-тело (Quill delta) — при наличии рендерится вместо plain `text`
177
+ * через `rt-delta-view`. `null`/отсутствует ⇒ показываем `text`.
178
+ */
179
+ delta?: IQuillDelta | null;
180
+ /** ISO-дата создания. */
181
+ createdAt: string;
182
+ /** URL вложения (если есть) — наличие включает карточку файла. */
183
+ fileUrl?: string | null;
184
+ /** Имя вложения для отображения в карточке. */
185
+ fileName?: string | null;
186
+ /**
187
+ * Вложения сообщения (несколько файлов). Непустой массив имеет приоритет
188
+ * над legacy-парой `fileUrl`/`fileName` (та равна первому вложению).
189
+ */
190
+ attachments?: readonly Attachment[];
191
+ /** Внешняя ссылка-призыв (привязка Telegram) — наличие включает кнопку-ссылку. */
192
+ deepLink?: string | null;
193
+ /** Подпись кнопки-ссылки `deepLink` (домен решает текст по состоянию). */
194
+ deepLinkLabel?: string | null;
195
+ /** Сообщение помечено удалённым — рендерится плейсхолдером вместо содержимого. */
196
+ deleted?: boolean;
197
+ /** Показать действие «удалить» на этом сообщении (домен решает по правам/направлению). */
198
+ canDelete?: boolean;
199
+ /**
200
+ * Текст подтверждения перед удалением. Задан — клик по корзине открывает
201
+ * confirm-попап с этим текстом, удаление уходит только после подтверждения.
202
+ * Пусто/undefined — удаление сразу по клику (домен без confirm-семантики).
203
+ */
204
+ deleteConfirm?: string;
205
+ }
206
+ /** Payload отправки: текст + файлы (пусто/undefined, если вложения выключены). */
207
+ interface SendPayload {
208
+ text: string;
209
+ files?: File[];
210
+ /**
211
+ * Rich-тело строкой JSON (`JSON.stringify(getContents())`) — взаимоисключимо
212
+ * с непустым `text`. При наличии домен шлёт delta-канал (`message:""`).
213
+ */
214
+ delta?: string | null;
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Контекст шаблона `[rtChatMessageActions]`: `$implicit` — реплика треда.
220
+ */
221
+ interface IRtChatMessageActionsContext {
222
+ $implicit: IRtChat.Message;
223
+ }
224
+ /**
225
+ * Захватывает `<ng-template rtChatMessageActions let-message>` — содержимое «…»-меню
226
+ * действий у реплики. `rt-chat` рисует этот шаблон рядом с точечными действиями реплики,
227
+ * прокидывая её через `$implicit`.
228
+ *
229
+ * Точечные действия остаются на месте: удаление, повтор отправки и скачивание общи для
230
+ * любой переписки. Шаблон нужен тем действиям, которых кит не знает и знать не должен, —
231
+ * без него потребителю оставалось ждать правки кита либо ставить кнопку над диалогом, то
232
+ * есть не там, где читают реплику.
233
+ *
234
+ * Дженерика у контекста нет: тип реплики у переписки один, и type-carrier, который у
235
+ * строки таблицы несёт тип строки, здесь не нужен.
236
+ *
237
+ * @example
238
+ * ```html
239
+ * <rt-chat [messages]="messages()" [messageHasActions]="canAct">
240
+ * <ng-template rtChatMessageActions let-message>
241
+ * <rt-menu-item icon="link" label="Скопировать ссылку" (selected)="copy(message)" />
242
+ * </ng-template>
243
+ * </rt-chat>
244
+ * ```
245
+ */
246
+ declare class RtChatMessageActionsDirective {
247
+ readonly template: TemplateRef<IRtChatMessageActionsContext>;
248
+ /** Type-guard: сужает контекст шаблона до реплики при проверке шаблонов. */
249
+ static ngTemplateContextGuard(_directive: RtChatMessageActionsDirective, context: unknown): context is IRtChatMessageActionsContext;
250
+ static ɵfac: i0.ɵɵFactoryDeclaration<RtChatMessageActionsDirective, never>;
251
+ static ɵdir: i0.ɵɵDirectiveDeclaration<RtChatMessageActionsDirective, "ng-template[rtChatMessageActions]", never, {}, {}, never, never, true, never>;
252
+ }
253
+
254
+ /**
255
+ * Общий презентационный чат: тред сообщений + композер. Никакого data-access/api — история
256
+ * приходит готовым массивом `IRtChat.Message[]`, отправка/обновление/скачивание уходят наружу
257
+ * через outputs, а стор и blob-загрузка остаются в feature-компоненте каждого домена.
258
+ *
259
+ * Композер держит свой `FormGroup` — наружу торчит только `send`. Полноэкранный
260
+ * режим (expand) — overlay внутри самого компонента (`--expanded`), потребителю
261
+ * лишь опционально сообщается через `expandedChange`.
262
+ *
263
+ * Шапка (тулбар «Чат» + refresh/expand) рендерится только когда включена хотя бы
264
+ * одна из иконок (`showRefresh`/`showExpand`): в диалогах своя header-обёртка,
265
+ * туда тулбар не нужен.
266
+ */
267
+ declare class RtChatComponent {
268
+ #private;
269
+ /**
270
+ * Дата сообщения форматируется по активной локали приложения, а не по
271
+ * `LOCALE_ID`: тот вычисляется на подъёме приложения и там, где язык
272
+ * переключают на ходу, оставил бы часы в прежнем формате до перезагрузки.
273
+ *
274
+ * Данные локали для `DatePipe` регистрирует приложение: без них пайп падает
275
+ * `Missing locale data`, и кит за это не отвечает.
276
+ */
277
+ protected readonly t: Signal<TRtKitLabelMap>;
278
+ protected readonly locale: Signal<string>;
279
+ /**
280
+ * Скролл-контейнер треда — для автопрокрутки к последнему сообщению.
281
+ * `viewChild` не работает на ES-private (`#`) поле, поэтому `protected`.
282
+ */
283
+ protected readonly thread: Signal<ElementRef<HTMLElement> | undefined>;
284
+ /** Скрытый нативный input — открывается кнопкой-скрепкой внутри поля ввода. */
285
+ protected readonly composerFileEl: Signal<ElementRef<HTMLInputElement> | undefined>;
286
+ protected readonly attachmentFallbackName: Signal<string>;
287
+ /** Подписи с учётом того, что передал хозяин компонента: своё важнее умолчания */
288
+ protected readonly titleText: Signal<string>;
289
+ protected readonly placeholderText: Signal<string>;
290
+ protected readonly emptyHintText: Signal<string>;
291
+ protected readonly MessageStatus: typeof ERtChatMessageStatus;
292
+ /** Плейсхолдеры скелетонов треда при первичной загрузке. */
293
+ protected readonly skeletonRows: readonly number[];
294
+ /** Полноэкранный режим (overlay поверх страницы). */
295
+ protected readonly expanded: WritableSignal<boolean>;
296
+ protected readonly replyForm: FormGroup<{
297
+ message: FormControl<string>;
298
+ files: FormControl<File[]>;
299
+ }>;
300
+ /** Композер заблокирован при загрузке/рефреше треда или отправке in-flight. */
301
+ protected readonly composerDisabled: Signal<boolean>;
302
+ /**
303
+ * Можно ли отправить: композер активен и есть содержимое — непустой текст или
304
+ * (при включённых вложениях) хотя бы один файл. Сообщение само по себе не
305
+ * бывает «невалидным» — пустоту гасим дизейблом кнопки, а не подсветкой поля.
306
+ */
307
+ protected readonly canSend: Signal<boolean>;
308
+ /** Показывать ли шапку: только когда включена хотя бы одна иконка. */
309
+ protected readonly showHeader: Signal<boolean>;
310
+ /** Подпись кнопки fullscreen: тернарник в шаблоне переводом не покрыть. */
311
+ protected readonly expandLabel: Signal<string>;
312
+ /** Выбранные, но ещё не отправленные файлы — карточки над полем ввода. */
313
+ protected readonly selectedFiles: Signal<File[]>;
314
+ /**
315
+ * Drop-зона активна только когда вложения включены, ответ доступен и композер
316
+ * не занят (загрузка/рефреш/отправка). Иначе обёртка `rt-file-drop` просто
317
+ * пропускает контент без оверлея.
318
+ */
319
+ protected readonly dropDisabled: Signal<boolean>;
320
+ /**
321
+ * Файл из drag-n-drop для rich-композера: каждый drop кладёт новый `File`,
322
+ * пробрасывается в `rt-message-composer` через input. Классический композер
323
+ * принимает drop напрямую в свой `replyForm`.
324
+ */
325
+ protected readonly droppedFiles: WritableSignal<File[] | null>;
326
+ /**
327
+ * Шаблон действий у реплики: `<ng-template rtChatMessageActions let-message>`.
328
+ *
329
+ * Не объявлен — разметка реплики прежняя, и лишней кнопки у неё не появляется.
330
+ */
331
+ protected readonly messageActions: Signal<RtChatMessageActionsDirective | undefined>;
332
+ /** История сообщений (consumer уже смаппил из доменной модели). */
333
+ readonly messages: InputSignal<readonly IRtChat.Message[]>;
334
+ /** Первичная загрузка — скелетоны вместо треда. */
335
+ readonly loading: InputSignalWithTransform<boolean, BooleanInput>;
336
+ /** Рефреш при наличии данных — тред остаётся, композер дизейблится. */
337
+ readonly fetching: InputSignalWithTransform<boolean, BooleanInput>;
338
+ /** Доступна ли отправка (иначе вместо композера — note с причиной). */
339
+ readonly canReply: InputSignalWithTransform<boolean, BooleanInput>;
340
+ /** Причина блокировки ответа — note под тредом. */
341
+ readonly replyBlockReason: InputSignal<string | null>;
342
+ /** Отправка in-flight — спиннер/дизейбл на кнопке. */
343
+ readonly sending: InputSignalWithTransform<boolean, BooleanInput>;
344
+ /**
345
+ * Подписи по умолчанию приходят пустыми и переводятся ниже: значение
346
+ * `input()` вычисляется при создании компонента, и готовый перевод застыл бы
347
+ * на языке, активном в этот момент.
348
+ */
349
+ readonly placeholder: InputSignal<string>;
350
+ readonly title: InputSignal<string>;
351
+ /** Текст, когда треда ещё нет (заявка не создана). */
352
+ readonly emptyHint: InputSignal<string>;
353
+ /** Есть ли тред (иначе показываем `emptyHint`). */
354
+ readonly hasThread: InputSignalWithTransform<boolean, BooleanInput>;
355
+ /** Включить загрузку файлов в композере. */
356
+ readonly attachments: InputSignalWithTransform<boolean, BooleanInput>;
357
+ /** `accept` для file-input (`.pdf,.doc,...`). */
358
+ readonly accept: InputSignal<string>;
359
+ /**
360
+ * Компактный композер (`rt-message-composer`): авто-растущая textarea над
361
+ * разделителем + ряд иконок (прикрепить слева, отправить справа) вместо
362
+ * классического вертикального стека. По умолчанию выключен.
363
+ */
364
+ readonly richComposer: InputSignalWithTransform<boolean, BooleanInput>;
365
+ /**
366
+ * Rich-текст в композере (Quill delta). Требует `richComposer` — форматирование
367
+ * живёт только в компактном композере. Отправка уходит delta-каналом.
368
+ */
369
+ readonly formatting: InputSignalWithTransform<boolean, BooleanInput>;
370
+ /** Набор кнопок rich-редактора (проброс до `rt-rich-editor` через композер). */
371
+ readonly formattingToolbar: InputSignal<TRtRichEditorToolbar>;
372
+ /**
373
+ * Растягивать чат на всю высоту контейнера: тред занимает свободное место,
374
+ * композер прижат к низу. По умолчанию выключен (тред с фикс. max-height).
375
+ * Требует, чтобы родитель давал высоту (flex-цепочка).
376
+ */
377
+ readonly fill: InputSignalWithTransform<boolean, BooleanInput>;
378
+ /** Показать иконку refresh в шапке. */
379
+ readonly showRefresh: InputSignalWithTransform<boolean, BooleanInput>;
380
+ /** Показать иконку expand (fullscreen) в шапке. */
381
+ readonly showExpand: InputSignalWithTransform<boolean, BooleanInput>;
382
+ /** Отправка сообщения (+файлы) — consumer вызывает свой store/api. */
383
+ readonly send: OutputEmitterRef<IRtChat.SendPayload>;
384
+ /** Клик refresh — consumer перетягивает тред. */
385
+ readonly refresh: OutputEmitterRef<void>;
386
+ /** Клик по вложению — consumer качает blob своим api. Шаблон зовёт выход прямо: релей-метод,
387
+ * который только повторял `emit`, ничего не добавлял, а файл при этом рос. */
388
+ readonly downloadFile: OutputEmitterRef<IRtChat.Message>;
389
+ /** Клик по карточке вложения из `attachments[]` — консюмер качает по `publicId`. */
390
+ readonly downloadAttachment: OutputEmitterRef<IRtChat.Attachment>;
391
+ /** Смена fullscreen-состояния. */
392
+ readonly expandedChange: OutputEmitterRef<boolean>;
393
+ readonly deleteMessage: OutputEmitterRef<IRtChat.Message>;
394
+ /** Повторная отправка своего сообщения со статусом `failed`. */
395
+ readonly retryMessage: OutputEmitterRef<IRtChat.Message>;
396
+ /**
397
+ * Есть ли у реплики действия потребителя — гейт кнопки «…» у неё.
398
+ *
399
+ * Не задан — точка действий показывается у каждой реплики: экран, который ничего не
400
+ * гейтит, показывает её так же, как это делает строка таблицы.
401
+ */
402
+ readonly messageHasActions: InputSignal<IRtChat.MessageActionsPredicate | null>;
403
+ constructor();
404
+ protected onSubmit(): void;
405
+ protected onComposerKeydown(event: KeyboardEvent): void;
406
+ /** Релей submit'а от `rt-message-composer` (rich-режим) наружу как `send`. */
407
+ protected onComposerSubmit(payload: IRtMessageComposer.SubmitPayload): void;
408
+ /**
409
+ * Файлы выброшены в обёртку чата: в rich-режиме отдаём массив композеру через
410
+ * input, в классическом — добавляем к уже выбранным в собственном `replyForm`.
411
+ */
412
+ protected onFilesDropped(files: File[]): void;
413
+ protected openFilePicker(): void;
414
+ protected onFilesPicked(event: Event): void;
415
+ protected removeFile(index: number): void;
416
+ /** Признак «читатель у нижнего края» обновляется по прокрутке: на нём стоит автопрокрутка. */
417
+ protected onThreadScroll(): void;
418
+ protected toggleExpand(): void;
419
+ static ɵfac: i0.ɵɵFactoryDeclaration<RtChatComponent, never>;
420
+ static ɵcmp: i0.ɵɵComponentDeclaration<RtChatComponent, "rt-chat", never, { "messages": { "alias": "messages"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "fetching": { "alias": "fetching"; "required": false; "isSignal": true; }; "canReply": { "alias": "canReply"; "required": false; "isSignal": true; }; "replyBlockReason": { "alias": "replyBlockReason"; "required": false; "isSignal": true; }; "sending": { "alias": "sending"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "emptyHint": { "alias": "emptyHint"; "required": false; "isSignal": true; }; "hasThread": { "alias": "hasThread"; "required": false; "isSignal": true; }; "attachments": { "alias": "attachments"; "required": false; "isSignal": true; }; "accept": { "alias": "accept"; "required": false; "isSignal": true; }; "richComposer": { "alias": "richComposer"; "required": false; "isSignal": true; }; "formatting": { "alias": "formatting"; "required": false; "isSignal": true; }; "formattingToolbar": { "alias": "formattingToolbar"; "required": false; "isSignal": true; }; "fill": { "alias": "fill"; "required": false; "isSignal": true; }; "showRefresh": { "alias": "showRefresh"; "required": false; "isSignal": true; }; "showExpand": { "alias": "showExpand"; "required": false; "isSignal": true; }; "messageHasActions": { "alias": "messageHasActions"; "required": false; "isSignal": true; }; }, { "send": "send"; "refresh": "refresh"; "downloadFile": "downloadFile"; "downloadAttachment": "downloadAttachment"; "expandedChange": "expandedChange"; "deleteMessage": "deleteMessage"; "retryMessage": "retryMessage"; }, ["messageActions"], ["[rtChatHeaderActions]", "[rtChatFooter]"], true, never>;
421
+ }
422
+
423
+ /**
424
+ * Есть ли у реплики хотя бы одно доступное действие потребителя.
425
+ *
426
+ * Признак приходит предикатом от экрана, а не подсчётом отрисованных пунктов меню:
427
+ * содержимое спроецированного шаблона известно только после отрисовки, и кнопка
428
+ * успевала бы мигнуть на первом кадре.
429
+ *
430
+ * Предикат не задан — у реплики есть действия: экран, который ничего не гейтит,
431
+ * показывает кнопку меню. Так же это устроено у строки таблицы, и расхождение между
432
+ * двумя точками проекции одного кита читалось бы дефектом.
433
+ */
434
+ declare function messageHasAvailableActions(message: IRtChat.Message, hasActions: IRtChat.MessageActionsPredicate | null): boolean;
435
+
436
+ /**
437
+ * Есть ли у реплики доступные действия — гейт кнопки «…» у неё.
438
+ *
439
+ * Pure-pipe: пересчитывается только при смене реплики или предиката, а не на каждом
440
+ * цикле проверки. Значение приходит из контекста шаблона (реплика треда), поэтому
441
+ * `computed()` здесь неприменим.
442
+ */
443
+ declare class RtChatMessageHasActionsPipe implements PipeTransform {
444
+ transform(message: IRtChat.Message, hasActions: IRtChat.MessageActionsPredicate | null): boolean;
445
+ static ɵfac: i0.ɵɵFactoryDeclaration<RtChatMessageHasActionsPipe, never>;
446
+ static ɵpipe: i0.ɵɵPipeDeclaration<RtChatMessageHasActionsPipe, "rtChatMessageHasActions", true>;
447
+ }
448
+
449
+ /**
450
+ * Статус уже сохранённого своего сообщения: сервер знает только, дошло ли оно до
451
+ * собеседника. Часы и ошибка — состояния клиентской очереди, сюда не попадают.
452
+ */
453
+ declare function deliveryStatus(read: boolean): ERtChatMessageStatus;
454
+ /**
455
+ * Сообщение, ещё не подтверждённое сервером. Серверного id и ссылок на вложения
456
+ * у него нет: есть клиентский ключ, текст и выбранные файлы.
457
+ *
458
+ * Rich-тело приходит уже разобранным — доменная форма его хранения тут ни при
459
+ * чём, а хранят его стороны по-разному: одна шлёт обычный текст, другая —
460
+ * строку композера, которая в том же виде уходит на сервер.
461
+ */
462
+ interface IRtPendingMessage {
463
+ tempId: string;
464
+ text: string;
465
+ delta?: IQuillDelta | null;
466
+ createdAt: string;
467
+ status: IRtChat.MessageStatus;
468
+ files: readonly File[];
469
+ }
470
+ /**
471
+ * Неподтверждённое сообщение → строка ленты. Показывается сразу после отправки:
472
+ * ждать ответа сервера, глядя в пустоту, незачем ни одной из сторон.
473
+ *
474
+ * Скрыть такое сообщение нельзя: на сервере его ещё нет, и удалять нечего.
475
+ */
476
+ declare function pendingToRtMessage(pending: IRtPendingMessage, authorLabel: string): IRtChat.Message;
477
+ /**
478
+ * Структурный контракт доменного сообщения, достаточный для маппинга в
479
+ * `IRtChat.Message`. Доменные модели удовлетворяют ему по форме;
480
+ * роль-специфика (автор/own/system) задаётся опциями.
481
+ */
482
+ interface IRtMappableMessage {
483
+ id: string | number;
484
+ tempId?: string;
485
+ status?: IRtChat.MessageStatus;
486
+ message: string;
487
+ messageDelta: IQuillDelta | null;
488
+ createdAt: string;
489
+ fileUrl: string | null;
490
+ attachments: readonly {
491
+ id: number;
492
+ name: string;
493
+ publicId: string;
494
+ }[];
495
+ }
496
+ /**
497
+ * Опции маппинга: роль-специфичные куски, которые не вывести из общего контракта.
498
+ *
499
+ * - `roleLabel` — подпись автора (обычно ярлык роли).
500
+ * - `isOwn` — своё ли это сообщение (правый пузырь).
501
+ * - `isSystem` — системное ли (нейтральный стиль); по умолчанию нет.
502
+ * - `fileName` — имя файла вложения; по умолчанию выводится из `fileUrl`.
503
+ * - `optimistic` — включить `tempId`/`status` (оптимистичная отправка); по умолчанию да.
504
+ * Для read-only тредов (агент) — `false`: тогда `id` берётся из серверного `id`.
505
+ */
506
+ interface IToRtMessageOptions<TMessage extends IRtMappableMessage> {
507
+ roleLabel: (message: TMessage) => string;
508
+ isOwn: (message: TMessage) => boolean;
509
+ isSystem?: (message: TMessage) => boolean;
510
+ fileName?: (message: TMessage) => string | null;
511
+ optimistic?: boolean;
512
+ }
513
+ /**
514
+ * Собрать `IRtChat.Message` из доменного сообщения. Общие поля (текст, delta,
515
+ * дата, вложения, файл) берутся из контракта; автор/own/system/имя файла —
516
+ * из опций. Ключ `id` — `tempId ?? id` в оптимистичном режиме (пока сервер не
517
+ * подтвердил), иначе серверный `id`.
518
+ */
519
+ declare function toRtMessage<TMessage extends IRtMappableMessage>(message: TMessage, options: IToRtMessageOptions<TMessage>): IRtChat.Message;
520
+
521
+ /** Ключ текстовой расшифровки иконки статуса сообщения. */
522
+ declare class RtChatStatusLabelPipe implements PipeTransform {
523
+ transform(status: ERtChatMessageStatus): string;
524
+ static ɵfac: i0.ɵɵFactoryDeclaration<RtChatStatusLabelPipe, never>;
525
+ static ɵpipe: i0.ɵɵPipeDeclaration<RtChatStatusLabelPipe, "rtChatStatusLabel", true>;
526
+ }
527
+
528
+ export { ERtChatMessageStatus, IRtChat, IRtMessageComposer, RtChatComponent, RtChatMessageActionsDirective, RtChatMessageHasActionsPipe, RtChatStatusLabelPipe, RtMessageComposerComponent, RtRichEditorComponent, deliveryStatus, messageHasAvailableActions, pendingToRtMessage, toRtMessage };
529
+ export type { IRtChatMessageActionsContext, IRtMappableMessage, IRtPendingMessage, IToRtMessageOptions, TRtRichEditorToolbar };