From 7ed9e68779c0aa0a3635b1208a829ebd57d7409d Mon Sep 17 00:00:00 2001 From: Ruslan Farkhutdinov Date: Tue, 31 Mar 2026 15:40:47 +0300 Subject: [PATCH 1/2] Chat: WIP Add mainButton option --- packages/devextreme/js/ui/chat.d.ts | 42 +++++++++++++++++++++++ packages/devextreme/js/ui/chat_types.d.ts | 2 ++ packages/devextreme/ts/dx.all.d.ts | 33 ++++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/packages/devextreme/js/ui/chat.d.ts b/packages/devextreme/js/ui/chat.d.ts index 0ae1d65d657e..4eb2dcfa206e 100644 --- a/packages/devextreme/js/ui/chat.d.ts +++ b/packages/devextreme/js/ui/chat.d.ts @@ -173,6 +173,43 @@ export type AttachmentDownloadClickEvent = EventInfo & { */ export type InputFieldTextChangedEvent = NativeEventInfo & ValueChangedInfo; +/** + * @docid _ui_chat_MainButtonClickEvent + * @public + * @type object + * @inherits NativeEventInfo + */ +export type MainButtonClickEvent = NativeEventInfo; + +/** + * @docid + * @namespace DevExpress.ui.dxChat + * @public + */ +export type MainButton = { + /** + * @docid + * @public + */ + icon?: string; + /** + * @docid + * @public + */ + hint?: string; + /** + * @docid + * @public + */ + action: 'default' | 'custom'; + /** + * @docid + * @type_function_param1 e:{ui/chat:MainButtonClickEvent} + * @public + */ + onClick: ((e: MainButtonClickEvent) => void); +}; + /** * @docid * @namespace DevExpress.ui.dxChat @@ -446,6 +483,11 @@ export interface dxChatOptions extends WidgetOptions { * @public */ inputFieldText?: TextAreaProperties['value']; + /** + * @docid + * @public + */ + mainButton?: MainButton; /** * @docid * @default null diff --git a/packages/devextreme/js/ui/chat_types.d.ts b/packages/devextreme/js/ui/chat_types.d.ts index 23ad3fd6a0f4..f7ff41119be0 100644 --- a/packages/devextreme/js/ui/chat_types.d.ts +++ b/packages/devextreme/js/ui/chat_types.d.ts @@ -13,6 +13,8 @@ export { MessageUpdatedEvent, AttachmentDownloadClickEvent, InputFieldTextChangedEvent, + MainButtonClickEvent, + MainButton, User, Alert, Attachment, diff --git a/packages/devextreme/ts/dx.all.d.ts b/packages/devextreme/ts/dx.all.d.ts index e69e30db6bc2..68616d3a6cf8 100644 --- a/packages/devextreme/ts/dx.all.d.ts +++ b/packages/devextreme/ts/dx.all.d.ts @@ -11238,6 +11238,14 @@ declare module DevExpress.ui { DevExpress.events.InteractionEvent | Event > & DevExpress.ui.Editor.ValueChangedInfo; + /** + * [descr:_ui_chat_MainButtonClickEvent] + */ + export type MainButtonClickEvent = + DevExpress.common.core.events.NativeEventInfo< + dxChat, + DevExpress.events.InteractionEvent + >; /** * [descr:_ui_chat_MessageDeletedEvent] */ @@ -11442,6 +11450,10 @@ declare module DevExpress.ui { * [descr:dxChatOptions.inputFieldText] */ inputFieldText?: DevExpress.ui.dxTextArea.Properties['value']; + /** + * [descr:dxChatOptions.mainButton] + */ + mainButton?: DevExpress.ui.dxChat.MainButton; /** * [descr:dxChatOptions.messageTemplate] */ @@ -34134,6 +34146,27 @@ declare module DevExpress.ui.dxChat { */ alt?: string; }; + /** + * [descr:MainButton] + */ + export type MainButton = { + /** + * [descr:MainButton.icon] + */ + icon?: string; + /** + * [descr:MainButton.hint] + */ + hint?: string; + /** + * [descr:MainButton.action] + */ + action: 'default' | 'custom'; + /** + * [descr:MainButton.onClick] + */ + onClick: (e: MainButtonClickEvent) => void; + }; /** * [descr:Message] */ From e647f91b2379beebb6fe2072d107a77dca6e16a1 Mon Sep 17 00:00:00 2001 From: Ruslan Farkhutdinov Date: Wed, 1 Apr 2026 16:23:36 +0300 Subject: [PATCH 2/2] Chat: WIP Add mainButtonOptions option support --- .../devextreme/js/__internal/ui/chat/chat.ts | 5 +++ .../ui/chat/message_box/chat_text_area.ts | 36 +++++++++++++++++-- .../ui/chat/message_box/message_box.ts | 10 +++++- packages/devextreme/js/ui/chat.d.ts | 12 +++---- 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/chat/chat.ts b/packages/devextreme/js/__internal/ui/chat/chat.ts index 20290db49137..9850b567aab7 100644 --- a/packages/devextreme/js/__internal/ui/chat/chat.ts +++ b/packages/devextreme/js/__internal/ui/chat/chat.ts @@ -471,6 +471,7 @@ class Chat extends Widget { inputFieldText, speechToTextEnabled, speechToTextOptions, + mainButtonOptions, } = this.option(); const $messageBox = $('
'); @@ -485,6 +486,7 @@ class Chat extends Widget { text: inputFieldText, speechToTextEnabled, speechToTextOptions, + mainButtonOptions, onMessageEntered: (e) => { this._messageEnteredHandler(e); }, @@ -743,6 +745,9 @@ class Chat extends Widget { break; case 'reloadOnChange': break; + case 'mainButtonOptions': + this._messageBox.option(name, value); + break; default: super._optionChanged(args); } diff --git a/packages/devextreme/js/__internal/ui/chat/message_box/chat_text_area.ts b/packages/devextreme/js/__internal/ui/chat/message_box/chat_text_area.ts index 07a4c5b795a5..1f6661873872 100644 --- a/packages/devextreme/js/__internal/ui/chat/message_box/chat_text_area.ts +++ b/packages/devextreme/js/__internal/ui/chat/message_box/chat_text_area.ts @@ -14,7 +14,7 @@ import type { InitializedEvent, } from '@js/ui/button'; import type Button from '@js/ui/button'; -import type { Attachment } from '@js/ui/chat'; +import type { Attachment, MainButtonOptions } from '@js/ui/chat'; import type { UploadedEvent, UploadStartedEvent, @@ -60,6 +60,8 @@ export type Properties = TextAreaProperties & { speechToTextOptions?: SpeechToTextProperties; + mainButtonOptions?: MainButtonOptions; + onSend?: (e: SendEvent) => void; }; @@ -104,6 +106,12 @@ export const SEND_BUTTON_READY_TO_SEND_STATE: ButtonState = { disabled: false, }; +export const MAIN_BUTTON_CUSTOM_ACTIVE_STATE: ButtonState = { + stylingMode: 'contained', + type: 'default', + disabled: false, +}; + const isMobile = (): boolean => devices.current().deviceType !== 'desktop'; export const DEFAULT_ALLOWED_FILE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.pdf', '.docx', '.xlsx', '.pptx', '.txt', '.rtf', '.csv', '.md']; @@ -378,6 +386,7 @@ class ChatTextArea extends TextArea { activeStateEnabled, focusStateEnabled, hoverStateEnabled, + mainButtonOptions, } = this.option(); const configuration = { @@ -387,13 +396,15 @@ class ChatTextArea extends TextArea { activeStateEnabled, focusStateEnabled, hoverStateEnabled, - icon: 'arrowright', + icon: mainButtonOptions?.icon ?? 'arrowright', ...SEND_BUTTON_INITIAL_STATE, elementAttr: { 'aria-label': messageLocalization.format('dxChat-sendButtonAriaLabel'), }, onClick: (e: ClickEvent): void => { this._processSendButtonActivation(e); + // @ts-expect-error + mainButtonOptions?.onClick?.(e); }, onInitialized: (e: InitializedEvent): void => { this._sendButton = e.component; @@ -552,6 +563,11 @@ class ChatTextArea extends TextArea { _updateButtonsState(): void { const { speechToTextEnabled } = this.option(); + if (this._isCustomBehavior()) { + this._sendButton?.option(MAIN_BUTTON_CUSTOM_ACTIVE_STATE); + return; + } + if (this._isSpeechToTextListening === true && speechToTextEnabled) { this._speechToTextButton?.option(STT_LISTENING_STATE); this._sendButton?.option(SEND_BUTTON_INITIAL_STATE); @@ -594,7 +610,18 @@ class ChatTextArea extends TextArea { this._updateButtonsState(); } + _isCustomBehavior(): boolean { + const { mainButtonOptions } = this.option(); + + return mainButtonOptions?.behavior === 'custom'; + } + _processSendButtonActivation(e: Partial): void { + if (this._isCustomBehavior()) { + this._updateButtonsState(); + return; + } + this._sendAction?.(e); this.clear(); this.resetFileUploader(); @@ -640,6 +667,11 @@ class ChatTextArea extends TextArea { this._speechToTextButton?.option(this._getSpeechToTextButtonOptions()); break; + case 'mainButtonOptions': + this._toolbar?.option({ items: this._getToolbarItems() }); + this._updateButtonsState(); + break; + default: super._optionChanged(args); } diff --git a/packages/devextreme/js/__internal/ui/chat/message_box/message_box.ts b/packages/devextreme/js/__internal/ui/chat/message_box/message_box.ts index 244e9293714d..84a1e71497ee 100644 --- a/packages/devextreme/js/__internal/ui/chat/message_box/message_box.ts +++ b/packages/devextreme/js/__internal/ui/chat/message_box/message_box.ts @@ -1,7 +1,7 @@ import type { NativeEventInfo } from '@js/common/core/events'; import $, { type dxElementWrapper } from '@js/core/renderer'; import type { InteractionEvent } from '@js/events'; -import type { Attachment, InputFieldTextChangedEvent } from '@js/ui/chat'; +import type { Attachment, InputFieldTextChangedEvent, MainButtonOptions } from '@js/ui/chat'; import type { Properties as FileUploaderProperties } from '@js/ui/file_uploader'; import type { Properties as SpeechToTextProperties } from '@js/ui/speech_to_text'; import type { InputEvent } from '@js/ui/text_area'; @@ -40,6 +40,8 @@ export interface Properties extends DOMComponentProperties { text?: string; + mainButtonOptions?: MainButtonOptions; + onMessageEntered?: (e: MessageEnteredEvent) => void; onTypingStart?: (e: TypingStartEvent) => void; @@ -176,6 +178,7 @@ class MessageBox extends DOMComponent { speechToTextEnabled, speechToTextOptions, text, + mainButtonOptions, } = this.option(); const options = { @@ -187,6 +190,7 @@ class MessageBox extends DOMComponent { value: previewText || text, speechToTextEnabled, speechToTextOptions, + mainButtonOptions, onInput: (e: InputEvent): void => { this._triggerTypingStartAction(e); this._updateTypingEndTimeout(); @@ -321,6 +325,10 @@ class MessageBox extends DOMComponent { this._textArea.option('value', value); break; + case 'mainButtonOptions': + this._textArea.option(name, value); + break; + default: super._optionChanged(args); } diff --git a/packages/devextreme/js/ui/chat.d.ts b/packages/devextreme/js/ui/chat.d.ts index 4eb2dcfa206e..f42cb3cae6b5 100644 --- a/packages/devextreme/js/ui/chat.d.ts +++ b/packages/devextreme/js/ui/chat.d.ts @@ -186,7 +186,7 @@ export type MainButtonClickEvent = NativeEventInfo; * @namespace DevExpress.ui.dxChat * @public */ -export type MainButton = { +export type MainButtonOptions = { /** * @docid * @public @@ -195,13 +195,9 @@ export type MainButton = { /** * @docid * @public + * @default 'default' */ - hint?: string; - /** - * @docid - * @public - */ - action: 'default' | 'custom'; + behavior?: 'default' | 'custom'; /** * @docid * @type_function_param1 e:{ui/chat:MainButtonClickEvent} @@ -487,7 +483,7 @@ export interface dxChatOptions extends WidgetOptions { * @docid * @public */ - mainButton?: MainButton; + mainButtonOptions?: MainButtonOptions; /** * @docid * @default null