@rt-tools/ui-kit-v2 0.39.2 → 0.41.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rt-tools/ui-kit-v2",
3
- "version": "0.39.2",
3
+ "version": "0.41.0",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Yauheni Krumin",
6
6
  "type": "module",
@@ -80,6 +80,11 @@
80
80
  глифом шрифта Material Symbols, как первый кит. Шрифт кит не везёт: глиф виден, где его подключило
81
81
  приложение.
82
82
 
83
+ **Размер значка ячейки задаёт поле `icon.size` колонки:** `sm`, `md` или `lg`, без поля — `lg`;
84
+ значение не из списка, пришедшее из сохранённых настроек мимо типов, тоже рисуется как `lg`.
85
+ Ячейка сама ставит ступени значка `--rt-icon-step-*`, и глиф читает те же свойства: значок набора
86
+ и глиф в одной колонке одного размера, даже если предок сменил ступени для своих детей.
87
+
83
88
  ## Части семейства
84
89
 
85
90
  | Часть | Что делает |
@@ -8,6 +8,12 @@
8
8
  --rt-data-table-cell-transition-duration: var(--rt-duration-fast);
9
9
  --rt-data-table-cell-glyph-size: var(--rt-size-6);
10
10
 
11
+ /* Значок набора в ячейке идёт теми же ступенями, что глиф: предок, сменивший ступень
12
+ значка для своих детей, иначе развёл бы их размеры в одной колонке. */
13
+ --rt-icon-step-sm: var(--rt-size-4);
14
+ --rt-icon-step-md: var(--rt-size-5);
15
+ --rt-icon-step-lg: var(--rt-size-6);
16
+
11
17
  position: relative;
12
18
  display: flex;
13
19
  width: 100%;
@@ -34,6 +40,15 @@
34
40
  &--filled {
35
41
  font-variation-settings: 'FILL' 1;
36
42
  }
43
+
44
+ /* Размер значка из описания колонки — те же ступени, что у значка кита в ячейке. */
45
+ &--sm {
46
+ --rt-data-table-cell-glyph-size: var(--rt-icon-step-sm);
47
+ }
48
+
49
+ &--md {
50
+ --rt-data-table-cell-glyph-size: var(--rt-icon-step-md);
51
+ }
37
52
  }
38
53
 
39
54
  &__prefix,
@@ -0,0 +1,10 @@
1
+ @layer rt-kit.components {
2
+ /* Пояснение под матрицей: почему в колонке глифа видно начало слова, а не значок. Ширина —
3
+ две ступени по 400px, чтобы строка не тянулась на всю витрину. */
4
+ .app-data-table-matrix__note {
5
+ max-width: calc(var(--rt-size-100) * 2);
6
+ color: var(--rt-color-text-muted);
7
+ font-size: var(--rt-text-sm);
8
+ line-height: var(--rt-leading-relaxed);
9
+ }
10
+ }
@@ -44,6 +44,23 @@ rtChatMessageActions let-message>` рисуется в меню «…» у ре
44
44
  Что печатает вторая сторона, показывает `typingText`: плашка стоит поверх низа ленты и места не
45
45
  занимает, пустая строка её прячет. Текст и перевод даёт приложение.
46
46
 
47
+ **Готовый текст подставляет метод `setDraft`.** Он заменяет черновик во всех трёх полях ответа и
48
+ ставит курсор в конец. Файлы остаются, сигнала набора нет, закрытое поле текст не принимает.
49
+ Работу метода показывает история `QuickReplies` страницы переписки: кнопка у поля ответа и
50
+ подсказка в боковой панели ставят текст в поле, гостю ничего не уходит. Метод `draftText` читает
51
+ текст поля без разметки и файлы не трогает.
52
+
53
+ **Подпись на пузыре даёт приложение.** `dateLabel` стоит вместо времени, `dateIcon` на месте значка
54
+ статуса. Значки ошибки отправки и недоставки остаются: они предупреждают. Подписи дней считаются от
55
+ `createdAt`.
56
+
57
+ **Пузырь мессенджера не шире 85% ленты и не шире 320.** Предел стоит на пузыре, а не на строке:
58
+ кнопки у реплики лежат рядом и не отнимают его ширину. Значок статуса 16, подпись и значок на своём
59
+ пузыре без прозрачности, пузырь гостя белый с тенью. Боковой отступ чата 16, на узком месте 12.
60
+
61
+ **Слот `rtChatComposerBar` стоит над рамкой поля ответа** во всех видах поля. Пустой слот места не
62
+ занимает, а без поля слота нет.
63
+
47
64
  **Удалённое сообщение остаётся в ленте** отдельной пометкой вместо текста: иначе непонятно, на
48
65
  что отвечали соседние реплики.
49
66
 
@@ -1,5 +1,7 @@
1
1
  // Вид мессенджера чата: метки дней, ряд кнопок под полем, пузыри мягкие, свой — цвета действия.
2
2
  // Лежит отдельно от стилей чата, чтобы оба файла уместились в предел длины.
3
+ @use '../../../../styles/breakpoints' as bp;
4
+
3
5
  @layer rt-kit.components {
4
6
  .rt-chat {
5
7
  --rt-chat-day-label-radius: var(--rt-radius-full);
@@ -32,20 +34,25 @@
32
34
  }
33
35
  }
34
36
 
35
- // Вид мессенджера: пузырь мягкий, свой — цвета действия, пузырь гостя серый на белой ленте,
36
- // время и знак доставки справа внизу
37
+ // Вид мессенджера: пузырь мягкий, свой — цвета действия, пузырь гостя белый с тенью на белой
38
+ // ленте, время и знак доставки справа внизу
37
39
  &--messenger {
38
40
  --rt-chat-bubble-radius: var(--rt-radius-xl);
39
41
  --rt-chat-bubble-corner-radius: var(--rt-radius-sm);
40
42
  --rt-chat-messenger-border-width: var(--rt-border-width-thin);
41
43
  --rt-chat-messenger-panel-radius: var(--rt-radius-lg);
42
44
  --rt-chat-messenger-actions-shadow: var(--rt-shadow-sm);
45
+ --rt-chat-bubble-in-shadow: var(--rt-shadow-sm);
43
46
  --rt-chat-bubble-own-bg: var(--rt-color-action-primary);
44
- --rt-chat-bubble-in-bg: var(--rt-color-bg-subtle);
47
+ --rt-chat-bubble-in-bg: var(--rt-color-bg-surface);
45
48
 
46
49
  box-sizing: border-box;
47
50
  padding: var(--rt-space-4);
48
51
 
52
+ @include bp.place-max(bp.$rt-bp-md) {
53
+ padding: calc(var(--rt-space-2) + var(--rt-space-1));
54
+ }
55
+
49
56
  // Высоту шапке дают имя и строка под ним, плотная полоса панели здесь не нужна
50
57
  .rt-chat__header {
51
58
  --rt-toolbar-height: auto;
@@ -69,12 +76,29 @@
69
76
  > :first-child {
70
77
  margin-block-start: auto;
71
78
  }
79
+
80
+ @include bp.place-max(bp.$rt-bp-sm) {
81
+ padding-inline: calc(var(--rt-space-2) + var(--rt-space-1));
82
+ }
83
+ }
84
+
85
+ // Строка занимает всю ленту: кнопки у реплики стоят рядом и не отнимают ширину пузыря
86
+ .rt-chat__message-row {
87
+ width: 100%;
88
+ max-width: 100%;
89
+ }
90
+
91
+ .rt-chat__message-row--own {
92
+ justify-content: flex-end;
72
93
  }
73
94
 
74
95
  .rt-chat__message {
96
+ max-width: round(min(85%, 320px), 1px);
97
+ box-sizing: border-box;
75
98
  padding: var(--rt-space-2) calc(var(--rt-space-2) + var(--rt-space-1)) var(--rt-space-1);
76
99
  border-radius: var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius)
77
100
  var(--rt-chat-bubble-corner-radius);
101
+ box-shadow: var(--rt-chat-bubble-in-shadow);
78
102
  gap: var(--rt-space-0-5);
79
103
  }
80
104
 
@@ -103,7 +127,7 @@
103
127
 
104
128
  .rt-chat__message-text {
105
129
  font-size: var(--rt-text-sm);
106
- line-height: round(1em * var(--rt-leading-tight), 1px);
130
+ line-height: round(1em * var(--rt-leading-snug), 1px);
107
131
  }
108
132
 
109
133
  .rt-chat__message--own .rt-chat__message-text,
@@ -112,15 +136,6 @@
112
136
  color: var(--rt-color-action-on-primary);
113
137
  }
114
138
 
115
- // На своём пузыре время и галочки тише текста: время прозрачнее, галочки чуть плотнее
116
- .rt-chat__message--own .rt-chat__message-date {
117
- opacity: 0.75;
118
- }
119
-
120
- .rt-chat__message--own .rt-chat__message-status {
121
- opacity: 0.85;
122
- }
123
-
124
139
  .rt-chat__message-footer {
125
140
  justify-content: flex-end;
126
141
  }
@@ -143,7 +158,6 @@
143
158
 
144
159
  .rt-chat__message-date {
145
160
  font-size: var(--rt-text-2xs);
146
- line-height: round(1em * var(--rt-leading-tight), 1px);
147
161
  }
148
162
 
149
163
  .rt-chat__message-author {
@@ -176,6 +176,24 @@
176
176
  }
177
177
  }
178
178
 
179
+ &__composer-bar {
180
+ display: flex;
181
+ flex: 0 0 auto;
182
+ flex-direction: column;
183
+
184
+ // Колонка держит шаг `--rt-space-4`, а полоса стоит вплотную к полю: 8px по кадру
185
+ margin-block-end: calc(var(--rt-space-2) - var(--rt-space-4));
186
+
187
+ &:empty {
188
+ display: none;
189
+ }
190
+
191
+ &:not(:empty) + .rt-chat__composer,
192
+ &:not(:empty) + .rt-chat__composer-rich {
193
+ margin-top: 0;
194
+ }
195
+ }
196
+
179
197
  &__footer-slot {
180
198
  display: flex;
181
199
  flex-direction: column;
@@ -298,14 +316,17 @@
298
316
 
299
317
  &__message-footer {
300
318
  display: flex;
319
+ min-width: 0;
301
320
  align-items: center;
302
321
  gap: var(--rt-space-1);
303
322
  }
304
323
 
305
324
  &__message-date {
325
+ min-width: 0;
306
326
  color: var(--rt-chat-bubble-date);
307
327
  font-size: var(--rt-text-xs);
308
328
  line-height: var(--rt-leading-none);
329
+ overflow-wrap: anywhere;
309
330
  }
310
331
 
311
332
  // Пер-месседж статус своего сообщения: часы / двойной чек / «!»+retry.
@@ -18,6 +18,8 @@ export declare namespace IRtAiPromptInput {
18
18
  }
19
19
  /** Можно ли отправить: есть непустой текст или файл, поле свободно. */
20
20
  export declare function rtPromptCanSend(text: string, files: number, busy: boolean): boolean;
21
+ /** Ставит фокус в поле, а курсор — за последний символ. */
22
+ export declare function rtCaretToEnd(node: HTMLTextAreaElement): void;
21
23
  /**
22
24
  * Поле ввода ИИ-чата: одна рамка, текст сверху, ряд действий внутри рамки снизу.
23
25
  *
@@ -102,6 +104,8 @@ export declare class RtAiPromptInputComponent {
102
104
  readonly controlPicked: OutputEmitterRef<IRtAiPromptInput.Control>;
103
105
  /** Ставит фокус в поле. */
104
106
  focus(): void;
107
+ /** Заменяет черновик и ставит курсор в конец: значение пишется в узел сразу, не ждёт отрисовки. */
108
+ setDraft(text: string): void;
105
109
  protected onInput(event: Event): void;
106
110
  protected onKeydown(event: KeyboardEvent): void;
107
111
  protected pick(control: IRtAiPromptInput.Control): void;
@@ -128,6 +128,8 @@ export declare namespace IRtDataTable {
128
128
  tooltip?: string;
129
129
  icon?: IRtDataTable.Icon;
130
130
  }
131
+ /** Размер значка ячейки — ступень значка кита. */
132
+ type IconSize = 'sm' | 'md' | 'lg';
131
133
  /**
132
134
  * Значок колонки. Имя — имя первого кита; рисует его набор кита через перечень соответствий,
133
135
  * а шаблон значка, отданный приложением, рисует его сам.
@@ -138,6 +140,11 @@ export declare namespace IRtDataTable {
138
140
  tooltip?: string;
139
141
  visible?: boolean;
140
142
  placement?: 'left' | 'right';
143
+ /**
144
+ * Размер значка в ячейке: `sm` — 16px, `md` — 20px, `lg` — 24px. Без поля значок 24px, как
145
+ * был. Шапка колонки его не читает.
146
+ */
147
+ size?: IRtDataTable.IconSize;
141
148
  /**
142
149
  * Контурный рисунок вместо залитого, как у первого кита: без признака значок колонки залит.
143
150
  * Заливку рисует материальный набор; свой набор кита рисует значок одним рисунком.
@@ -172,7 +172,17 @@ export declare class RtDataTableCellComponent<T = Record<string, unknown>> {
172
172
  * приложение, а без шрифта на месте значка стоит его имя.
173
173
  */
174
174
  protected readonly glyph: Signal<string | null>;
175
- /** Залитый глиф — как залитый значок набора: контурным его делает `outlined` колонки. */
175
+ /**
176
+ * Размер значка из описания колонки; без него 24px, как было. Описание колонки приходит и из
177
+ * сохранённых настроек, мимо типов: размер не из списка тоже даёт 24px, иначе значок набора
178
+ * взял бы свою ступень, а глиф остался бы прежним.
179
+ */
180
+ protected readonly iconSize: Signal<IRtDataTable.IconSize>;
181
+ /**
182
+ * Залитый глиф — как залитый значок набора: контурным его делает `outlined` колонки. Размер
183
+ * глифа идёт той же ступенью, что значок набора; 24px задаёт сам элемент, и для `lg` своего
184
+ * признака нет.
185
+ */
176
186
  protected readonly glyphMods: Signal<Record<string, boolean>>;
177
187
  protected readonly iconContext: Signal<IRtDataTableIconContext<T> | null>;
178
188
  /**
@@ -33,6 +33,8 @@ export declare class RtRichEditorComponent extends RtFormControlBase<IQuillDelta
33
33
  ngAfterViewInit(): Promise<void>;
34
34
  /** Ставит фокус в редактор; до загрузки Quill ничего не делает. */
35
35
  focus(): void;
36
+ /** Ставит фокус в редактор, а курсор — за последний символ; до загрузки Quill ничего не делает. */
37
+ focusAtEnd(): void;
36
38
  ngOnDestroy(): void;
37
39
  writeValue(value: IQuillDelta | null): void;
38
40
  setDisabledState(isDisabled: boolean): void;
@@ -142,6 +144,10 @@ export declare class RtMessageComposerComponent {
142
144
  constructor();
143
145
  /** Ставит фокус в поле: в textarea, а в режиме форматирования — в rich-редактор. */
144
146
  focus(): void;
147
+ /** Заменяет черновик и ставит курсор в конец; выбранные файлы остаются. */
148
+ setDraft(text: string): void;
149
+ /** Текст поля без разметки: форматирование читается как обычный текст, файлы сюда не входят. */
150
+ draftText(): string;
145
151
  protected onKeydown(event: KeyboardEvent): void;
146
152
  protected stop(): void;
147
153
  protected openPicker(): void;
@@ -287,6 +293,13 @@ export declare namespace IRtChat {
287
293
  delta?: IQuillDelta | null;
288
294
  /** ISO-дата создания. */
289
295
  createdAt: string;
296
+ /** Готовая подпись вместо времени `createdAt`. Подписи дней считаются от `createdAt` и так. */
297
+ dateLabel?: string;
298
+ /**
299
+ * Значок на месте значка статуса, размером как он. Статусы «не ушло» и «не доставлено»
300
+ * сильнее: их значок предупреждает об ошибке.
301
+ */
302
+ dateIcon?: IRtIcon.Name;
290
303
  /** URL вложения (если есть) — наличие включает карточку файла. */
291
304
  fileUrl?: string | null;
292
305
  /** Имя вложения для отображения в карточке. */
@@ -401,6 +414,10 @@ export declare class RtChatComponent {
401
414
  protected readonly thread: Signal<ElementRef<HTMLElement> | undefined>;
402
415
  /** Скрытый нативный input — открывается кнопкой-скрепкой внутри поля ввода. */
403
416
  protected readonly composerFileEl: Signal<ElementRef<HTMLInputElement> | undefined>;
417
+ /** Поля ответа трёх видов: метод `setDraft` пишет в то, которое сейчас стоит. */
418
+ protected readonly composerInputEl: Signal<ElementRef<HTMLTextAreaElement> | undefined>;
419
+ protected readonly messageComposer: Signal<RtMessageComposerComponent | undefined>;
420
+ protected readonly promptInput: Signal<RtAiPromptInputComponent | undefined>;
404
421
  protected readonly attachmentFallbackName: Signal<string>;
405
422
  /** Подписи с учётом того, что передал хозяин компонента: своё важнее умолчания */
406
423
  protected readonly titleText: Signal<string>;
@@ -488,7 +505,6 @@ export declare class RtChatComponent {
488
505
  * Требует, чтобы родитель давал высоту (flex-цепочка).
489
506
  */
490
507
  readonly fill: InputSignalWithTransform<boolean, BooleanInput>;
491
- /** Показать иконку refresh в шапке. */
492
508
  /**
493
509
  * Вид мессенджера: без подписи автора, свой пузырь цвета действия, время без даты, над
494
510
  * сообщениями дня — его подпись, поле ответа в одну строку с рядом кнопок под ним.
@@ -522,6 +538,13 @@ export declare class RtChatComponent {
522
538
  */
523
539
  readonly messageHasActions: InputSignal<IRtChat.MessageActionsPredicate | null>;
524
540
  constructor();
541
+ /**
542
+ * Заменяет черновик в поле ответа и ставит курсор в конец: так приложение подставляет готовый
543
+ * текст. Выбранные файлы остаются, сигнала набора нет, закрытое поле не меняется.
544
+ */
545
+ setDraft(text: string): void;
546
+ /** Текст поля ответа в виде, который сейчас на экране. Файлы сюда не входят, поля нет — пустая строка. */
547
+ draftText(): string;
525
548
  protected onSubmit(): void;
526
549
  protected onComposerKeydown(event: KeyboardEvent): void;
527
550
  /** Релей submit'а от `rt-message-composer` (rich-режим) наружу как `send`. */
@@ -665,7 +688,7 @@ export declare class RtChatComponent {
665
688
  "expandedChange": "expandedChange";
666
689
  "deleteMessage": "deleteMessage";
667
690
  "retryMessage": "retryMessage";
668
- }, ["messageActions", "messageButtons"], ["[rtChatHeaderActions]", "[rtChatComposerTools]", "[rtChatFooter]"], true, never>;
691
+ }, ["messageActions", "messageButtons"], ["[rtChatHeaderActions]", "[rtChatComposerBar]", "[rtChatComposerTools]", "[rtChatFooter]"], true, never>;
669
692
  }
670
693
  /**
671
694
  * Есть ли у реплики хотя бы одно доступное действие потребителя.