UNPKG

@progress/kendo-angular-conversational-ui

Version:

Kendo UI for Angular Conversational UI components

1,016 lines (977 loc) 47.6 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { Component, ContentChild, ElementRef, EventEmitter, HostBinding, Input, isDevMode, Output, ViewChild, NgZone, Renderer2, ViewContainerRef } from '@angular/core'; import { AttachmentTemplateDirective } from './templates/attachment-template.directive'; import { L10N_PREFIX, LocalizationService } from '@progress/kendo-angular-l10n'; import { makeHandler } from './builtin-actions'; import { Subscription } from 'rxjs'; import { validatePackage } from '@progress/kendo-licensing'; import { packageMetadata } from '../package-metadata'; import { ChatMessageBoxTemplateDirective } from './templates/message-box.directive'; import { MessageBoxComponent } from './message-box.component'; import { MessageListComponent } from './message-list.component'; import { ScrollAnchorDirective } from './common/scroll-anchor.directive'; import { LocalizedMessagesDirective } from './l10n/localized-messages.directive'; import { isChanged, isPresent, processCssValue } from '@progress/kendo-angular-common'; import { ChatService } from './common/chat.service'; import { AppBarComponent } from "@progress/kendo-angular-navigation"; import { ChatHeaderTemplateDirective } from './templates/header-template.directive'; import { NgTemplateOutlet } from '@angular/common'; import { KENDO_BUTTON } from '@progress/kendo-angular-buttons'; import { pinIcon, xIcon } from '@progress/kendo-svg-icons'; import { IconWrapperComponent } from '@progress/kendo-angular-icons'; import { defaultModelFields } from './common/models/default-model-fields'; import { processMessages, SEND_BTN_DEFAULT_SETTINGS, FILE_ACTIONS, CONTEXT_MENU_ACTIONS, transformActions, MENU_ITEM_SELECTOR } from './common/utils'; import { ChatTimestampTemplateDirective } from './templates/timestamp-template.directive'; import { MessageReferenceComponent } from './message-reference-content.component'; import { ChatStatusTemplateDirective } from './templates/status-template.directive'; import { ChatSuggestionTemplateDirective } from './templates/suggestion-template.directive'; import { ContextMenuComponent } from '@progress/kendo-angular-menu'; import { NoDataTemplateDirective, MessageTemplateDirective, AuthorMessageContentTemplateDirective, ReceiverMessageContentTemplateDirective, ReceiverMessageTemplateDirective, AuthorMessageTemplateDirective, MessageContentTemplateDirective } from './chat.directives'; import { ChatUserStatusTemplateDirective } from './templates/user-status-template.directive'; import { SuggestionsScrollService } from './common/scroll.service'; import * as i0 from "@angular/core"; import * as i1 from "@progress/kendo-angular-l10n"; import * as i2 from "./common/chat.service"; import * as i3 from "@progress/kendo-angular-buttons"; /** * Represents the [Kendo UI Chat component for Angular](slug:overview_convui). * * Provides a conversational UI for chat-based applications. * Supports message templates, attachments, localization, and user actions. * * @example * ```html * <kendo-chat * [messages]="messages" * [authorId]="authorId" * (sendMessage)="onSendMessage($event)" * (executeAction)="onExecuteAction($event)"> * </kendo-chat> * ``` * * @remarks * Supported children components are: {@link CustomMessagesComponent}, {@link HeroCardComponent}, {@link AttachmentTemplateDirective}, {@link ChatHeaderTemplateDirective}, {@link ChatMessageBoxTemplateDirective}, {@link MessageContentTemplateDirective}, {@link MessageTemplateDirective},{@link ChatStatusTemplateDirective}, {@link ChatSuggestionTemplateDirective}, {@link ChatTimestampTemplateDirective}, {@link ChatNoDataTemplateDirective}, {@link ChatUserStatusTemplateDirective}, {@link AuthorMessageContentTemplateDirective}, {@link ReceiverMessageContentTemplateDirective}, {@link ReceiverMessageTemplateDirective}, {@link AuthorMessageTemplateDirective}. */ export class ChatComponent { localization; zone; renderer; element; chatService; /** * Sets the Chat messages. * Accepts an array of `Message` objects, but can also accept custom data types. * For more information, check [Data Binding](slug:databinding_chat) section in the documentation. */ messages; /** * Sets the ID that represents the local user. */ authorId; /** * Determines the type of input used in the message box. * ([see example](slug:message_chat)). * @default 'textarea' * * @hidden */ messageBoxType = 'textarea'; /** * Sets the height of the Chat component. * Accepts a string with CSS units (for example, `'400px'`, `'50%'`) or a number (interpreted as pixels). * The minimum height is `600px`. */ height; /** * Sets the width of the Chat component. * Accepts a string with CSS units (for example, `'400px'`, `'50%'`) or a number (interpreted as pixels). * The minimum width is `320px`. */ width; /** * Sets the placeholder text for the message input box. */ placeholder; /** * Controls the width of the message between the predefined options. * * @default 'standard' */ messageWidthMode = 'standard'; /** * Controls the visibility of timestamps in messages. * * @default 'focus' */ timestampVisibility = 'focus'; /** * Controls the visibility of usernames in messages. * When set to `true`, the username displays above each message bubble. * * @default true */ showUsername = true; /** * Controls the avatar visibility for the messages. * When set to `true`, the user avatar displays next to each message bubble. * * @default true */ showAvatar = true; /** * Enables the expand or collapse functionality for messages. * * @default false */ allowMessageCollapse = false; /** * Sets the Speech to Text button settings. * * @default true */ enableSpeechToText = true; /** * Sets the File Select settings. * * @default true */ enableFileSelect = true; /** * Sets the actions of the message toolbar. * These actions display in the message toolbar and let you perform specific operations on the message. * * @default [] */ messageToolbarActions = []; /** * Sets the value of the Message Box. * * @default '' */ inputValue = ''; /** * Sets the settings for the author's messages. */ authorMessageSettings; /** * Sets the settings for the receivers' messages. */ receiverMessageSettings; /** * Sets the default actions that display in the message context menu. * * @default [{ id: 'copy', label: 'Copy', icon: 'copy', svgIcon: copyIcon, disabled: false }, { id: 'reply', label: 'Reply', icon: 'undo', svgIcon: undoIcon, disabled: false }] */ defaultContextMenuActions = CONTEXT_MENU_ACTIONS; /** * Sets the actions that display in the message as a context menu. * These actions display as menu items and let you perform specific operations on the message. * The default actions are `copy` and `reply` and are defined by their `id`. */ set messageContextMenuActions(actions) { this._messageContextMenuActions = this.mergeWithDefaultActions(actions, CONTEXT_MENU_ACTIONS); } get messageContextMenuActions() { return this._messageContextMenuActions; } /** * Sets the default actions that display in the file actions DropDownButton. * * @default [{ id: 'download', label: 'Download', icon: 'download', svgIcon: downloadIcon, disabled: false }] */ defaultFileActions = FILE_ACTIONS; /** * Sets the actions that display in the file as items of a DropDownButton. * These actions display when you click the file DropDownButton and let you perform specific operations on the file. * The default action is `download` and is defined by its `id`. * * @default [{ id: 'download', label: 'Download', icon: 'download', svgIcon: downloadIcon, disabled: false }] */ set fileActions(actions) { this._fileActions = this.mergeWithDefaultActions(actions, FILE_ACTIONS); } get fileActions() { return this._fileActions; } /** * Sets the layout of the files in the message bubble. * * @default 'vertical' */ set messageFilesLayout(layout) { this.chatService.messageFilesLayout = layout; } /** * Sets the layout of the suggestions above the message input box. * * @default 'scroll' */ set suggestionsLayout(layoutMode) { if (layoutMode) { this.chatService.suggestionsLayout = layoutMode; } } /** * Sets the layout of the quick actions suggested below the messages. * * @default 'scroll' */ set quickActionsLayout(layoutMode) { if (layoutMode) { this.chatService.quickActionsLayout = layoutMode; } } /** * Sets the suggestions that display in the message input box. * Suggestions display as a list of clickable items that let you quickly insert predefined text into the message input. * * @default [] */ suggestions = []; /** * Sets the send button settings for the Chat component. * Allows customization of the send button appearance, icons and disabled state. * * @default { fillMode: 'solid', rounded: 'full', size: 'medium', themeColor: 'primary', icon: 'paper-plane', svgIcon: paperPlaneIcon} */ sendButtonSettings = SEND_BTN_DEFAULT_SETTINGS; /** * Sets the names of the model fields from which the Chat reads its data. * Lets you map custom data types to the expected `Message` format. */ set modelFields(value) { this._modelFields = { ...defaultModelFields, ...value }; } get modelFields() { return this._modelFields; } /** * Fires when the user sends a message by clicking the **Send** button or pressing **Enter**. * * The message is not automatically added to the `messages` array. */ sendMessage = new EventEmitter(); /** * Fires when the user clicks a quick action button in the message toolbar. */ toolbarActionClick = new EventEmitter(); /** * Fires when the user clicks an action in the message context menu. */ contextMenuActionClick = new EventEmitter(); /** * Fires when the user clicks an action in the file context menu. */ fileActionClick = new EventEmitter(); /** * Fires when the user clicks an action in the file context menu. */ download = new EventEmitter(); /** * Fires when the user clicks a quick action button. * The Chat internally handles [known actions](slug:api_conversational-ui_actiontype) such as `reply`, `openUrl`, and `call`. * * The event is preventable. Calling [`preventDefault`](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault) suppresses the built-in action. */ executeAction = new EventEmitter(); /** * Fires when the user clicks a suggestion in the message input box. */ suggestionExecute = new EventEmitter(); /** * Fires when the user selects a file in the message input box. */ fileSelect = new EventEmitter(); /** * Fires when the user removes a file from the message input box. */ fileRemove = new EventEmitter(); /** * Fires when the user unpins the pinned message. * This event triggers when the user clicks the delete button on the pinned message. */ unpin = new EventEmitter(); /** * Fires when the user types in the message input box. */ inputValueChange = new EventEmitter(); get className() { return 'k-chat'; } get dirAttr() { return this.direction; } set messagesContextMenu(contextMenu) { this.chatService.messagesContextMenu = contextMenu; } attachmentTemplate; chatHeaderTemplate; chatNoDataTemplate; authorMessageContentTemplate; receiverMessageContentTemplate; messageContentTemplate; authorMessageTemplate; receiverMessageTemplate; messageTemplate; timestampTemplate; suggestionTemplate; statusTemplate; messageBoxTemplate; userStatusTemplate; messageBox; /** * @hidden */ messageList; /** * @hidden * Returns processed messages when model fields are used, otherwise returns original messages. */ get processedMessages() { if (!this.messages || this.messages.length === 0) { return []; } if (this._modelFields && Object.keys(this._modelFields).some(key => this._modelFields[key] !== defaultModelFields[key])) { if (this.messages !== this._lastMessagesReference || this._modelFields !== this._lastModelFields) { this._cachedProcessedMessages = processMessages(this.messages, this._modelFields); this._lastMessagesReference = this.messages; this._lastModelFields = this._modelFields; } return this._cachedProcessedMessages; } return this.messages; } /** * Gets the actions available in the message context menu. * * @hidden */ get contextMenuActions() { const currentActions = this.chatService.calculatedContextMenuActions; if (currentActions !== this._lastContextMenuActionsReference) { this._cachedContextMenuActions = transformActions(currentActions); this._lastContextMenuActionsReference = currentActions; } return this._cachedContextMenuActions; } /** * @hidden */ get localizationText() { return this.localization; } /** * @hidden */ autoScroll = true; /** * @hidden */ pinIcon = pinIcon; /** * @hidden */ deleteIcon = xIcon; /** * @hidden */ pinnedMessage; direction; subs = new Subscription(); _modelFields = defaultModelFields; _messageContextMenuActions = CONTEXT_MENU_ACTIONS; _fileActions = FILE_ACTIONS; _cachedProcessedMessages = []; _lastMessagesReference = null; _lastModelFields = null; _cachedContextMenuActions = []; _lastContextMenuActionsReference = null; constructor(localization, zone, renderer, element, chatService) { this.localization = localization; this.zone = zone; this.renderer = renderer; this.element = element; this.chatService = chatService; validatePackage(packageMetadata); this.direction = localization.rtl ? 'rtl' : 'ltr'; this.subs.add(localization.changes.subscribe(({ rtl }) => { this.direction = rtl ? 'rtl' : 'ltr'; })); } /** * @hidden */ ngOnInit() { this.chatService.messageWidthMode = this.messageWidthMode; this.chatService.timestampVisibility = this.timestampVisibility; this.chatService.showUsername = this.showUsername; this.chatService.showAvatar = this.showAvatar; this.chatService.allowMessageCollapse = this.allowMessageCollapse; this.chatService.enableSpeechToText = this.enableSpeechToText; this.chatService.sendButtonSettings = this.sendButtonSettings; this.chatService.enableFileSelect = this.enableFileSelect; this.chatService.messageToolbarActions = this.messageToolbarActions; this.chatService.messageContextMenuActions = this.messageContextMenuActions; this.chatService.fileActions = this.fileActions; this.chatService.authorMessageSettings = this.authorMessageSettings; this.chatService.receiverMessageSettings = this.receiverMessageSettings; this.chatService.messages = this.processedMessages || []; this.chatService.chatElement = this.messageList; this.subs.add(this.chatService.toolbarAction$.subscribe((actionEvent) => { this.toolbarActionClick.emit(actionEvent); })); this.subs.add(this.chatService.contextMenuAction$.subscribe((actionEvent) => { this.contextMenuActionClick.emit(actionEvent); })); this.subs.add(this.chatService.fileAction$.subscribe((actionEvent) => { this.fileActionClick.emit(actionEvent); })); this.subs.add(this.chatService.fileDownload$.subscribe((actionEvent) => { this.download.emit(actionEvent); })); this.subs.add(this.chatService.inputValueChange$.subscribe((value) => { this.inputValueChange.emit(value); })); this.pinnedMessage = this.findLastPinnedMessage(); this.chatService.authorId = this.authorId; } /** * @hidden */ ngOnChanges(changes) { this.zone.runOutsideAngular(() => setTimeout(() => { this.messageList.element.nativeElement.style.flex = '1 1 auto'; })); if (isChanged('messages', changes, false)) { this.pinnedMessage = this.findLastPinnedMessage(); this.chatService.messages = this.processedMessages; } if (isChanged('height', changes, false)) { this.renderer.setStyle(this.element.nativeElement, 'height', `${processCssValue(this.height)}`); } if (isChanged('width', changes, false)) { this.renderer.setStyle(this.element.nativeElement, 'width', `${processCssValue(this.width)}`); } this.updateChatServiceProperties([ 'authorId', 'messageWidthMode', 'timestampVisibility', 'showUsername', 'showAvatar', 'allowMessageCollapse', 'enableSpeechToText', 'sendButtonSettings', 'enableFileSelect', 'messageToolbarActions', 'messageContextMenuActions', 'fileActions', 'authorMessageSettings', 'receiverMessageSettings' ], changes); } /** * @hidden */ ngAfterViewInit() { if (!isDevMode()) { return; } if (!isPresent(this.authorId)) { throw new Error('AuthorId must be set.'); } } /** * @hidden */ ngOnDestroy() { if (this.subs) { this.subs.unsubscribe(); } } /** * @hidden */ dispatchAction(e) { this.executeAction.emit(e); if (!e.isDefaultPrevented()) { const handler = makeHandler(e.action); handler(e.action, this); if (!this.messageBoxTemplate) { this.messageBox.messageBoxInput.focus(); } } } /** * @hidden */ textFor(key) { return this.localization.get(key); } /** * @hidden */ scrollToPinnedMessage() { if (this.pinnedMessage) { this.chatService.scrollToMessage(this.pinnedMessage?.id); } } /** * @hidden */ onContextMenuAction(action) { if (action.id === 'reply') { this.chatService.reply = this.chatService.activeMessage; } if (action.id === 'copy') { navigator.clipboard.writeText(this.chatService.activeMessage.text); } this.chatService.emit('contextMenuAction', { action, message: this.chatService.activeMessage }); } /** * @hidden */ handleMenuClose(event) { if (event) { const originalEvent = event.originalEvent; originalEvent && this.onActionButtonClick(originalEvent); } this.chatService.activeMessage = null; this.chatService.emit('contextMenuVisibilityChange', false); if (this.chatService.selectOnMenuClose) { this.chatService.activeMessageElement.selected = true; this.chatService.focusActiveMessageElement(); } } /** * @hidden */ onActionButtonClick(event) { const clickOutsideMessage = event instanceof MouseEvent && !event.target?.closest('.k-chat-bubble'); const menuItemClick = event instanceof MouseEvent && event.target?.closest(MENU_ITEM_SELECTOR); if (clickOutsideMessage && !menuItemClick) { this.chatService.selectOnMenuClose = false; } } findLastPinnedMessage() { return [...this.processedMessages].reverse().find((message) => message.isPinned); } updateChatServiceProperties(propNames, changes) { propNames.forEach(propName => { if (isChanged(propName, changes, false)) { this.chatService[propName] = this[propName]; } }); } mergeWithDefaultActions(actions, defaultActions) { if (!actions || actions.length === 0) { return []; } return actions.map(userAction => { const defaultAction = defaultActions.find(action => action.id === userAction?.id); if (defaultAction) { return { ...defaultAction, ...userAction }; } return userAction; }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatComponent, deps: [{ token: i1.LocalizationService }, { token: i0.NgZone }, { token: i0.Renderer2 }, { token: i0.ElementRef }, { token: i2.ChatService }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ChatComponent, isStandalone: true, selector: "kendo-chat", inputs: { messages: "messages", authorId: "authorId", messageBoxType: "messageBoxType", height: "height", width: "width", placeholder: "placeholder", messageWidthMode: "messageWidthMode", timestampVisibility: "timestampVisibility", showUsername: "showUsername", showAvatar: "showAvatar", allowMessageCollapse: "allowMessageCollapse", enableSpeechToText: "enableSpeechToText", enableFileSelect: "enableFileSelect", messageToolbarActions: "messageToolbarActions", inputValue: "inputValue", authorMessageSettings: "authorMessageSettings", receiverMessageSettings: "receiverMessageSettings", messageContextMenuActions: "messageContextMenuActions", fileActions: "fileActions", messageFilesLayout: "messageFilesLayout", suggestionsLayout: "suggestionsLayout", quickActionsLayout: "quickActionsLayout", suggestions: "suggestions", sendButtonSettings: "sendButtonSettings", modelFields: "modelFields" }, outputs: { sendMessage: "sendMessage", toolbarActionClick: "toolbarActionClick", contextMenuActionClick: "contextMenuActionClick", fileActionClick: "fileActionClick", download: "download", executeAction: "executeAction", suggestionExecute: "suggestionExecute", fileSelect: "fileSelect", fileRemove: "fileRemove", unpin: "unpin", inputValueChange: "inputValueChange" }, host: { properties: { "class": "this.className", "attr.dir": "this.dirAttr" } }, providers: [ LocalizationService, ChatService, SuggestionsScrollService, { provide: L10N_PREFIX, useValue: 'kendo.chat' } ], queries: [{ propertyName: "attachmentTemplate", first: true, predicate: AttachmentTemplateDirective, descendants: true }, { propertyName: "chatHeaderTemplate", first: true, predicate: ChatHeaderTemplateDirective, descendants: true }, { propertyName: "chatNoDataTemplate", first: true, predicate: NoDataTemplateDirective, descendants: true }, { propertyName: "authorMessageContentTemplate", first: true, predicate: AuthorMessageContentTemplateDirective, descendants: true }, { propertyName: "receiverMessageContentTemplate", first: true, predicate: ReceiverMessageContentTemplateDirective, descendants: true }, { propertyName: "messageContentTemplate", first: true, predicate: MessageContentTemplateDirective, descendants: true }, { propertyName: "authorMessageTemplate", first: true, predicate: AuthorMessageTemplateDirective, descendants: true }, { propertyName: "receiverMessageTemplate", first: true, predicate: ReceiverMessageTemplateDirective, descendants: true }, { propertyName: "messageTemplate", first: true, predicate: MessageTemplateDirective, descendants: true }, { propertyName: "timestampTemplate", first: true, predicate: ChatTimestampTemplateDirective, descendants: true }, { propertyName: "suggestionTemplate", first: true, predicate: ChatSuggestionTemplateDirective, descendants: true }, { propertyName: "statusTemplate", first: true, predicate: ChatStatusTemplateDirective, descendants: true }, { propertyName: "messageBoxTemplate", first: true, predicate: ChatMessageBoxTemplateDirective, descendants: true }, { propertyName: "userStatusTemplate", first: true, predicate: ChatUserStatusTemplateDirective, descendants: true }], viewQueries: [{ propertyName: "messagesContextMenu", first: true, predicate: ["messagesContextMenu"], descendants: true }, { propertyName: "messageBox", first: true, predicate: ["messageBox"], descendants: true }, { propertyName: "messageList", first: true, predicate: ["messageList"], descendants: true, read: ViewContainerRef, static: true }], usesOnChanges: true, ngImport: i0, template: ` <ng-container kendoChatLocalizedMessages i18n-deletedMessageSenderText="kendo.chat.deletedMessageSenderText|The text that is displayed when the sender deletes a message" deletedMessageSenderText="You removed this message." i18n-deletedMessageReceiverText="kendo.chat.deletedMessageReceiverText|The text that is displayed when the receiver deletes a message" deletedMessageReceiverText="This message was removed by its sender." i18n-messagePlaceholder="kendo.chat.messagePlaceholder|The placholder text of the message text input" messagePlaceholder="Type a message..." i18n-send="kendo.chat.send|The text for the Send button" send="Send..." i18n-messageListLabel="kendo.chat.messageListLabel|The label text for the Message list" messageListLabel="Message list" i18n-messageBoxInputLabel="kendo.chat.messageBoxInputLabel|The label text for the Message input box" messageBoxInputLabel="Message" i18n-messageAttachmentLeftArrow="kendo.chat.messageAttachmentLeftArrow|The text for the left arrow of the message attachments" messageAttachmentLeftArrow="Previous item" i18n-messageAttachmentRightArrow="kendo.chat.messageAttachmentRightArrow|The text for the right arrow of the message attachments" messageAttachmentRightArrow="Next item" i18n-speechToTextButtonTitle="kendo.chat.speechToTextButtonTitle|Sets the Speech to Text button title." speechToTextButtonTitle="Speech to Text" i18n-fileSelectButtonTitle="kendo.chat.fileSelectButtonTitle|Sets the File Select button title." fileSelectButtonTitle="Select files" i18n-removeReplyTitle="kendo.chat.removeReplyTitle|Sets the title of the icon which removes the reply reference in the Message Box." removeReplyTitle="Remove reply" i18n-removeFileTitle="kendo.chat.removeFileTitle|Sets the title of the icon which removes a selected file in the Message Box." removeFileTitle="Remove file" i18n-expandTitle="kendo.chat.expandTitle|Sets the title of the icon which demonstrates that the message can be expanded." expandTitle="Expand message" i18n-collapseTitle="kendo.chat.collapseTitle|Sets the title of the icon which demonstrates that the message can be collapsed." collapseTitle="Collapse message" i18n-fileActionsTitle="kendo.chat.fileActionsTitle|Sets the title of the DropDownButton which opens the File actions." fileActionsTitle="File actions" i18n-downloadAllFilesText="kendo.chat.downloadAllFilesText|Sets the text that is displayed in the download section of the message." downloadAllFilesText="Download all" i18n-previousSuggestionsButtonTitle="kendo.chat.previousSuggestionsButtonTitle|The title of the button that scrolls to the previous suggestions" previousSuggestionsButtonTitle="Scroll left" i18n-nextSuggestionsButtonTitle="kendo.chat.nextSuggestionsButtonTitle|The title of the button that scrolls to the next suggestions" nextSuggestionsButtonTitle="Scroll right" > </ng-container> @if (chatHeaderTemplate) { <kendo-appbar class="k-chat-header" positionMode="sticky" themeColor="inherit"> <ng-container *ngTemplateOutlet="chatHeaderTemplate.templateRef"></ng-container> </kendo-appbar> } @if (pinnedMessage) { <div class="k-message-reference k-message-reference-receiver k-message-pinned" (click)="scrollToPinnedMessage()"> <kendo-icon-wrapper size="xlarge" name="pin" [svgIcon]="pinIcon" > </kendo-icon-wrapper> <chat-message-reference-content [message]="pinnedMessage"></chat-message-reference-content> <span class="k-spacer"></span> <button kendoButton [svgIcon]="deleteIcon" (click)="unpin.emit(pinnedMessage)" fillMode="flat"></button> </div> } <div #messageList class="k-message-list" aria-live="polite" role="log" kendoChatScrollAnchor [attr.aria-label]="textFor('messageListLabel')" #anchor="scrollAnchor" [(autoScroll)]="autoScroll" > @if (processedMessages && processedMessages.length === 0) { <div class="k-message-list-content k-message-list-content-empty"> <ng-template [ngTemplateOutlet]="chatNoDataTemplate?.templateRef"> </ng-template> </div> } @else { <kendo-chat-message-list [messages]="processedMessages" [authorMessageContentTemplate]="authorMessageContentTemplate" [receiverMessageContentTemplate]="receiverMessageContentTemplate" [messageContentTemplate]="messageContentTemplate" [authorMessageTemplate]="authorMessageTemplate" [receiverMessageTemplate]="receiverMessageTemplate" [messageTemplate]="messageTemplate" [timestampTemplate]="timestampTemplate" [statusTemplate]="statusTemplate" [userStatusTemplate]="userStatusTemplate" [localization]="localizationText" [attachmentTemplate]="attachmentTemplate" [authorId]="authorId" (executeAction)="dispatchAction($event)" (resize)="anchor.scrollToBottom()" (navigate)="this.autoScroll = false" > </kendo-chat-message-list> } </div> <kendo-message-box #messageBox [messageBoxTemplate]="messageBoxTemplate" [suggestionTemplate]="suggestionTemplate" [suggestions]="suggestions" [placeholder]="placeholder" [authorId]="authorId" [autoScroll]="autoScroll" [inputValue]="inputValue" [localization]="localizationText" (sendMessage)="sendMessage.emit($event)" (executeSuggestion)="suggestionExecute.emit($event)" (fileSelect)="fileSelect.emit($event)" (fileRemove)="fileRemove.emit($event)" > </kendo-message-box> <kendo-contextmenu #messagesContextMenu [items]="contextMenuActions" [popupAlign]="{ horizontal: 'right', vertical: 'top' }" [collision]="{ horizontal: 'flip', vertical: 'flip'}" (popupClose)="handleMenuClose($event)" (select)="onContextMenuAction($event.item.originalAction)" ></kendo-contextmenu> `, isInline: true, dependencies: [{ kind: "directive", type: LocalizedMessagesDirective, selector: "[kendoChatLocalizedMessages]" }, { kind: "directive", type: ScrollAnchorDirective, selector: "[kendoChatScrollAnchor]", inputs: ["autoScroll"], outputs: ["autoScrollChange"], exportAs: ["scrollAnchor"] }, { kind: "component", type: MessageListComponent, selector: "kendo-chat-message-list", inputs: ["messages", "attachmentTemplate", "authorMessageContentTemplate", "receiverMessageContentTemplate", "messageContentTemplate", "authorMessageTemplate", "receiverMessageTemplate", "messageTemplate", "timestampTemplate", "statusTemplate", "userStatusTemplate", "localization", "authorId"], outputs: ["executeAction", "navigate", "resize"] }, { kind: "component", type: MessageBoxComponent, selector: "kendo-message-box", inputs: ["authorId", "autoScroll", "suggestions", "placeholder", "inputValue", "localization", "messageBoxTemplate", "suggestionTemplate"], outputs: ["sendMessage", "executeSuggestion", "fileSelect", "fileRemove"] }, { kind: "component", type: MessageReferenceComponent, selector: "chat-message-reference-content", inputs: ["message"] }, { kind: "component", type: AppBarComponent, selector: "kendo-appbar", inputs: ["position", "positionMode", "themeColor"], exportAs: ["kendoAppBar"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconWrapperComponent, selector: "kendo-icon-wrapper", inputs: ["name", "svgIcon", "innerCssClass", "customFontClass", "size"], exportAs: ["kendoIconWrapper"] }, { kind: "component", type: i3.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "component", type: ContextMenuComponent, selector: "kendo-contextmenu", inputs: ["showOn", "target", "filter", "alignToAnchor", "vertical", "popupAnimate", "popupAlign", "anchorAlign", "collision", "appendTo", "ariaLabel"], outputs: ["popupOpen", "popupClose", "select", "open", "close"], exportAs: ["kendoContextMenu"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatComponent, decorators: [{ type: Component, args: [{ providers: [ LocalizationService, ChatService, SuggestionsScrollService, { provide: L10N_PREFIX, useValue: 'kendo.chat' } ], selector: 'kendo-chat', template: ` <ng-container kendoChatLocalizedMessages i18n-deletedMessageSenderText="kendo.chat.deletedMessageSenderText|The text that is displayed when the sender deletes a message" deletedMessageSenderText="You removed this message." i18n-deletedMessageReceiverText="kendo.chat.deletedMessageReceiverText|The text that is displayed when the receiver deletes a message" deletedMessageReceiverText="This message was removed by its sender." i18n-messagePlaceholder="kendo.chat.messagePlaceholder|The placholder text of the message text input" messagePlaceholder="Type a message..." i18n-send="kendo.chat.send|The text for the Send button" send="Send..." i18n-messageListLabel="kendo.chat.messageListLabel|The label text for the Message list" messageListLabel="Message list" i18n-messageBoxInputLabel="kendo.chat.messageBoxInputLabel|The label text for the Message input box" messageBoxInputLabel="Message" i18n-messageAttachmentLeftArrow="kendo.chat.messageAttachmentLeftArrow|The text for the left arrow of the message attachments" messageAttachmentLeftArrow="Previous item" i18n-messageAttachmentRightArrow="kendo.chat.messageAttachmentRightArrow|The text for the right arrow of the message attachments" messageAttachmentRightArrow="Next item" i18n-speechToTextButtonTitle="kendo.chat.speechToTextButtonTitle|Sets the Speech to Text button title." speechToTextButtonTitle="Speech to Text" i18n-fileSelectButtonTitle="kendo.chat.fileSelectButtonTitle|Sets the File Select button title." fileSelectButtonTitle="Select files" i18n-removeReplyTitle="kendo.chat.removeReplyTitle|Sets the title of the icon which removes the reply reference in the Message Box." removeReplyTitle="Remove reply" i18n-removeFileTitle="kendo.chat.removeFileTitle|Sets the title of the icon which removes a selected file in the Message Box." removeFileTitle="Remove file" i18n-expandTitle="kendo.chat.expandTitle|Sets the title of the icon which demonstrates that the message can be expanded." expandTitle="Expand message" i18n-collapseTitle="kendo.chat.collapseTitle|Sets the title of the icon which demonstrates that the message can be collapsed." collapseTitle="Collapse message" i18n-fileActionsTitle="kendo.chat.fileActionsTitle|Sets the title of the DropDownButton which opens the File actions." fileActionsTitle="File actions" i18n-downloadAllFilesText="kendo.chat.downloadAllFilesText|Sets the text that is displayed in the download section of the message." downloadAllFilesText="Download all" i18n-previousSuggestionsButtonTitle="kendo.chat.previousSuggestionsButtonTitle|The title of the button that scrolls to the previous suggestions" previousSuggestionsButtonTitle="Scroll left" i18n-nextSuggestionsButtonTitle="kendo.chat.nextSuggestionsButtonTitle|The title of the button that scrolls to the next suggestions" nextSuggestionsButtonTitle="Scroll right" > </ng-container> @if (chatHeaderTemplate) { <kendo-appbar class="k-chat-header" positionMode="sticky" themeColor="inherit"> <ng-container *ngTemplateOutlet="chatHeaderTemplate.templateRef"></ng-container> </kendo-appbar> } @if (pinnedMessage) { <div class="k-message-reference k-message-reference-receiver k-message-pinned" (click)="scrollToPinnedMessage()"> <kendo-icon-wrapper size="xlarge" name="pin" [svgIcon]="pinIcon" > </kendo-icon-wrapper> <chat-message-reference-content [message]="pinnedMessage"></chat-message-reference-content> <span class="k-spacer"></span> <button kendoButton [svgIcon]="deleteIcon" (click)="unpin.emit(pinnedMessage)" fillMode="flat"></button> </div> } <div #messageList class="k-message-list" aria-live="polite" role="log" kendoChatScrollAnchor [attr.aria-label]="textFor('messageListLabel')" #anchor="scrollAnchor" [(autoScroll)]="autoScroll" > @if (processedMessages && processedMessages.length === 0) { <div class="k-message-list-content k-message-list-content-empty"> <ng-template [ngTemplateOutlet]="chatNoDataTemplate?.templateRef"> </ng-template> </div> } @else { <kendo-chat-message-list [messages]="processedMessages" [authorMessageContentTemplate]="authorMessageContentTemplate" [receiverMessageContentTemplate]="receiverMessageContentTemplate" [messageContentTemplate]="messageContentTemplate" [authorMessageTemplate]="authorMessageTemplate" [receiverMessageTemplate]="receiverMessageTemplate" [messageTemplate]="messageTemplate" [timestampTemplate]="timestampTemplate" [statusTemplate]="statusTemplate" [userStatusTemplate]="userStatusTemplate" [localization]="localizationText" [attachmentTemplate]="attachmentTemplate" [authorId]="authorId" (executeAction)="dispatchAction($event)" (resize)="anchor.scrollToBottom()" (navigate)="this.autoScroll = false" > </kendo-chat-message-list> } </div> <kendo-message-box #messageBox [messageBoxTemplate]="messageBoxTemplate" [suggestionTemplate]="suggestionTemplate" [suggestions]="suggestions" [placeholder]="placeholder" [authorId]="authorId" [autoScroll]="autoScroll" [inputValue]="inputValue" [localization]="localizationText" (sendMessage)="sendMessage.emit($event)" (executeSuggestion)="suggestionExecute.emit($event)" (fileSelect)="fileSelect.emit($event)" (fileRemove)="fileRemove.emit($event)" > </kendo-message-box> <kendo-contextmenu #messagesContextMenu [items]="contextMenuActions" [popupAlign]="{ horizontal: 'right', vertical: 'top' }" [collision]="{ horizontal: 'flip', vertical: 'flip'}" (popupClose)="handleMenuClose($event)" (select)="onContextMenuAction($event.item.originalAction)" ></kendo-contextmenu> `, standalone: true, imports: [LocalizedMessagesDirective, ScrollAnchorDirective, MessageListComponent, MessageBoxComponent, MessageReferenceComponent, AppBarComponent, NgTemplateOutlet, IconWrapperComponent, KENDO_BUTTON, ContextMenuComponent] }] }], ctorParameters: () => [{ type: i1.LocalizationService }, { type: i0.NgZone }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: i2.ChatService }], propDecorators: { messages: [{ type: Input }], authorId: [{ type: Input }], messageBoxType: [{ type: Input }], height: [{ type: Input }], width: [{ type: Input }], placeholder: [{ type: Input }], messageWidthMode: [{ type: Input }], timestampVisibility: [{ type: Input }], showUsername: [{ type: Input }], showAvatar: [{ type: Input }], allowMessageCollapse: [{ type: Input }], enableSpeechToText: [{ type: Input }], enableFileSelect: [{ type: Input }], messageToolbarActions: [{ type: Input }], inputValue: [{ type: Input }], authorMessageSettings: [{ type: Input }], receiverMessageSettings: [{ type: Input }], messageContextMenuActions: [{ type: Input }], fileActions: [{ type: Input }], messageFilesLayout: [{ type: Input }], suggestionsLayout: [{ type: Input }], quickActionsLayout: [{ type: Input }], suggestions: [{ type: Input }], sendButtonSettings: [{ type: Input }], modelFields: [{ type: Input }], sendMessage: [{ type: Output }], toolbarActionClick: [{ type: Output }], contextMenuActionClick: [{ type: Output }], fileActionClick: [{ type: Output }], download: [{ type: Output }], executeAction: [{ type: Output }], suggestionExecute: [{ type: Output }], fileSelect: [{ type: Output }], fileRemove: [{ type: Output }], unpin: [{ type: Output }], inputValueChange: [{ type: Output }], className: [{ type: HostBinding, args: ['class'] }], dirAttr: [{ type: HostBinding, args: ['attr.dir'] }], messagesContextMenu: [{ type: ViewChild, args: ['messagesContextMenu'] }], attachmentTemplate: [{ type: ContentChild, args: [AttachmentTemplateDirective] }], chatHeaderTemplate: [{ type: ContentChild, args: [ChatHeaderTemplateDirective] }], chatNoDataTemplate: [{ type: ContentChild, args: [NoDataTemplateDirective] }], authorMessageContentTemplate: [{ type: ContentChild, args: [AuthorMessageContentTemplateDirective] }], receiverMessageContentTemplate: [{ type: ContentChild, args: [ReceiverMessageContentTemplateDirective] }], messageContentTemplate: [{ type: ContentChild, args: [MessageContentTemplateDirective] }], authorMessageTemplate: [{ type: ContentChild, args: [AuthorMessageTemplateDirective] }], receiverMessageTemplate: [{ type: ContentChild, args: [ReceiverMessageTemplateDirective] }], messageTemplate: [{ type: ContentChild, args: [MessageTemplateDirective] }], timestampTemplate: [{ type: ContentChild, args: [ChatTimestampTemplateDirective] }], suggestionTemplate: [{ type: ContentChild, args: [ChatSuggestionTemplateDirective] }], statusTemplate: [{ type: ContentChild, args: [ChatStatusTemplateDirective] }], messageBoxTemplate: [{ type: ContentChild, args: [ChatMessageBoxTemplateDirective] }], userStatusTemplate: [{ type: ContentChild, args: [ChatUserStatusTemplateDirective] }], messageBox: [{ type: ViewChild, args: ['messageBox'] }], messageList: [{ type: ViewChild, args: ['messageList', { static: true, read: ViewContainerRef }] }] } });