Skip to content

Commit dd1dc05

Browse files
marker-daomarker dao ®
andauthored
Chat: Integrate Informer into ChatTextArea (#31488)
Co-authored-by: marker dao ® <[email protected]>
1 parent 1b6249d commit dd1dc05

File tree

42 files changed

+497
-193
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+497
-193
lines changed

packages/devextreme-scss/scss/widgets/base/chat/layout/chat-messagebox/_index.scss

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
align-items: flex-end;
1111
}
1212

13-
.dx-chat-messagebox-textarea.dx-textarea {
13+
.dx-chat-textarea.dx-textarea {
1414
display: flex;
1515
flex-direction: column;
1616
flex-grow: 1;
@@ -25,7 +25,7 @@
2525
}
2626
}
2727

28-
.dx-textarea-attachments {
28+
.dx-chat-textarea-attachments {
2929
.dx-fileuploader-input-wrapper {
3030
display: none;
3131
}
@@ -35,7 +35,7 @@
3535
}
3636
}
3737

38-
.dx-textarea-toolbar.dx-toolbar {
38+
.dx-chat-textarea-toolbar.dx-toolbar {
3939
.dx-toolbar-items-container {
4040
height: auto;
4141
}

packages/devextreme-scss/scss/widgets/base/chat/layout/chat-messagebox/_mixins.scss

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
gap: $gap;
1717
}
1818

19-
.dx-chat-messagebox-textarea.dx-textarea {
19+
.dx-chat-textarea.dx-textarea {
2020
gap: $gap;
2121
padding: $textarea-padding;
2222

@@ -31,7 +31,7 @@
3131
}
3232
}
3333

34-
.dx-textarea-toolbar.dx-toolbar {
34+
.dx-chat-textarea-toolbar.dx-toolbar {
3535
.dx-toolbar-items-container {
3636
min-height: $toolbar-min-height;
3737
}

packages/devextreme/js/__internal/ui/chat/message_box/chat_text_area.ts

Lines changed: 114 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,23 @@ import type { SupportedKeys } from '@ts/core/widget/widget';
2121
import Widget from '@ts/core/widget/widget';
2222
import FileUploader from '@ts/ui/file_uploader/file_uploader';
2323
import type { CancelButtonClickEvent, Properties as FileUploaderProperties } from '@ts/ui/file_uploader/file_uploader.types';
24+
import Informer from '@ts/ui/informer/informer';
2425
import type { TextAreaProperties } from '@ts/ui/m_text_area';
2526
import TextArea from '@ts/ui/m_text_area';
2627

27-
export const TEXT_AREA_TOOLBAR = 'dx-textarea-toolbar';
28-
const TEXT_AREA_ATTACHMENTS = 'dx-textarea-attachments';
29-
const TEXT_AREA_ATTACH_BUTTON = 'dx-textarea-attach-button';
28+
const CHAT_TEXT_AREA_ATTACHMENTS = 'dx-chat-textarea-attachments';
29+
export const CHAT_TEXT_AREA_ATTACH_BUTTON = 'dx-chat-textarea-attach-button';
30+
31+
export const CHAT_TEXTAREA_CLASS = 'dx-chat-textarea';
32+
export const CHAT_TEXT_AREA_TOOLBAR = 'dx-chat-textarea-toolbar';
33+
34+
const MAX_ATTACHMENTS_COUNT = 10;
35+
const INFORMER_DELAY = 10000;
36+
37+
const ERRORS = {
38+
// @ts-expect-error format params should be extended
39+
fileLimit: messageLocalization.format('dxChat-fileLimitReachedWarning', MAX_ATTACHMENTS_COUNT),
40+
};
3041

3142
const isMobile = (): boolean => devices.current().deviceType !== 'desktop';
3243

@@ -45,6 +56,11 @@ export type Properties = TextAreaProperties & {
4556
};
4657

4758
class ChatTextArea extends TextArea<Properties> {
59+
// eslint-disable-next-line no-restricted-globals
60+
_informerTimeoutId?: ReturnType<typeof setTimeout> | undefined;
61+
62+
_informer?: Informer | null;
63+
4864
_$toolbar?: dxElementWrapper | null;
4965

5066
_toolbar?: Toolbar | null;
@@ -57,7 +73,7 @@ class ChatTextArea extends TextArea<Properties> {
5773

5874
_sendButton?: Button;
5975

60-
_sendAction?: (e: SendEvent) => void;
76+
_sendAction?: (e: Partial<SendEvent>) => void;
6177

6278
getAttachments(): Attachment[] | undefined {
6379
if (!this._filesToSend?.size) {
@@ -75,8 +91,8 @@ class ChatTextArea extends TextArea<Properties> {
7591
stylingMode: 'outlined',
7692
placeholder: messageLocalization.format('dxChat-textareaPlaceholder'),
7793
autoResizeEnabled: true,
78-
maxHeight: '8em',
7994
valueChangeEvent: 'input',
95+
maxHeight: '16em',
8096
fileUploaderOptions: undefined,
8197
};
8298
}
@@ -114,11 +130,7 @@ class ChatTextArea extends TextArea<Properties> {
114130
}
115131

116132
if (this._shouldSendMessageOnEnter(e)) {
117-
this._processSendButtonActivation({
118-
component: this,
119-
element: this.element(),
120-
event: e,
121-
});
133+
this._processSendButtonActivation({ event: e });
122134
}
123135
}
124136

@@ -136,11 +148,46 @@ class ChatTextArea extends TextArea<Properties> {
136148
}
137149

138150
_initMarkup(): void {
151+
this.$element().addClass(CHAT_TEXTAREA_CLASS);
139152
super._initMarkup();
140153
this._renderToolbar();
141154
this._initFileUploader();
142155
}
143156

157+
_showInformer(text: string): void {
158+
if (this._informer) {
159+
this._informer.option({ text });
160+
} else {
161+
this._renderInformer(text);
162+
}
163+
164+
this._updateInformerTimeout();
165+
}
166+
167+
_renderInformer(text: string): void {
168+
const $informer = $('<div>').prependTo(this.$element());
169+
170+
this._informer = this._createComponent(
171+
$informer,
172+
Informer,
173+
{
174+
text,
175+
contentAlignment: 'start',
176+
icon: 'errorcircle',
177+
},
178+
);
179+
}
180+
181+
_updateInformerTimeout(): void {
182+
clearTimeout(this._informerTimeoutId);
183+
184+
// eslint-disable-next-line no-restricted-globals
185+
this._informerTimeoutId = setTimeout(() => {
186+
this._cleanInformer();
187+
this._updateInputHeight();
188+
}, INFORMER_DELAY);
189+
}
190+
144191
_renderToolbar(): void {
145192
const toolbarItems = this._getToolbarItems();
146193

@@ -149,7 +196,7 @@ class ChatTextArea extends TextArea<Properties> {
149196
};
150197

151198
this._$toolbar = $('<div>')
152-
.addClass(TEXT_AREA_TOOLBAR)
199+
.addClass(CHAT_TEXT_AREA_TOOLBAR)
153200
.appendTo(this.$element());
154201

155202
this._toolbar = this._createComponent(
@@ -167,13 +214,13 @@ class ChatTextArea extends TextArea<Properties> {
167214
];
168215

169216
if (fileUploaderOptions) {
170-
items.push(this._getFileUploaderButtonConfig());
217+
items.push(this._getAttachButtonConfig());
171218
}
172219

173220
return items;
174221
}
175222

176-
_getFileUploaderButtonConfig(): ToolbarItem {
223+
_getAttachButtonConfig(): ToolbarItem {
177224
const {
178225
activeStateEnabled,
179226
focusStateEnabled,
@@ -187,8 +234,12 @@ class ChatTextArea extends TextArea<Properties> {
187234
activeStateEnabled,
188235
focusStateEnabled,
189236
hoverStateEnabled,
190-
elementAttr: { class: TEXT_AREA_ATTACH_BUTTON },
237+
elementAttr: { class: CHAT_TEXT_AREA_ATTACH_BUTTON },
191238
icon: 'attach',
239+
onClick: (): void => {
240+
this._cleanInformer();
241+
this._updateInputHeight();
242+
},
192243
},
193244
} as ToolbarItem;
194245

@@ -241,7 +292,7 @@ class ChatTextArea extends TextArea<Properties> {
241292

242293
_renderFileUploader(): void {
243294
this._$fileUploader = $('<div>')
244-
.addClass(TEXT_AREA_ATTACHMENTS)
295+
.addClass(CHAT_TEXT_AREA_ATTACHMENTS)
245296
.insertBefore(this._$textEditorContainer);
246297

247298
this._fileUploader = this._createComponent(
@@ -257,6 +308,7 @@ class ChatTextArea extends TextArea<Properties> {
257308

258309
_getFileUploaderOptions(): FileUploaderProperties {
259310
const { fileUploaderOptions = {} } = this.option();
311+
260312
const multiple = fileUploaderOptions.multiple ?? true;
261313
const visible = this._shouldHideFileUploader(fileUploaderOptions.value);
262314

@@ -265,14 +317,16 @@ class ChatTextArea extends TextArea<Properties> {
265317
multiple,
266318
visible,
267319
uploadMode: 'instantly',
268-
dialogTrigger: this.$element().find(`.${TEXT_AREA_ATTACH_BUTTON}`).get(0),
320+
dialogTrigger: this.$element().find(`.${CHAT_TEXT_AREA_ATTACH_BUTTON}`).get(0),
269321
_hideCancelButtonOnUpload: false,
270322
_showFileIcon: true,
271323
_cancelButtonPosition: 'end',
324+
_maxFileCount: MAX_ATTACHMENTS_COUNT,
272325
onValueChanged: (e) => this._fileUploaderOnValueChanged(e),
273326
onUploadStarted: (e) => this._fileUploaderOnUploadStarted(e),
274327
onUploaded: (e) => this._fileUploaderOnUploaded(e),
275328
onCancelButtonClick: (e) => this._fileUploaderOnCancelButtonClick(e),
329+
onFileLimitReached: () => this._fileUploaderFileLimitReached(),
276330
};
277331
}
278332

@@ -326,6 +380,11 @@ class ChatTextArea extends TextArea<Properties> {
326380
this._toggleButtonDisableState();
327381
};
328382

383+
_fileUploaderFileLimitReached(): void {
384+
this._showInformer(ERRORS.fileLimit);
385+
this._updateInputHeight();
386+
}
387+
329388
_toggleButtonDisableState(state?: boolean): void {
330389
const shouldDisable = state ?? !this._isMessageCanBeSent();
331390
this._sendButton?.option('disabled', shouldDisable);
@@ -334,12 +393,28 @@ class ChatTextArea extends TextArea<Properties> {
334393
_renderButtonContainers(): void {}
335394

336395
_getHeightDifference($input: dxElementWrapper): number {
337-
const superResult = super._getHeightDifference($input);
338-
const toolbarHeight = getOuterHeight(this._$toolbar);
396+
const baseDifference = super._getHeightDifference($input);
397+
398+
const gap = parseFloat(this.$element().css('gap') ?? '0');
399+
400+
const informerHeight = this._informer ? getOuterHeight(this._informer.$element()) : 0;
339401
const fileUploaderHeight = getOuterHeight(this._$fileUploader);
340-
const sum: number = superResult + toolbarHeight + fileUploaderHeight;
402+
const toolbarHeight = getOuterHeight(this._$toolbar);
403+
404+
const visibleSections = [
405+
toolbarHeight,
406+
informerHeight,
407+
fileUploaderHeight,
408+
].filter(Boolean).length;
341409

342-
return sum;
410+
const totalExtraHeight = toolbarHeight
411+
+ informerHeight
412+
+ fileUploaderHeight
413+
+ visibleSections * gap;
414+
415+
const difference: number = baseDifference + totalExtraHeight;
416+
417+
return difference;
343418
}
344419

345420
_keyPressHandler(e: InputEvent): void {
@@ -348,7 +423,7 @@ class ChatTextArea extends TextArea<Properties> {
348423
this._toggleButtonDisableState();
349424
}
350425

351-
_processSendButtonActivation(e: SendEvent): void {
426+
_processSendButtonActivation(e: Partial<SendEvent>): void {
352427
this._sendAction?.(e);
353428
this.reset();
354429
this._fileUploader?.reset();
@@ -437,6 +512,23 @@ class ChatTextArea extends TextArea<Properties> {
437512
this._$fileUploader = null;
438513
}
439514

515+
_cleanInformer(): void {
516+
this._clearInformerTimeout();
517+
this._removeInformer();
518+
}
519+
520+
_removeInformer(): void {
521+
this._informer?.dispose();
522+
this._informer?.$element().remove();
523+
this._informer = null;
524+
}
525+
526+
_clearInformerTimeout(): void {
527+
clearTimeout(this._informerTimeoutId);
528+
529+
this._informerTimeoutId = undefined;
530+
}
531+
440532
_cleanToolbar(): void {
441533
this._toolbar?.dispose();
442534
this._$toolbar?.remove();
@@ -447,6 +539,7 @@ class ChatTextArea extends TextArea<Properties> {
447539
_dispose(): void {
448540
this._cleanFileUploader();
449541
this._cleanToolbar();
542+
this._cleanInformer();
450543
super._dispose();
451544
}
452545
}

packages/devextreme/js/__internal/ui/chat/message_box/message_box.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import EditingPreview from '@ts/ui/chat/message_box/editing_preview';
1616

1717
export const CHAT_MESSAGEBOX_CLASS = 'dx-chat-messagebox';
1818
export const CHAT_MESSAGEBOX_TEXTAREA_CONTAINER_CLASS = 'dx-chat-messagebox-textarea-container';
19-
export const CHAT_MESSAGEBOX_TEXTAREA_CLASS = 'dx-chat-messagebox-textarea';
2019

2120
export const TYPING_END_DELAY = 2000;
2221
const ESCAPE_KEY = 'escape';
@@ -137,7 +136,7 @@ class MessageBox extends DOMComponent<MessageBox, Properties> {
137136
}
138137

139138
_renderTextArea($parent: dxElementWrapper): void {
140-
const $textArea = $('<div>').addClass(CHAT_MESSAGEBOX_TEXTAREA_CLASS);
139+
const $textArea = $('<div>');
141140
const textAreaOptions = this._getTextAreaOptions();
142141

143142
$parent.append($textArea);

packages/devextreme/js/__internal/ui/file_uploader/file_uploader.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import {
4141

4242
const window = getWindow();
4343

44-
const FILEUPLOADER_CLASS = 'dx-fileuploader';
44+
export const FILEUPLOADER_CLASS = 'dx-fileuploader';
4545
const FILEUPLOADER_EMPTY_CLASS = 'dx-fileuploader-empty';
4646
const FILEUPLOADER_SHOW_FILE_LIST_CLASS = 'dx-fileuploader-show-file-list';
4747
const FILEUPLOADER_DRAGOVER_CLASS = 'dx-fileuploader-dragover';
@@ -65,7 +65,7 @@ const FILEUPLOADER_FILE_ICON_CLASS = 'dx-fileuploader-file-icon';
6565

6666
const FILEUPLOADER_BUTTON_CLASS = 'dx-fileuploader-button';
6767
const FILEUPLOADER_BUTTON_CONTAINER_CLASS = 'dx-fileuploader-button-container';
68-
const FILEUPLOADER_CANCEL_BUTTON_CLASS = 'dx-fileuploader-cancel-button';
68+
export const FILEUPLOADER_CANCEL_BUTTON_CLASS = 'dx-fileuploader-cancel-button';
6969
const FILEUPLOADER_UPLOAD_BUTTON_CLASS = 'dx-fileuploader-upload-button';
7070

7171
const FILEUPLOADER_INVALID_CLASS = 'dx-fileuploader-invalid';

packages/devextreme/js/__internal/ui/informer/informer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const INFORMER_ALIGNMENT_START_CLASS = 'dx-informer-alignment-start';
1313
const INFORMER_ALIGNMENT_CENTER_CLASS = 'dx-informer-alignment-center';
1414
const INFORMER_ALIGNMENT_END_CLASS = 'dx-informer-alignment-end';
1515
const INFORMER_BG_CLASS = 'dx-informer-bg';
16-
const INFORMER_TEXT_CLASS = 'dx-informer-text';
16+
export const INFORMER_TEXT_CLASS = 'dx-informer-text';
1717
const INFORMER_ICON_CLASS = 'dx-informer-icon';
1818

1919
export interface Properties extends WidgetOptions<Informer> {

packages/devextreme/js/__internal/ui/toolbar/toolbar.base.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ import type { CollectionItemKey, CollectionWidgetBaseProperties } from '@ts/ui/c
2222

2323
import { TOOLBAR_CLASS } from './constants';
2424

25-
const TOOLBAR_BEFORE_CLASS = 'dx-toolbar-before';
25+
export const TOOLBAR_BEFORE_CLASS = 'dx-toolbar-before';
2626
const TOOLBAR_CENTER_CLASS = 'dx-toolbar-center';
27-
const TOOLBAR_AFTER_CLASS = 'dx-toolbar-after';
27+
export const TOOLBAR_AFTER_CLASS = 'dx-toolbar-after';
2828
const TOOLBAR_MINI_CLASS = 'dx-toolbar-mini';
2929
const TOOLBAR_ITEM_CLASS = 'dx-toolbar-item';
3030
const TOOLBAR_LABEL_CLASS = 'dx-toolbar-label';

packages/devextreme/js/localization/messages/ar.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,7 @@
415415
"dxChat-defaultImageAlt": "Image shared in chat",
416416
"dxChat-fileViewLabel": "File list",
417417
"dxChat-downloadButtonLabel": "Download file {0}",
418+
"dxChat-fileLimitReachedWarning": "You selected too many files. Select no more than {0} files and retry.",
418419

419420
"dxColorView-ariaRed": "أحمر",
420421
"dxColorView-ariaGreen": "أخضر",

0 commit comments

Comments
 (0)