@rt-tools/ui-kit-v2 0.13.1 → 0.15.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 -5
- package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +90 -12
- package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2.mjs +2267 -664
- package/fesm2022/rt-tools-ui-kit-v2.mjs.map +1 -1
- package/package.json +1 -1
- package/rt-tools-ui-kit-v2-0.15.0.tgz +0 -0
- package/src/lib/components/CONTEXT.md +1 -1
- package/src/lib/components/action-bar/rt-action-bar.component.scss +5 -0
- package/src/lib/components/autocomplete/rt-autocomplete.component.scss +8 -1
- package/src/lib/components/bottom-sheet/rt-bottom-sheet.component.scss +5 -0
- package/src/lib/components/button/CONTEXT.md +1 -1
- package/src/lib/components/button/rt-button.directive.scss +5 -7
- package/src/lib/components/calendar/CONTEXT.md +16 -2
- package/src/lib/components/calendar/rt-calendar.component.scss +81 -4
- package/src/lib/components/card/rt-card.component.scss +5 -0
- package/src/lib/components/checkbox/rt-checkbox.component.scss +5 -0
- package/src/lib/components/data-list/settings/rt-data-list-settings-aside.component.scss +1 -1
- package/src/lib/components/data-list/toolbar/rt-data-list-toolbar.component.scss +1 -1
- package/src/lib/components/data-table/cell/rt-data-table-cell.component.scss +1 -1
- package/src/lib/components/data-table/filter-cell/rt-data-table-filter-cell.component.scss +1 -1
- package/src/lib/components/data-table/rt-data-table.component.scss +1 -1
- package/src/lib/components/date-picker/CONTEXT.md +33 -14
- package/src/lib/components/date-picker/panel/rt-date-panel.component.scss +155 -0
- package/src/lib/components/date-picker/rt-date-picker.component.scss +18 -2
- package/src/lib/components/date-range/CONTEXT.md +53 -0
- package/src/lib/components/date-range/panel/rt-date-range-panel.component.scss +103 -0
- package/src/lib/components/date-range/rt-date-range.component.scss +120 -0
- package/src/lib/components/dialog/rt-dialog.component.scss +5 -0
- package/src/lib/components/empty-state/rt-empty-state.component.scss +5 -0
- package/src/lib/components/file-card/rt-file-card.component.scss +5 -0
- package/src/lib/components/file-drop/rt-file-drop.component.scss +5 -0
- package/src/lib/components/header/rt-header.component.scss +5 -0
- package/src/lib/components/icon-button/CONTEXT.md +1 -1
- package/src/lib/components/icon-button/rt-icon-button.component.scss +9 -29
- package/src/lib/components/input/rt-input.component.scss +7 -1
- package/src/lib/components/input-number/rt-input-number.component.scss +7 -1
- package/src/lib/components/markdown-text/rt-markdown-text.component.scss +5 -0
- package/src/lib/components/menu/rt-menu-item.component.scss +5 -0
- package/src/lib/components/message/rt-message.component.scss +5 -0
- package/src/lib/components/money-list/rt-money-list.component.scss +5 -0
- package/src/lib/components/multiselect/CONTEXT.md +12 -5
- package/src/lib/components/multiselect/rt-multiselect.component.scss +32 -1
- package/src/lib/components/note/rt-note.component.scss +5 -0
- package/src/lib/components/notifications-bell/rt-notifications-bell.component.scss +5 -0
- package/src/lib/components/pagination/rt-pagination.component.scss +6 -1
- package/src/lib/components/radio-button/rt-radio-button.component.scss +5 -0
- package/src/lib/components/section-nav/rt-section-nav.component.scss +5 -0
- package/src/lib/components/select/CONTEXT.md +10 -7
- package/src/lib/components/select/rt-select.component.scss +36 -1
- package/src/lib/components/skeleton/CONTEXT.md +14 -16
- package/src/lib/components/skeleton/rt-skeleton.component.scss +7 -1
- package/src/lib/components/skeleton-wrapper/CONTEXT.md +6 -6
- package/src/lib/components/split-button/rt-split-button.component.scss +6 -0
- package/src/lib/components/stepper/rt-stepper.component.scss +5 -0
- package/src/lib/components/table/rt-table.component.scss +12 -0
- package/src/lib/components/tag/CONTEXT.md +14 -15
- package/src/lib/components/tag/rt-tag.component.scss +6 -30
- package/src/lib/components/textarea/rt-textarea.component.scss +8 -1
- package/src/lib/components/thread-list/rt-thread-list.component.scss +5 -0
- package/src/lib/components/toggle-button-group/rt-toggle-button-group.component.scss +7 -6
- package/src/lib/components/toggle-switch/rt-toggle-switch.component.scss +5 -0
- package/src/rich-editor/lib/components/message-composer/CONTEXT.md +26 -2
- package/src/rich-editor/lib/components/message-composer/rt-message-composer.component.scss +78 -34
- package/src/styles/_mixins.scss +39 -0
- package/styles/tokens.css +30 -5
- package/types/rt-tools-ui-kit-v2-rich-editor.d.ts +32 -6
- package/types/rt-tools-ui-kit-v2.d.ts +396 -109
- package/rt-tools-ui-kit-v2-0.13.1.tgz +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
@use '../../../styles/mixins' as kv;
|
|
2
|
+
|
|
1
3
|
@layer rt-kit.components {
|
|
2
4
|
/* rt-toggle-switch — переключатель в common/ui. ViewEncapsulation.None.
|
|
3
5
|
Хост адресуется по имени элемента, а не по классу блока: класс `.rt-toggle-switch` висит
|
|
@@ -6,6 +8,9 @@
|
|
|
6
8
|
|
|
7
9
|
rt-toggle-switch {
|
|
8
10
|
display: inline-flex;
|
|
11
|
+
|
|
12
|
+
/* Шаг входа `radius` переназначает скругление дорожки, ручка идёт за ней. */
|
|
13
|
+
@include kv.radius-steps('--rt-toggle-radius', '> .rt-toggle-switch');
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
rt-toggle-switch .rt-toggle-switch {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# `rt-message-composer`
|
|
2
2
|
|
|
3
|
-
Поле отправки
|
|
3
|
+
Поле отправки сообщения — капсула с круглыми кнопками: текст, вложения внутри капсулы,
|
|
4
|
+
необязательное форматирование и подсказка про Enter.
|
|
4
5
|
|
|
5
6
|
```html
|
|
6
7
|
<rt-message-composer
|
|
@@ -17,12 +18,15 @@
|
|
|
17
18
|
| `attachments` | `boolean` | `false` |
|
|
18
19
|
| `accept` | `string` | `''` |
|
|
19
20
|
| `sending` / `disabled` | `boolean` | `false` |
|
|
21
|
+
| `stoppable` | `boolean` | `false` |
|
|
22
|
+
| `text` (model) | `string` | `''` |
|
|
20
23
|
| `formatting` | `boolean` | `false` |
|
|
24
|
+
| `hint` | `boolean` | `false` |
|
|
21
25
|
| `toolbar` | `TRtRichEditorToolbar` | `'full'` |
|
|
22
26
|
| `minRows` / `maxRows` | `number` | 1 / 6 |
|
|
23
27
|
| `droppedFiles` | `File[] \| null` | `null` |
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
Выходы: `submitted` — `{ text, delta?, files }`; `stopped` — клик по «Стоп». Метод: `focus()`.
|
|
26
30
|
|
|
27
31
|
## Главное, что нужно знать
|
|
28
32
|
|
|
@@ -30,12 +34,32 @@
|
|
|
30
34
|
вложенный файл разблокирует отправку и без текста. Во время `sending` поле и кнопка заблокированы —
|
|
31
35
|
иначе второе сообщение ушло бы поверх ещё не доставленного.
|
|
32
36
|
|
|
37
|
+
**`stoppable` меняет смысл `sending` только для того, кто его включил.** При обоих флагах стрелку
|
|
38
|
+
сменяет «Стоп», форма не блокируется, а `submit` отказывает по `canSend`, не трогая черновик:
|
|
39
|
+
следующий вопрос набирается, пока идёт ответ. `rt-chat` и прочие потребители без флага получают
|
|
40
|
+
прежнюю блокировку.
|
|
41
|
+
|
|
42
|
+
**`text` — модель черновика, а не CVA.** У композера своя форма с файлами; потребителю нужно задать,
|
|
43
|
+
прочитать и очистить текст, а не встроить поле в чужую форму. Синхронизация двусторонняя: эффект
|
|
44
|
+
пишет модель в контрол, `valueChanges` контрола — в модель; сброс после отправки обнуляет обе.
|
|
45
|
+
|
|
46
|
+
**`focus()` в режиме форматирования идёт через `RtRichEditorComponent.focus`.** Узел Quill появляется
|
|
47
|
+
после динамического импорта; до него вызов ничего не делает.
|
|
48
|
+
|
|
33
49
|
**Enter отправляет, Shift+Enter переносит строку.** Набор во время IME-композиции игнорируется.
|
|
34
50
|
|
|
35
51
|
**`droppedFiles` — вход, а не выход.** Файлы, брошенные в область
|
|
36
52
|
[`rt-file-drop`](../../../../lib/components/file-drop/CONTEXT.md) снаружи, передаются сюда входом и добавляются к
|
|
37
53
|
вложениям; без `attachments` они игнорируются.
|
|
38
54
|
|
|
55
|
+
**Капсула высокая, когда в ней больше одной строки.** Признак — высота текста, которую меряет
|
|
56
|
+
`ResizeObserver`, вложенный файл или режим форматирования; модификатор `tall` переназначает
|
|
57
|
+
скругление на шаг 20.
|
|
58
|
+
|
|
59
|
+
**Поле держит `content-box`, внешний отступ и место под ползунок.** Автовысота CDK пишет в `height`
|
|
60
|
+
высоту текста и на замере обнуляет боковой `padding`, а появившийся ползунок сужал бы поле: без
|
|
61
|
+
этих трёх правил текст ложился бы на строку больше, чем поле вмещает.
|
|
62
|
+
|
|
39
63
|
## Края
|
|
40
64
|
|
|
41
65
|
- После отправки форма сбрасывается целиком — и текст, и файлы.
|
|
@@ -1,74 +1,118 @@
|
|
|
1
1
|
@layer rt-kit.components {
|
|
2
2
|
.rt-message-composer {
|
|
3
|
-
--rt-message-composer-radius: var(--rt-radius-
|
|
3
|
+
--rt-message-composer-radius: var(--rt-radius-full);
|
|
4
|
+
--rt-message-composer-tall-radius: var(--rt-radius-2xl);
|
|
4
5
|
--rt-message-composer-border-width: var(--rt-border-width-thin);
|
|
5
|
-
--rt-message-composer-divider-width: var(--rt-border-width-thin);
|
|
6
6
|
|
|
7
7
|
display: flex;
|
|
8
8
|
flex-direction: column;
|
|
9
|
-
|
|
10
|
-
border: var(--rt-message-composer-border-width) solid var(--rt-color-border-default);
|
|
11
|
-
border-radius: var(--rt-message-composer-radius);
|
|
12
|
-
background: var(--rt-color-bg-surface);
|
|
13
|
-
gap: var(--rt-space-2);
|
|
9
|
+
gap: calc(var(--rt-space-1) * 1.5);
|
|
14
10
|
|
|
15
11
|
/* Ширина приходит от хозяина, а не от содержимого. Без этой строки её задавала textarea
|
|
16
12
|
без объявленной ширины: такую браузер считает от средней ширины знака первого
|
|
17
13
|
доступного начертания, и величина гуляла между поднятиями витрины — 158 против 161. */
|
|
18
14
|
inline-size: 100%;
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
&__capsule {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
padding: calc(var(--rt-space-1) * 1.5);
|
|
20
|
+
border: var(--rt-message-composer-border-width) solid var(--rt-input-color-border);
|
|
21
|
+
border-radius: var(--rt-message-composer-radius);
|
|
22
|
+
background: var(--rt-input-color-bg);
|
|
23
|
+
gap: calc(var(--rt-space-1) * 1.5);
|
|
24
|
+
|
|
25
|
+
// Фокус — как у полей кита: поверхность, рамка фокуса и кольцо.
|
|
26
|
+
&:focus-within {
|
|
27
|
+
border-color: var(--rt-input-color-border-focus);
|
|
28
|
+
background: var(--rt-color-bg-surface);
|
|
29
|
+
box-shadow: var(--rt-input-focus-shadow);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Выше одной строки капсула берёт шаг скругления 20: полное скругление
|
|
33
|
+
// на высокой капсуле срезало бы углы текста и карточек.
|
|
34
|
+
&--tall {
|
|
35
|
+
--rt-message-composer-radius: var(--rt-message-composer-tall-radius);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&--disabled {
|
|
39
|
+
background: var(--rt-input-color-bg-disabled);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
&__files {
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
gap: var(--rt-space-1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&__rich {
|
|
50
|
+
inline-size: 100%;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Кнопки прижаты к низу: растущий текст поднимает капсулу, а не их.
|
|
54
|
+
&__row {
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: flex-end;
|
|
57
|
+
gap: var(--rt-space-2);
|
|
22
58
|
}
|
|
23
59
|
|
|
24
60
|
&__input {
|
|
25
|
-
|
|
61
|
+
/* Автовысота CDK пишет в height высоту самого текста: при border-box её съели бы
|
|
62
|
+
отступы, и уже вторая строка уезжала бы под верхний край. */
|
|
63
|
+
box-sizing: content-box;
|
|
64
|
+
flex: 1 1 auto;
|
|
26
65
|
border: 0;
|
|
27
66
|
margin: 0;
|
|
28
67
|
background: transparent;
|
|
29
|
-
color:
|
|
68
|
+
color: var(--rt-input-color-text);
|
|
30
69
|
font: inherit;
|
|
31
|
-
font-size: var(--rt-
|
|
70
|
+
font-size: var(--rt-input-font-size);
|
|
32
71
|
inline-size: 100%;
|
|
33
|
-
line-height: var(--rt-leading-
|
|
72
|
+
line-height: var(--rt-leading-normal);
|
|
73
|
+
|
|
74
|
+
/* Отступ от скрепки — внешний: замер автовысоты CDK обнуляет боковой padding поля,
|
|
75
|
+
и на замере текст ложился бы в меньшее число строк, чем на экране. */
|
|
76
|
+
margin-inline-start: calc(var(--rt-space-1) * 1.5);
|
|
34
77
|
|
|
35
78
|
/* Своего размера поле ввода хозяину не отдаёт: иначе его ширина по `cols`
|
|
36
79
|
по-прежнему участвовала бы в измерении. */
|
|
37
80
|
min-inline-size: 0;
|
|
38
81
|
outline: 0;
|
|
82
|
+
|
|
83
|
+
/* Одна строка текста по высоте равна круглой кнопке: отступ добирает разницу. */
|
|
84
|
+
padding-block: calc((var(--rt-control-height-md) - 1lh) / 2);
|
|
39
85
|
resize: none;
|
|
40
86
|
|
|
87
|
+
/* Место под ползунок держится всегда, и на замере автовысоты тоже: иначе появившийся
|
|
88
|
+
ползунок сужал бы поле, текст ложился бы ещё на строку, и верхняя уезжала бы. */
|
|
89
|
+
scrollbar-color: var(--rt-color-border-default) transparent;
|
|
90
|
+
scrollbar-gutter: stable;
|
|
91
|
+
scrollbar-width: thin;
|
|
92
|
+
|
|
41
93
|
&::placeholder {
|
|
42
|
-
color: var(--rt-color-text-
|
|
94
|
+
color: var(--rt-input-color-text-placeholder);
|
|
43
95
|
}
|
|
44
96
|
|
|
45
97
|
&:disabled {
|
|
46
98
|
cursor: not-allowed;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
99
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Разделитель тянется на всю ширину бокса — компенсируем горизонтальный padding.
|
|
57
|
-
&__divider {
|
|
58
|
-
background: var(--rt-color-border-default);
|
|
59
|
-
block-size: var(--rt-message-composer-divider-width);
|
|
60
|
-
margin-inline: calc(-1 * var(--rt-space-4));
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
&__controls {
|
|
64
|
-
display: flex;
|
|
65
|
-
align-items: center;
|
|
66
|
-
gap: var(--rt-space-2);
|
|
100
|
+
&::placeholder {
|
|
101
|
+
color: var(--rt-color-text-disabled);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
67
104
|
}
|
|
68
105
|
|
|
69
|
-
// Кнопка отправки всегда у правого
|
|
106
|
+
// Кнопка отправки всегда у правого края: и без скрепки, и в режиме форматирования.
|
|
70
107
|
&__send {
|
|
71
108
|
margin-inline-start: auto;
|
|
72
109
|
}
|
|
110
|
+
|
|
111
|
+
&__hint {
|
|
112
|
+
margin: 0;
|
|
113
|
+
color: var(--rt-color-text-muted);
|
|
114
|
+
font-size: var(--rt-text-xs);
|
|
115
|
+
line-height: var(--rt-leading-snug);
|
|
116
|
+
}
|
|
73
117
|
}
|
|
74
118
|
}
|
package/src/styles/_mixins.scss
CHANGED
|
@@ -19,3 +19,42 @@
|
|
|
19
19
|
letter-spacing: var(--rt-letter-spacing-wide);
|
|
20
20
|
line-height: var(--rt-leading-snug);
|
|
21
21
|
}
|
|
22
|
+
|
|
23
|
+
/// Шаги скругления кита и их токены — те же шаги, что у типа `TRtRadius`. Токены названы
|
|
24
|
+
/// целиком, а не склеены из шага: проверка графа токенов ищет имя, а не его начало.
|
|
25
|
+
$radius-steps: (
|
|
26
|
+
none: var(--rt-radius-none),
|
|
27
|
+
xs: var(--rt-radius-xs),
|
|
28
|
+
sm: var(--rt-radius-sm),
|
|
29
|
+
ms: var(--rt-radius-ms),
|
|
30
|
+
md: var(--rt-radius-md),
|
|
31
|
+
lg: var(--rt-radius-lg),
|
|
32
|
+
xl: var(--rt-radius-xl),
|
|
33
|
+
2xl: var(--rt-radius-2xl),
|
|
34
|
+
full: var(--rt-radius-full),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/// Правила шагов входа `radius` для одного компонента.
|
|
38
|
+
///
|
|
39
|
+
/// Ставится внутрь правила хоста (`rt-card { … }`). На каждый шаг выходит правило
|
|
40
|
+
/// хоста с атрибутом `data-rt-radius`, которое переназначает собственное свойство
|
|
41
|
+
/// скругления компонента — и больше ничего: поверхность берёт углы из того же
|
|
42
|
+
/// свойства, и переопределение этого свойства приложением по-прежнему сильнее кита.
|
|
43
|
+
///
|
|
44
|
+
/// $property — собственное свойство скругления, `--rt-card-radius`.
|
|
45
|
+
/// $surface — где это свойство объявлено, от хоста: `'> .rt-card'` для корня шаблона,
|
|
46
|
+
/// пусто — если на самом хосте. Только прямой ребёнок: потомок задел бы
|
|
47
|
+
/// вложенный компонент того же рода.
|
|
48
|
+
@mixin radius-steps($property, $surface: null) {
|
|
49
|
+
@each $step, $token in $radius-steps {
|
|
50
|
+
@if $surface {
|
|
51
|
+
&[data-rt-radius='#{$step}'] #{$surface} {
|
|
52
|
+
#{$property}: $token;
|
|
53
|
+
}
|
|
54
|
+
} @else {
|
|
55
|
+
&[data-rt-radius='#{$step}'] {
|
|
56
|
+
#{$property}: $token;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
package/styles/tokens.css
CHANGED
|
@@ -6905,9 +6905,6 @@ html.rt-theme-dark.rt-preset-material {
|
|
|
6905
6905
|
.rt-button--text.rt-button--info {
|
|
6906
6906
|
--rt-btn-color: var(--rt-color-action-info-on-surface);
|
|
6907
6907
|
}
|
|
6908
|
-
.rt-button--rounded {
|
|
6909
|
-
--rt-btn-radius: 999px;
|
|
6910
|
-
}
|
|
6911
6908
|
.rt-button--sm {
|
|
6912
6909
|
--rt-btn-height: var(--rt-control-height-sm);
|
|
6913
6910
|
--rt-btn-padding: 0 0.875rem;
|
|
@@ -6941,8 +6938,36 @@ html.rt-theme-dark.rt-preset-material {
|
|
|
6941
6938
|
padding: 0;
|
|
6942
6939
|
gap: 0;
|
|
6943
6940
|
}
|
|
6944
|
-
.rt-button
|
|
6945
|
-
|
|
6941
|
+
.rt-button {
|
|
6942
|
+
/* Шаг входа `radius` переназначает собственное свойство кнопки. Класс блока стоит на
|
|
6943
|
+
самом хосте, поэтому правило шага — на нём же. */
|
|
6944
|
+
}
|
|
6945
|
+
.rt-button[data-rt-radius=none] {
|
|
6946
|
+
--rt-btn-radius: var(--rt-radius-none);
|
|
6947
|
+
}
|
|
6948
|
+
.rt-button[data-rt-radius=xs] {
|
|
6949
|
+
--rt-btn-radius: var(--rt-radius-xs);
|
|
6950
|
+
}
|
|
6951
|
+
.rt-button[data-rt-radius=sm] {
|
|
6952
|
+
--rt-btn-radius: var(--rt-radius-sm);
|
|
6953
|
+
}
|
|
6954
|
+
.rt-button[data-rt-radius=ms] {
|
|
6955
|
+
--rt-btn-radius: var(--rt-radius-ms);
|
|
6956
|
+
}
|
|
6957
|
+
.rt-button[data-rt-radius=md] {
|
|
6958
|
+
--rt-btn-radius: var(--rt-radius-md);
|
|
6959
|
+
}
|
|
6960
|
+
.rt-button[data-rt-radius=lg] {
|
|
6961
|
+
--rt-btn-radius: var(--rt-radius-lg);
|
|
6962
|
+
}
|
|
6963
|
+
.rt-button[data-rt-radius=xl] {
|
|
6964
|
+
--rt-btn-radius: var(--rt-radius-xl);
|
|
6965
|
+
}
|
|
6966
|
+
.rt-button[data-rt-radius="2xl"] {
|
|
6967
|
+
--rt-btn-radius: var(--rt-radius-2xl);
|
|
6968
|
+
}
|
|
6969
|
+
.rt-button[data-rt-radius=full] {
|
|
6970
|
+
--rt-btn-radius: var(--rt-radius-full);
|
|
6946
6971
|
}
|
|
6947
6972
|
.rt-button--loading {
|
|
6948
6973
|
cursor: wait;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { AfterViewInit, OnDestroy, Signal, ElementRef, WritableSignal, InputSignal, OutputEmitterRef, InputSignalWithTransform, TemplateRef, PipeTransform } from '@angular/core';
|
|
2
|
+
import { AfterViewInit, OnDestroy, Signal, ElementRef, WritableSignal, InputSignal, OutputEmitterRef, InputSignalWithTransform, ModelSignal, TemplateRef, PipeTransform } from '@angular/core';
|
|
3
3
|
import { RtFormControlBase, IQuillDelta, TRtKitLabelMap } from '@rt-tools/ui-kit-v2';
|
|
4
4
|
import { BooleanInput, NumberInput } from '@angular/cdk/coercion';
|
|
5
5
|
import { FormGroup, FormControl } from '@angular/forms';
|
|
@@ -32,6 +32,8 @@ declare class RtRichEditorComponent extends RtFormControlBase<IQuillDelta | null
|
|
|
32
32
|
readonly enterPressed: OutputEmitterRef<void>;
|
|
33
33
|
readonly displayText: Signal<string>;
|
|
34
34
|
ngAfterViewInit(): Promise<void>;
|
|
35
|
+
/** Ставит фокус в редактор; до загрузки Quill ничего не делает. */
|
|
36
|
+
focus(): void;
|
|
35
37
|
ngOnDestroy(): void;
|
|
36
38
|
writeValue(value: IQuillDelta | null): void;
|
|
37
39
|
setDisabledState(isDisabled: boolean): void;
|
|
@@ -64,30 +66,47 @@ interface IComposerFormShape {
|
|
|
64
66
|
delta: FormControl<IQuillDelta | null>;
|
|
65
67
|
}
|
|
66
68
|
/**
|
|
67
|
-
* Композер
|
|
68
|
-
*
|
|
69
|
-
* без data-access: наружу торчит только `submitted` с текстом и файлами.
|
|
69
|
+
* Композер сообщения — капсула: слева круглая кнопка вложения, по центру авто-растущая
|
|
70
|
+
* textarea, справа круглая кнопка отправки; вложения стоят внутри капсулы над строкой.
|
|
71
|
+
* Презентационный, без data-access: наружу торчит только `submitted` с текстом и файлами.
|
|
70
72
|
*
|
|
71
73
|
* Отправка — `Enter` (без Shift) или клик по иконке; `Shift+Enter` — перенос
|
|
72
74
|
* строки. Кнопка отправки активна, пока есть непустой текст или вложения.
|
|
75
|
+
*
|
|
76
|
+
* Со `stoppable` во время `sending` вместо стрелки стоит кнопка «Стоп», а поле
|
|
77
|
+
* остаётся открытым: следующий вопрос набирается, пока идёт ответ.
|
|
73
78
|
*/
|
|
74
79
|
declare class RtMessageComposerComponent {
|
|
75
80
|
#private;
|
|
76
81
|
protected readonly t: Signal<TRtKitLabelMap>;
|
|
77
82
|
protected readonly placeholderText: Signal<string>;
|
|
78
83
|
protected readonly fileInput: Signal<ElementRef<HTMLInputElement> | undefined>;
|
|
84
|
+
protected readonly textInput: Signal<ElementRef<HTMLTextAreaElement> | undefined>;
|
|
85
|
+
protected readonly richEditor: Signal<RtRichEditorComponent | undefined>;
|
|
79
86
|
protected readonly form: FormGroup<IComposerFormShape>;
|
|
80
87
|
protected readonly files: Signal<readonly File[]>;
|
|
81
88
|
/** Отправка доступна: есть контент (текст/delta/файлы), композер не занят. */
|
|
82
89
|
protected readonly canSend: Signal<boolean>;
|
|
90
|
+
/** Капсула выше одной строки: перенос текста, вложения или режим форматирования. */
|
|
91
|
+
protected readonly tall: Signal<boolean>;
|
|
92
|
+
/** Идёт ответ, который можно остановить: вместо стрелки — «Стоп», поле открыто. */
|
|
93
|
+
protected readonly isStoppable: Signal<boolean>;
|
|
94
|
+
protected readonly capsuleMods: Signal<Record<string, boolean>>;
|
|
83
95
|
/** Текст-подсказка textarea. Пусто — берётся переведённое умолчание. */
|
|
84
96
|
readonly placeholder: InputSignal<string>;
|
|
85
97
|
/** Фильтр типов для file-input (`.pdf,.doc,...`). */
|
|
86
98
|
readonly accept: InputSignal<string>;
|
|
99
|
+
/** Строка под капсулой про Enter и Shift + Enter. */
|
|
100
|
+
readonly hint: InputSignalWithTransform<boolean, BooleanInput>;
|
|
87
101
|
/** Показывать кнопку вложения и список файлов. */
|
|
88
102
|
readonly attachments: InputSignalWithTransform<boolean, BooleanInput>;
|
|
89
|
-
/** Отправка в процессе — блокирует submit
|
|
103
|
+
/** Отправка в процессе — блокирует submit, кнопка отправки крутит индикатор. */
|
|
90
104
|
readonly sending: InputSignalWithTransform<boolean, BooleanInput>;
|
|
105
|
+
/**
|
|
106
|
+
* Ответ на сообщение можно остановить. Во время `sending` стрелку сменяет кнопка
|
|
107
|
+
* «Стоп», а поле не блокируется; без входа `sending` блокирует поле, как раньше.
|
|
108
|
+
*/
|
|
109
|
+
readonly stoppable: InputSignalWithTransform<boolean, BooleanInput>;
|
|
91
110
|
/** Полная блокировка композера. */
|
|
92
111
|
readonly disabled: InputSignalWithTransform<boolean, BooleanInput>;
|
|
93
112
|
/** Rich-режим: вместо textarea — Quill-редактор, отправка уходит delta-каналом. */
|
|
@@ -106,14 +125,21 @@ declare class RtMessageComposerComponent {
|
|
|
106
125
|
readonly droppedFiles: InputSignal<File[] | null>;
|
|
107
126
|
/** Отправка сообщения: текст + файлы (последние — только при `attachments`). */
|
|
108
127
|
readonly submitted: OutputEmitterRef<IRtMessageComposer.SubmitPayload>;
|
|
128
|
+
/** Черновик поля без форматирования: задаётся снаружи, правки уходят наружу, после отправки пуст. */
|
|
129
|
+
readonly text: ModelSignal<string>;
|
|
130
|
+
/** Клик по кнопке «Стоп» во время ответа, который можно остановить. */
|
|
131
|
+
readonly stopped: OutputEmitterRef<void>;
|
|
109
132
|
constructor();
|
|
133
|
+
/** Ставит фокус в поле: в textarea, а в режиме форматирования — в rich-редактор. */
|
|
134
|
+
focus(): void;
|
|
110
135
|
protected onKeydown(event: KeyboardEvent): void;
|
|
136
|
+
protected stop(): void;
|
|
111
137
|
protected openPicker(): void;
|
|
112
138
|
protected onFilesPicked(event: Event): void;
|
|
113
139
|
protected removeFile(index: number): void;
|
|
114
140
|
protected submit(): void;
|
|
115
141
|
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>;
|
|
142
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<RtMessageComposerComponent, "rt-message-composer", never, { "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "accept": { "alias": "accept"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "attachments": { "alias": "attachments"; "required": false; "isSignal": true; }; "sending": { "alias": "sending"; "required": false; "isSignal": true; }; "stoppable": { "alias": "stoppable"; "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; }; "text": { "alias": "text"; "required": false; "isSignal": true; }; }, { "submitted": "submitted"; "text": "textChange"; "stopped": "stopped"; }, never, never, true, never>;
|
|
117
143
|
}
|
|
118
144
|
|
|
119
145
|
/**
|