@progress/kendo-angular-conversational-ui 21.0.0-develop.2 → 21.0.0-develop.21

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.
Files changed (69) hide show
  1. package/chat/api/files-layout.d.ts +12 -0
  2. package/chat/api/index.d.ts +4 -1
  3. package/chat/api/message-settings.interface.d.ts +41 -0
  4. package/chat/api/message-width-mode.d.ts +1 -1
  5. package/chat/api/message.interface.d.ts +5 -1
  6. package/chat/api/suggestions-layout.d.ts +20 -0
  7. package/chat/api/timestamp-visibility.d.ts +10 -0
  8. package/chat/chat.component.d.ts +111 -34
  9. package/chat/chat.directives.d.ts +18 -0
  10. package/chat/chat.module.d.ts +15 -8
  11. package/chat/common/chat.service.d.ts +36 -3
  12. package/chat/common/models/model-fields.d.ts +0 -6
  13. package/chat/common/scroll-button.component.d.ts +32 -0
  14. package/chat/common/scroll.service.d.ts +39 -0
  15. package/chat/common/utils.d.ts +13 -1
  16. package/chat/l10n/messages.d.ts +9 -1
  17. package/chat/message-list.component.d.ts +22 -3
  18. package/chat/message.component.d.ts +40 -16
  19. package/chat/suggested-actions.component.d.ts +30 -5
  20. package/chat/templates/author-message-content-template.directive.d.ts +28 -0
  21. package/chat/templates/author-message-template.directive.d.ts +28 -0
  22. package/chat/templates/message-content-template.directive.d.ts +28 -0
  23. package/chat/templates/message-template.directive.d.ts +1 -1
  24. package/chat/templates/no-data-template.directive.d.ts +27 -0
  25. package/chat/templates/receiver-message-content-template.directive.d.ts +28 -0
  26. package/chat/templates/receiver-message-template.directive.d.ts +28 -0
  27. package/chat/templates/user-status-template.directive.d.ts +27 -0
  28. package/codemods/utils.js +477 -375
  29. package/codemods/v20/chat-user.js +7 -5
  30. package/codemods/v21/chat-messagetoolbarvisibility.js +53 -0
  31. package/codemods/v21/chat-pinnedbyfield.js +19 -0
  32. package/conversational-ui.module.d.ts +18 -11
  33. package/directives.d.ts +9 -2
  34. package/esm2022/chat/api/index.mjs +4 -1
  35. package/{chat/api/message-toolbar-visibility.d.ts → esm2022/chat/api/message-settings.interface.mjs} +1 -4
  36. package/esm2022/chat/api/suggestions-layout.mjs +5 -0
  37. package/esm2022/chat/api/timestamp-visibility.mjs +5 -0
  38. package/esm2022/chat/builtin-actions.mjs +2 -0
  39. package/esm2022/chat/chat-file.component.mjs +2 -2
  40. package/esm2022/chat/chat.component.mjs +295 -71
  41. package/esm2022/chat/chat.directives.mjs +18 -0
  42. package/esm2022/chat/chat.module.mjs +16 -9
  43. package/esm2022/chat/common/chat.service.mjs +86 -4
  44. package/esm2022/chat/common/models/default-model-fields.mjs +0 -1
  45. package/esm2022/chat/common/scroll-button.component.mjs +81 -0
  46. package/esm2022/chat/common/scroll.service.mjs +110 -0
  47. package/esm2022/chat/common/utils.mjs +22 -3
  48. package/esm2022/chat/l10n/messages.mjs +13 -1
  49. package/esm2022/chat/message-attachments.component.mjs +2 -2
  50. package/esm2022/chat/message-box.component.mjs +5 -2
  51. package/esm2022/chat/message-list.component.mjs +184 -40
  52. package/esm2022/chat/message.component.mjs +487 -326
  53. package/esm2022/chat/suggested-actions.component.mjs +298 -80
  54. package/esm2022/chat/templates/author-message-content-template.directive.mjs +39 -0
  55. package/esm2022/chat/templates/author-message-template.directive.mjs +39 -0
  56. package/esm2022/chat/templates/message-content-template.directive.mjs +39 -0
  57. package/esm2022/chat/templates/message-template.directive.mjs +1 -1
  58. package/esm2022/chat/templates/no-data-template.directive.mjs +38 -0
  59. package/esm2022/chat/templates/receiver-message-content-template.directive.mjs +39 -0
  60. package/esm2022/chat/templates/receiver-message-template.directive.mjs +39 -0
  61. package/esm2022/chat/templates/user-status-template.directive.mjs +38 -0
  62. package/esm2022/conversational-ui.module.mjs +19 -12
  63. package/esm2022/directives.mjs +15 -1
  64. package/esm2022/index.mjs +7 -0
  65. package/esm2022/package-metadata.mjs +2 -2
  66. package/fesm2022/progress-kendo-angular-conversational-ui.mjs +1822 -567
  67. package/index.d.ts +7 -0
  68. package/package.json +26 -14
  69. /package/esm2022/chat/api/{message-toolbar-visibility.mjs → files-layout.mjs} +0 -0
@@ -3,15 +3,15 @@
3
3
  * Licensed under commercial license. See LICENSE.md in the project root for more information
4
4
  *-------------------------------------------------------------------------------------------*/
5
5
  import * as i0 from '@angular/core';
6
- import { InjectionToken, Directive, Inject, HostBinding, ViewChild, Input, Injectable, Optional, forwardRef, EventEmitter, Component, ContentChildren, ContentChild, Output, isDevMode, ViewChildren, HostListener, ElementRef, ViewContainerRef, inject, SkipSelf, NgModule } from '@angular/core';
6
+ import { InjectionToken, Directive, Inject, HostBinding, ViewChild, Input, Injectable, Optional, forwardRef, EventEmitter, Component, ContentChildren, ContentChild, Output, isDevMode, ElementRef, ViewChildren, HostListener, ViewContainerRef, inject, SkipSelf, NgModule } from '@angular/core';
7
7
  import { IconWrapperComponent, IconsService } from '@progress/kendo-angular-icons';
8
8
  import * as i1$2 from '@progress/kendo-angular-popup';
9
9
  import { PopupComponent, KENDO_POPUP, PopupService } from '@progress/kendo-angular-popup';
10
- import { isPresent, normalizeNumpadKeys, Keys, focusableSelector, guid, getter, closest as closest$1, ResizeSensorComponent, isChanged, processCssValue, isDocumentAvailable, ResizeBatchService } from '@progress/kendo-angular-common';
10
+ import { isPresent, normalizeNumpadKeys, Keys, focusableSelector, guid, getter, isDocumentAvailable, closest as closest$1, ResizeSensorComponent, isChanged, processCssValue, ResizeBatchService } from '@progress/kendo-angular-common';
11
11
  import { DialogContainerService, DialogService, WindowService, WindowContainerService } from '@progress/kendo-angular-dialog';
12
12
  import { NgFor, NgIf, NgTemplateOutlet, NgClass, NgSwitch, NgSwitchCase } from '@angular/common';
13
13
  import { Subject, Subscription, fromEvent } from 'rxjs';
14
- import { sparklesIcon, commentIcon, moreHorizontalIcon, stopSmIcon, thumbUpIcon, thumbDownOutlineIcon, thumbDownIcon, thumbUpOutlineIcon, copyIcon, arrowRotateCwIcon, chevronUpIcon, chevronDownIcon, paperPlaneIcon, undoIcon, downloadIcon, xIcon, moreVerticalIcon, paperclipIcon, fileIcon, chevronLeftIcon, chevronRightIcon, pinIcon, cancelOutlineIcon, menuIcon } from '@progress/kendo-svg-icons';
14
+ import { sparklesIcon, commentIcon, moreHorizontalIcon, stopSmIcon, thumbUpIcon, thumbDownOutlineIcon, thumbDownIcon, thumbUpOutlineIcon, copyIcon, arrowRotateCwIcon, chevronUpIcon, chevronDownIcon, paperPlaneIcon, undoIcon, downloadIcon, chevronLeftIcon, chevronRightIcon, xIcon, moreVerticalIcon, paperclipIcon, fileIcon, pinIcon, cancelOutlineIcon, menuIcon } from '@progress/kendo-svg-icons';
15
15
  import * as i1 from '@progress/kendo-angular-l10n';
16
16
  import { ComponentMessages, LocalizationService, L10N_PREFIX } from '@progress/kendo-angular-l10n';
17
17
  import { validatePackage } from '@progress/kendo-licensing';
@@ -25,10 +25,10 @@ import { TextAreaComponent, TextAreaSuffixComponent, TextAreaPrefixComponent, In
25
25
  import { fileSVGGroupIcon, fileGroupClass, getTotalFilesSizeMessage, FileSelectComponent } from '@progress/kendo-angular-upload';
26
26
  import { FormsModule } from '@angular/forms';
27
27
  import * as i1$1 from '@progress/kendo-angular-intl';
28
- import * as i4 from '@progress/kendo-angular-menu';
29
- import { ContextMenuComponent, KENDO_CONTEXTMENU } from '@progress/kendo-angular-menu';
30
28
  import { ToolBarComponent, ToolBarButtonComponent } from '@progress/kendo-angular-toolbar';
31
29
  import { AppBarComponent } from '@progress/kendo-angular-navigation';
30
+ import * as i4 from '@progress/kendo-angular-menu';
31
+ import { ContextMenuComponent, KENDO_CONTEXTMENU } from '@progress/kendo-angular-menu';
32
32
 
33
33
  /**
34
34
  * @hidden
@@ -216,8 +216,8 @@ const packageMetadata = {
216
216
  productName: 'Kendo UI for Angular',
217
217
  productCode: 'KENDOUIANGULAR',
218
218
  productCodes: ['KENDOUIANGULAR'],
219
- publishDate: 1761752891,
220
- version: '21.0.0-develop.2',
219
+ publishDate: 1762426801,
220
+ version: '21.0.0-develop.21',
221
221
  licensingDocsUrl: 'https://www.telerik.com/kendo-angular-ui/my-license/'
222
222
  };
223
223
 
@@ -1887,6 +1887,7 @@ const noop = () => { };
1887
1887
  const handlers = {
1888
1888
  'reply': (action, sender) => {
1889
1889
  sender.sendMessage.emit(new SendMessageEvent({
1890
+ id: guid(),
1890
1891
  author: { id: sender.authorId },
1891
1892
  text: action.value,
1892
1893
  timestamp: new Date()
@@ -1904,40 +1905,6 @@ const handlers = {
1904
1905
  */
1905
1906
  const makeHandler = (action) => handlers[action.type] || noop;
1906
1907
 
1907
- /**
1908
- * Defines a template for displaying Chat messages.
1909
- *
1910
- * To define a message template, nest an `<ng-template>` tag with the `kendoChatMessageTemplate` directive inside the `<kendo-chat>` component.
1911
- * The template context is set to the `message` instance.
1912
- * For more information, refer to the article on [message templates](slug:message_templates_chat).
1913
- *
1914
- * @example
1915
- * ```html
1916
- * <kendo-chat>
1917
- * <ng-template kendoChatMessageTemplate let-message>
1918
- * <div>Message: {{ message.text }}</div>
1919
- * </ng-template>
1920
- * </kendo-chat>
1921
- * ```
1922
- */
1923
- class MessageTemplateDirective {
1924
- templateRef;
1925
- constructor(templateRef) {
1926
- this.templateRef = templateRef;
1927
- }
1928
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageTemplateDirective, deps: [{ token: i0.TemplateRef, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
1929
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: MessageTemplateDirective, isStandalone: true, selector: "[kendoChatMessageTemplate]", ngImport: i0 });
1930
- }
1931
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageTemplateDirective, decorators: [{
1932
- type: Directive,
1933
- args: [{
1934
- selector: '[kendoChatMessageTemplate]',
1935
- standalone: true
1936
- }]
1937
- }], ctorParameters: () => [{ type: i0.TemplateRef, decorators: [{
1938
- type: Optional
1939
- }] }] });
1940
-
1941
1908
  /**
1942
1909
  * Creates a message box area that overrides the default message box of the Chat component.
1943
1910
  *
@@ -1986,7 +1953,6 @@ const defaultModelFields = {
1986
1953
  attachmentLayoutField: 'attachmentLayout',
1987
1954
  suggestedActionsField: 'suggestedActions',
1988
1955
  isPinnedField: 'isPinned',
1989
- pinnedByField: 'pinnedBy',
1990
1956
  replyToIdField: 'replyToId',
1991
1957
  isDeletedField: 'isDeleted',
1992
1958
  typingField: 'typing'
@@ -2053,6 +2019,10 @@ const FILESELECT_DEFAULT_SETTINGS = {
2053
2019
  multiple: true,
2054
2020
  disabled: false
2055
2021
  };
2022
+ /**
2023
+ * @hidden
2024
+ */
2025
+ const SUGGESTIONS_LAYOUT_DEFAULT_SETTINGS = 'scroll';
2056
2026
  /**
2057
2027
  * @hidden
2058
2028
  */
@@ -2105,14 +2075,16 @@ const parseMessage = (message, fields) => {
2105
2075
  id: authorId,
2106
2076
  ...(authorName && { name: authorName }),
2107
2077
  ...(authorImageUrl && { avatarUrl: authorImageUrl }),
2108
- ...(authorImageAltText && { avatarAltText: authorImageAltText })
2078
+ ...(authorImageAltText && { avatarAltText: authorImageAltText }),
2109
2079
  };
2110
2080
  }
2081
+ const timestampValue = getter(modelFields.timestampField)(message);
2082
+ const timestamp = timestampValue instanceof Date ? timestampValue : new Date(timestampValue);
2111
2083
  return {
2112
2084
  id: getter(modelFields.idField)(message),
2113
2085
  text: getter(modelFields.textField)(message),
2114
2086
  author: author,
2115
- timestamp: new Date(getter(modelFields.timestampField)(message)),
2087
+ timestamp: timestamp,
2116
2088
  status: getter(modelFields.statusField)(message),
2117
2089
  files: getter(modelFields.filesField)(message),
2118
2090
  attachments: getter(modelFields.attachmentsField)(message),
@@ -2121,9 +2093,22 @@ const parseMessage = (message, fields) => {
2121
2093
  isPinned: getter(modelFields.isPinnedField)(message),
2122
2094
  replyToId: getter(modelFields.replyToIdField)(message),
2123
2095
  isDeleted: getter(modelFields.isDeletedField)(message),
2124
- typing: getter(modelFields.typingField)(message)
2096
+ typing: getter(modelFields.typingField)(message),
2097
+ dataItem: message
2125
2098
  };
2126
2099
  };
2100
+ /**
2101
+ * @hidden
2102
+ */
2103
+ const transformActions = (actions) => {
2104
+ return actions.map(action => ({
2105
+ text: action.label,
2106
+ icon: action.icon,
2107
+ svgIcon: action.svgIcon,
2108
+ disabled: action.disabled,
2109
+ originalAction: action
2110
+ }));
2111
+ };
2127
2112
 
2128
2113
  /**
2129
2114
  * @hidden
@@ -2131,26 +2116,45 @@ const parseMessage = (message, fields) => {
2131
2116
  class ChatService {
2132
2117
  authorId;
2133
2118
  messageWidthMode;
2134
- allowMessageCollapse;
2135
2119
  messageToolbarActions = [];
2136
2120
  messageContextMenuActions = [];
2121
+ calculatedContextMenuActions = [];
2137
2122
  fileActions = [];
2138
- messageToolbarVisibility = 'hidden';
2139
2123
  toggleMessageState = false;
2140
2124
  reply;
2141
2125
  messages = [];
2142
2126
  chatElement;
2143
2127
  messageElementsMap = new Map();
2128
+ messagesContextMenu;
2129
+ activeMessage;
2130
+ activeMessageElement;
2131
+ selectOnMenuClose = false;
2132
+ active = false;
2133
+ messageFilesLayout = 'vertical';
2134
+ timestampVisibility = 'focus';
2135
+ showUsername = true;
2136
+ showAvatar = true;
2144
2137
  _enableSpeechToText = STB_DEFAULT_SETTINGS;
2145
2138
  _enableFileSelect = FILESELECT_DEFAULT_SETTINGS;
2146
2139
  _sendButtonSettings = SEND_BTN_DEFAULT_SETTINGS;
2140
+ _suggestionsLayout = SUGGESTIONS_LAYOUT_DEFAULT_SETTINGS;
2141
+ _quickActionsLayout = SUGGESTIONS_LAYOUT_DEFAULT_SETTINGS;
2142
+ _authorMessageSettings;
2143
+ _receiverMessageSettings;
2144
+ _allowMessageCollapse;
2147
2145
  subjects = {
2148
2146
  toolbarAction: new Subject(),
2149
2147
  contextMenuAction: new Subject(),
2150
2148
  fileAction: new Subject(),
2151
2149
  fileDownload: new Subject(),
2152
2150
  replyReferenceClick: new Subject(),
2153
- inputValueChange: new Subject()
2151
+ inputValueChange: new Subject(),
2152
+ contextMenuVisibilityChange: new Subject(),
2153
+ suggestionsLayoutChange: new Subject(),
2154
+ quickActionsLayoutChange: new Subject(),
2155
+ authorMessageSettingsChange: new Subject(),
2156
+ receiverMessageSettingsChange: new Subject(),
2157
+ allowMessageCollapseChange: new Subject(),
2154
2158
  };
2155
2159
  toolbarAction$ = this.subjects.toolbarAction.asObservable();
2156
2160
  contextMenuAction$ = this.subjects.contextMenuAction.asObservable();
@@ -2158,6 +2162,32 @@ class ChatService {
2158
2162
  fileDownload$ = this.subjects.fileDownload.asObservable();
2159
2163
  replyReferenceClick$ = this.subjects.replyReferenceClick.asObservable();
2160
2164
  inputValueChange$ = this.subjects.inputValueChange.asObservable();
2165
+ contextMenuVisibilityChange$ = this.subjects.contextMenuVisibilityChange.asObservable();
2166
+ suggestionsLayoutChange$ = this.subjects.suggestionsLayoutChange.asObservable();
2167
+ quickActionsLayoutChange$ = this.subjects.quickActionsLayoutChange.asObservable();
2168
+ authorMessageSettingsChange$ = this.subjects.authorMessageSettingsChange.asObservable();
2169
+ receiverMessageSettingsChange$ = this.subjects.receiverMessageSettingsChange.asObservable();
2170
+ allowMessageCollapseChange$ = this.subjects.allowMessageCollapseChange.asObservable();
2171
+ set authorMessageSettings(settings) {
2172
+ const previousSettings = this._authorMessageSettings;
2173
+ if (JSON.stringify(previousSettings) !== JSON.stringify(settings)) {
2174
+ this.updateComponentSettings('_authorMessageSettings', settings, null);
2175
+ this.emit('authorMessageSettingsChange', this._authorMessageSettings);
2176
+ }
2177
+ }
2178
+ get authorMessageSettings() {
2179
+ return this._authorMessageSettings;
2180
+ }
2181
+ set receiverMessageSettings(settings) {
2182
+ const previousSettings = this._receiverMessageSettings;
2183
+ if (JSON.stringify(previousSettings) !== JSON.stringify(settings)) {
2184
+ this.updateComponentSettings('_receiverMessageSettings', settings, null);
2185
+ this.emit('receiverMessageSettingsChange', this._receiverMessageSettings);
2186
+ }
2187
+ }
2188
+ get receiverMessageSettings() {
2189
+ return this._receiverMessageSettings;
2190
+ }
2161
2191
  set enableSpeechToText(settings) {
2162
2192
  this.updateComponentSettings('_enableSpeechToText', settings, STB_DEFAULT_SETTINGS);
2163
2193
  }
@@ -2176,6 +2206,38 @@ class ChatService {
2176
2206
  get sendButtonSettings() {
2177
2207
  return this._sendButtonSettings;
2178
2208
  }
2209
+ set suggestionsLayout(layoutMode) {
2210
+ this._suggestionsLayout = layoutMode;
2211
+ this.emit('suggestionsLayoutChange', this._suggestionsLayout);
2212
+ }
2213
+ get suggestionsLayout() {
2214
+ return this._suggestionsLayout;
2215
+ }
2216
+ set quickActionsLayout(layoutMode) {
2217
+ this._quickActionsLayout = layoutMode;
2218
+ this.emit('quickActionsLayoutChange', this._quickActionsLayout);
2219
+ }
2220
+ get quickActionsLayout() {
2221
+ return this._quickActionsLayout;
2222
+ }
2223
+ set allowMessageCollapse(value) {
2224
+ const previousValue = this._allowMessageCollapse;
2225
+ if (previousValue !== value) {
2226
+ this._allowMessageCollapse = value;
2227
+ this.emit('allowMessageCollapseChange', this._allowMessageCollapse);
2228
+ }
2229
+ }
2230
+ get allowMessageCollapse() {
2231
+ return this._allowMessageCollapse;
2232
+ }
2233
+ calculateContextMenuActions(isOwn) {
2234
+ const settings = isOwn ? this.authorMessageSettings : this.receiverMessageSettings;
2235
+ if (settings?.messageContextMenuActions) {
2236
+ this.calculatedContextMenuActions = settings.messageContextMenuActions;
2237
+ return;
2238
+ }
2239
+ this.calculatedContextMenuActions = this.messageContextMenuActions || [];
2240
+ }
2179
2241
  emit(subjectKey, value) {
2180
2242
  (this.subjects[subjectKey]).next(value);
2181
2243
  }
@@ -2197,6 +2259,11 @@ class ChatService {
2197
2259
  elementRef.nativeElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
2198
2260
  }
2199
2261
  }
2262
+ focusActiveMessageElement() {
2263
+ if (this.activeMessageElement) {
2264
+ this.activeMessageElement.element?.nativeElement?.focus();
2265
+ }
2266
+ }
2200
2267
  updateComponentSettings(property, settings, defaultSettings) {
2201
2268
  if (settings === true) {
2202
2269
  this[property] = defaultSettings;
@@ -2253,24 +2320,238 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2253
2320
  }]
2254
2321
  }], ctorParameters: () => [{ type: i0.TemplateRef }] });
2255
2322
 
2323
+ const DIRECTION_CLASSES = {
2324
+ left: 'chevron-left',
2325
+ right: 'chevron-right',
2326
+ };
2327
+ /**
2328
+ * @hidden
2329
+ */
2330
+ class ChatScrollableButtonComponent {
2331
+ host;
2332
+ renderer;
2333
+ ngZone;
2334
+ localization;
2335
+ role = 'button';
2336
+ prev = false;
2337
+ onClick = new EventEmitter();
2338
+ get scrollButtonIconClass() {
2339
+ const defaultPrevIcon = !this.localization.rtl ? DIRECTION_CLASSES.left : DIRECTION_CLASSES.right;
2340
+ const defaultNextIcon = !this.localization.rtl ? DIRECTION_CLASSES.right : DIRECTION_CLASSES.left;
2341
+ return this.prev ? defaultPrevIcon : defaultNextIcon;
2342
+ }
2343
+ get scrollButtonSVGIcon() {
2344
+ const defaultPrevSVGIcon = !this.localization.rtl ? this.chevronLeftIcon : this.chevronRightIcon;
2345
+ const defaultNextSVGIcon = !this.localization.rtl ? this.chevronRightIcon : this.chevronLeftIcon;
2346
+ return this.prev ? defaultPrevSVGIcon : defaultNextSVGIcon;
2347
+ }
2348
+ chevronLeftIcon = chevronLeftIcon;
2349
+ chevronRightIcon = chevronRightIcon;
2350
+ subs = new Subscription();
2351
+ constructor(host, renderer, ngZone, localization) {
2352
+ this.host = host;
2353
+ this.renderer = renderer;
2354
+ this.ngZone = ngZone;
2355
+ this.localization = localization;
2356
+ }
2357
+ ngAfterViewInit() {
2358
+ this.ngZone.runOutsideAngular(() => {
2359
+ this.subs.add(this.renderer.listen(this.host.nativeElement, 'click', () => this.clickHandler()));
2360
+ });
2361
+ }
2362
+ ngOnDestroy() {
2363
+ this.subs.unsubscribe();
2364
+ }
2365
+ clickHandler() {
2366
+ const buttonType = this.prev ? 'prev' : 'next';
2367
+ this.onClick.emit(buttonType);
2368
+ }
2369
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatScrollableButtonComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.NgZone }, { token: i1.LocalizationService }], target: i0.ɵɵFactoryTarget.Component });
2370
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ChatScrollableButtonComponent, isStandalone: true, selector: "[kendoChatScrollableButton]", inputs: { prev: "prev" }, outputs: { onClick: "onClick" }, host: { properties: { "attr.role": "this.role" } }, ngImport: i0, template: `
2371
+ <kendo-icon-wrapper [name]="scrollButtonIconClass" [svgIcon]="scrollButtonSVGIcon" innerCssClass="k-button-icon"> </kendo-icon-wrapper>
2372
+ `, isInline: true, dependencies: [{ kind: "component", type: IconWrapperComponent, selector: "kendo-icon-wrapper", inputs: ["name", "svgIcon", "innerCssClass", "customFontClass", "size"], exportAs: ["kendoIconWrapper"] }] });
2373
+ }
2374
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatScrollableButtonComponent, decorators: [{
2375
+ type: Component,
2376
+ args: [{
2377
+ template: `
2378
+ <kendo-icon-wrapper [name]="scrollButtonIconClass" [svgIcon]="scrollButtonSVGIcon" innerCssClass="k-button-icon"> </kendo-icon-wrapper>
2379
+ `,
2380
+ // eslint-disable-next-line @angular-eslint/component-selector
2381
+ selector: '[kendoChatScrollableButton]',
2382
+ standalone: true,
2383
+ imports: [IconWrapperComponent],
2384
+ }]
2385
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.NgZone }, { type: i1.LocalizationService }], propDecorators: { role: [{
2386
+ type: HostBinding,
2387
+ args: ['attr.role']
2388
+ }], prev: [{
2389
+ type: Input
2390
+ }], onClick: [{
2391
+ type: Output
2392
+ }] } });
2393
+
2394
+ const DEFAULT_SCROLL_BEHAVIOR = 'smooth';
2395
+ const DEFAULT_SCROLL_SPEED = 100;
2396
+ /**
2397
+ * @hidden
2398
+ */
2399
+ class SuggestionsScrollService {
2400
+ ngZone;
2401
+ localization;
2402
+ owner;
2403
+ position = 0;
2404
+ scrollButtonActiveStateChange = new Subject();
2405
+ get scrollElement() {
2406
+ return this.owner.suggestionsContainer?.nativeElement;
2407
+ }
2408
+ get scrollContainerOverflowSize() {
2409
+ if (!isDocumentAvailable()) {
2410
+ return 0;
2411
+ }
2412
+ if (!this.scrollElement) {
2413
+ return 0;
2414
+ }
2415
+ const overflowSize = Math.floor(this.scrollElement.scrollWidth - this.scrollElement.offsetWidth);
2416
+ return overflowSize < 0 ? 0 : overflowSize;
2417
+ }
2418
+ get suggestionsOverflow() {
2419
+ return this.scrollContainerOverflowSize > 0;
2420
+ }
2421
+ constructor(ngZone, localization) {
2422
+ this.ngZone = ngZone;
2423
+ this.localization = localization;
2424
+ }
2425
+ toggleScrollButtonsState() {
2426
+ const suggestedActions = this.owner;
2427
+ if (!suggestedActions?.hasScrollButtons) {
2428
+ return;
2429
+ }
2430
+ const currentPrevButtonActive = !this.isDisabled('prev');
2431
+ const currentNextButtonActive = !this.isDisabled('next');
2432
+ const defaultOffset = 1;
2433
+ const rtlDelta = this.localization.rtl ? -1 : 1;
2434
+ const calculatedPrevButtonActive = (this.position * rtlDelta) > 0 && this.scrollContainerOverflowSize > 0;
2435
+ const calculatedNextButtonActive = (this.position * rtlDelta) < this.scrollContainerOverflowSize - defaultOffset && this.scrollContainerOverflowSize > 0;
2436
+ if (calculatedPrevButtonActive !== currentPrevButtonActive) {
2437
+ this.ngZone.run(() => this.toggleButtonActiveState('prev', calculatedPrevButtonActive));
2438
+ }
2439
+ if (calculatedNextButtonActive !== currentNextButtonActive) {
2440
+ this.ngZone.run(() => this.toggleButtonActiveState('next', calculatedNextButtonActive));
2441
+ }
2442
+ }
2443
+ onScroll(e) {
2444
+ this.position = e.target.scrollLeft;
2445
+ this.toggleScrollButtonsState();
2446
+ }
2447
+ scrollSuggestions(direction) {
2448
+ this.calculateListPosition(direction, DEFAULT_SCROLL_SPEED);
2449
+ if (this.scrollElement) {
2450
+ this.scrollElement.scrollTo({ left: this.position, behavior: DEFAULT_SCROLL_BEHAVIOR });
2451
+ }
2452
+ this.toggleScrollButtonsState();
2453
+ }
2454
+ updateScrollPosition(element) {
2455
+ this.position = element.scrollLeft;
2456
+ }
2457
+ calculateListPosition(direction, scrollSpeed) {
2458
+ if (direction === 'prev') {
2459
+ if (!this.localization.rtl) {
2460
+ this.position = this.position - scrollSpeed <= 0 ? 0 : this.position - scrollSpeed;
2461
+ }
2462
+ else {
2463
+ this.position = this.position + scrollSpeed >= 0 ? 0 : this.position + scrollSpeed;
2464
+ }
2465
+ }
2466
+ else if (direction === 'next' && this.position < this.scrollContainerOverflowSize) {
2467
+ if (this.position + scrollSpeed > this.scrollContainerOverflowSize) {
2468
+ if (this.localization.rtl) {
2469
+ this.position = -this.scrollContainerOverflowSize;
2470
+ }
2471
+ else {
2472
+ this.position = this.scrollContainerOverflowSize;
2473
+ }
2474
+ return;
2475
+ }
2476
+ if (this.localization.rtl) {
2477
+ this.position -= scrollSpeed;
2478
+ }
2479
+ else {
2480
+ this.position += scrollSpeed;
2481
+ }
2482
+ }
2483
+ }
2484
+ toggleButtonActiveState(buttonType, active) {
2485
+ this.scrollButtonActiveStateChange.next({ buttonType, active });
2486
+ }
2487
+ isDisabled = (buttonType) => this.owner[`${buttonType}ScrollButton`]?.nativeElement.classList.contains('k-disabled');
2488
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SuggestionsScrollService, deps: [{ token: i0.NgZone }, { token: i1.LocalizationService }], target: i0.ɵɵFactoryTarget.Injectable });
2489
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SuggestionsScrollService });
2490
+ }
2491
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SuggestionsScrollService, decorators: [{
2492
+ type: Injectable
2493
+ }], ctorParameters: () => [{ type: i0.NgZone }, { type: i1.LocalizationService }] });
2494
+
2256
2495
  /* eslint-disable @typescript-eslint/no-explicit-any */
2257
2496
  /* eslint-disable @typescript-eslint/no-empty-function */
2258
2497
  /**
2259
2498
  * @hidden
2260
2499
  */
2261
2500
  class SuggestedActionsComponent extends ChatItem {
2501
+ chatService;
2502
+ localization;
2503
+ scrollService;
2504
+ ngZone;
2505
+ renderer;
2506
+ get defaultClass() {
2507
+ if (this.type === 'suggestion') {
2508
+ return this.chatService.suggestionsLayout === 'wrap' || this.chatService.suggestionsLayout === 'scroll';
2509
+ }
2510
+ else if (this.type === 'action') {
2511
+ return this.chatService.quickActionsLayout === 'wrap' || this.chatService.quickActionsLayout === 'scroll';
2512
+ }
2513
+ }
2514
+ get scrollableClass() {
2515
+ if (this.type === 'suggestion') {
2516
+ return this.chatService.suggestionsLayout === 'scroll';
2517
+ }
2518
+ else if (this.type === 'action') {
2519
+ return this.chatService.quickActionsLayout === 'scroll';
2520
+ }
2521
+ }
2522
+ get scrollButtonsClass() {
2523
+ if (this.type === 'suggestion') {
2524
+ return this.chatService.suggestionsLayout === 'scrollbuttons';
2525
+ }
2526
+ else if (this.type === 'action') {
2527
+ return this.chatService.quickActionsLayout === 'scrollbuttons';
2528
+ }
2529
+ }
2530
+ get role() {
2531
+ if (!this.hasScrollButtons) {
2532
+ return 'group';
2533
+ }
2534
+ return null;
2535
+ }
2262
2536
  actions;
2263
2537
  suggestions;
2264
2538
  tabbable;
2539
+ type;
2265
2540
  suggestionTemplate;
2266
2541
  dispatchAction = new EventEmitter();
2267
2542
  dispatchSuggestion = new EventEmitter();
2268
- defaultClass = true;
2269
- role = 'group';
2270
2543
  items;
2544
+ suggestionsContainer;
2545
+ prevScrollButton;
2546
+ nextScrollButton;
2271
2547
  selectedIndex = 0;
2272
2548
  activeIndex = -1;
2273
2549
  active = false;
2550
+ get hasScrollButtons() {
2551
+ return this.type === 'suggestion' ? this.chatService.suggestionsLayout === 'scrollbuttons' : this.chatService.quickActionsLayout === 'scrollbuttons';
2552
+ }
2553
+ subscriptions = new Subscription();
2554
+ resizeObserver = null;
2274
2555
  actionKeyHandlers = {
2275
2556
  [Keys.Tab]: (e) => this.changeSelectedIndex(e),
2276
2557
  [Keys.Enter]: (_, action) => this.actionClick(action),
@@ -2281,6 +2562,58 @@ class SuggestedActionsComponent extends ChatItem {
2281
2562
  [Keys.Enter]: (_, suggestion) => this.suggestionClick(suggestion),
2282
2563
  [Keys.Space]: (_, suggestion) => this.suggestionClick(suggestion),
2283
2564
  };
2565
+ constructor(chatService, localization, scrollService, ngZone, renderer) {
2566
+ super();
2567
+ this.chatService = chatService;
2568
+ this.localization = localization;
2569
+ this.scrollService = scrollService;
2570
+ this.ngZone = ngZone;
2571
+ this.renderer = renderer;
2572
+ this.scrollService.owner = this;
2573
+ }
2574
+ ngAfterViewInit() {
2575
+ const layoutChangeObservable = this.type === 'suggestion'
2576
+ ? this.chatService.suggestionsLayoutChange$
2577
+ : this.chatService.quickActionsLayoutChange$;
2578
+ this.subscriptions.add(layoutChangeObservable.subscribe((layoutMode) => {
2579
+ if (layoutMode === 'scrollbuttons') {
2580
+ this.ngZone.onStable.pipe(take(1)).subscribe(() => {
2581
+ if (this.suggestionsContainer) {
2582
+ this.scrollService.updateScrollPosition(this.suggestionsContainer.nativeElement);
2583
+ }
2584
+ this.scrollService.toggleScrollButtonsState();
2585
+ });
2586
+ }
2587
+ }));
2588
+ this.subscriptions.add(this.scrollService.scrollButtonActiveStateChange.subscribe((change) => {
2589
+ this.toggleScrollButtonState(change.buttonType, change.active);
2590
+ }));
2591
+ if (this.hasScrollButtons && this.suggestionsContainer) {
2592
+ this.ngZone.runOutsideAngular(() => {
2593
+ this.resizeObserver = new ResizeObserver(() => {
2594
+ this.ngZone.run(() => {
2595
+ this.scrollService.toggleScrollButtonsState();
2596
+ });
2597
+ });
2598
+ this.resizeObserver.observe(this.suggestionsContainer.nativeElement);
2599
+ });
2600
+ }
2601
+ if (this.hasScrollButtons) {
2602
+ this.ngZone.onStable.pipe(take(1)).subscribe(() => {
2603
+ this.scrollService.toggleScrollButtonsState();
2604
+ });
2605
+ if (this.suggestionsContainer) {
2606
+ this.scrollService.updateScrollPosition(this.suggestionsContainer.nativeElement);
2607
+ }
2608
+ }
2609
+ }
2610
+ ngOnDestroy() {
2611
+ this.subscriptions.unsubscribe();
2612
+ if (this.resizeObserver) {
2613
+ this.resizeObserver.disconnect();
2614
+ this.resizeObserver = null;
2615
+ }
2616
+ }
2284
2617
  isSelected(index) {
2285
2618
  return this.selected && this.selectedIndex === index;
2286
2619
  }
@@ -2314,67 +2647,130 @@ class SuggestedActionsComponent extends ChatItem {
2314
2647
  handler(e, suggestion);
2315
2648
  }
2316
2649
  }
2650
+ getScrollButtonTitle(direction) {
2651
+ let currentButton;
2652
+ if (this.localization.rtl) {
2653
+ currentButton = direction === 'prev' ? 'nextSuggestionsButtonTitle' : 'previousSuggestionsButtonTitle';
2654
+ }
2655
+ else {
2656
+ currentButton = direction === 'prev' ? 'previousSuggestionsButtonTitle' : 'nextSuggestionsButtonTitle';
2657
+ }
2658
+ return this.localization.get(currentButton);
2659
+ }
2660
+ onScroll(event) {
2661
+ this.scrollService.onScroll(event);
2662
+ }
2663
+ scrollSuggestions(direction) {
2664
+ this.scrollService.scrollSuggestions(direction);
2665
+ }
2317
2666
  focus() { }
2667
+ toggleScrollButtonState(buttonType, active) {
2668
+ const button = this[`${buttonType}ScrollButton`];
2669
+ if (button?.nativeElement) {
2670
+ if (active) {
2671
+ this.renderer.removeClass(button.nativeElement, 'k-disabled');
2672
+ }
2673
+ else {
2674
+ this.renderer.addClass(button.nativeElement, 'k-disabled');
2675
+ }
2676
+ }
2677
+ }
2318
2678
  changeSelectedIndex(e) {
2319
2679
  const offset = e.shiftKey ? -1 : 1;
2320
2680
  const prevIndex = this.selectedIndex;
2321
2681
  this.selectedIndex = Math.max(0, Math.min(prevIndex + offset, this.items.length - 1));
2322
2682
  }
2323
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SuggestedActionsComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
2324
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: SuggestedActionsComponent, isStandalone: true, selector: "kendo-chat-suggested-actions", inputs: { actions: "actions", suggestions: "suggestions", tabbable: "tabbable", suggestionTemplate: "suggestionTemplate" }, outputs: { dispatchAction: "dispatchAction", dispatchSuggestion: "dispatchSuggestion" }, host: { properties: { "class.k-suggestion-group": "this.defaultClass", "attr.role": "this.role" } }, providers: [{
2683
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SuggestedActionsComponent, deps: [{ token: ChatService }, { token: i1.LocalizationService }, { token: SuggestionsScrollService }, { token: i0.NgZone }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
2684
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: SuggestedActionsComponent, isStandalone: true, selector: "kendo-chat-suggested-actions", inputs: { actions: "actions", suggestions: "suggestions", tabbable: "tabbable", type: "type", suggestionTemplate: "suggestionTemplate" }, outputs: { dispatchAction: "dispatchAction", dispatchSuggestion: "dispatchSuggestion" }, host: { properties: { "class.k-suggestion-group": "this.defaultClass", "class.k-suggestion-group-scrollable": "this.scrollableClass", "class.k-suggestion-scrollwrap": "this.scrollButtonsClass", "attr.role": "this.role" } }, providers: [{
2325
2685
  provide: ChatItem,
2326
2686
  useExisting: forwardRef(() => SuggestedActionsComponent)
2327
- }], viewQueries: [{ propertyName: "items", predicate: ["item"], descendants: true }], usesInheritance: true, ngImport: i0, template: `
2328
- <ng-container *ngIf="actions">
2329
- <span
2330
- #item
2331
- *ngFor="let action of actions; index as index; first as first; last as last"
2332
- class="k-suggestion k-suggestion-primary"
2333
- role="button"
2334
- [class.k-selected]="isSelected(index)"
2335
- [class.k-focus]="isSelected(index)"
2336
- [class.k-active]="isActive(index)"
2337
- [attr.tabindex]="0"
2338
- (click)="actionClick(action, index)"
2339
- (keydown)="actionKeydown($event, action)"
2340
- (mousedown)="toggleActiveState(true, index)"
2341
- (mouseup)="toggleActiveState(false, index)"
2342
- >
2343
- {{ action.title || action.value }}
2344
- </span>
2345
- </ng-container>
2687
+ },
2688
+ SuggestionsScrollService
2689
+ ], viewQueries: [{ propertyName: "suggestionsContainer", first: true, predicate: ["suggestionsContainer"], descendants: true, read: ElementRef }, { propertyName: "prevScrollButton", first: true, predicate: ["prevScrollButton"], descendants: true, read: ElementRef }, { propertyName: "nextScrollButton", first: true, predicate: ["nextScrollButton"], descendants: true, read: ElementRef }, { propertyName: "items", predicate: ["item"], descendants: true }], usesInheritance: true, ngImport: i0, template: `
2690
+ <span
2691
+ *ngIf="hasScrollButtons"
2692
+ #prevScrollButton
2693
+ kendoChatScrollableButton
2694
+ [prev]="true"
2695
+ [title]="getScrollButtonTitle('prev')"
2696
+ class="k-button k-button-md k-button-solid k-button-solid-base k-rounded-md k-icon-button"
2697
+ (onClick)="scrollSuggestions($event)"
2698
+ >
2699
+ </span>
2700
+ <div class="k-suggestion-group" *ngIf="hasScrollButtons; else noScrollButtons"
2701
+ #suggestionsContainer
2702
+ role="group"
2703
+ (scroll)="onScroll($event)">
2704
+ <ng-container *ngTemplateOutlet="suggestionsContent"></ng-container>
2705
+ </div>
2706
+
2707
+ <ng-template #noScrollButtons>
2708
+ <ng-container *ngTemplateOutlet="suggestionsContent"></ng-container>
2709
+ </ng-template>
2346
2710
 
2347
- <ng-container *ngIf="suggestions">
2348
- <ng-container *ngIf="!suggestionTemplate?.templateRef">
2711
+ <ng-template #suggestionsContent>
2712
+ <ng-container *ngIf="actions">
2349
2713
  <span
2350
2714
  #item
2351
- *ngFor="let suggestion of suggestions; index as index; first as first; last as last"
2352
- class="k-suggestion"
2715
+ *ngFor="let action of actions; index as index; first as first; last as last"
2716
+ class="k-suggestion k-suggestion-primary"
2353
2717
  role="button"
2354
2718
  [class.k-selected]="isSelected(index)"
2355
2719
  [class.k-focus]="isSelected(index)"
2356
2720
  [class.k-active]="isActive(index)"
2357
2721
  [attr.tabindex]="0"
2358
- (click)="suggestionClick(suggestion, index)"
2359
- (keydown)="suggestionKeydown($event, suggestion)"
2722
+ (click)="actionClick(action, index)"
2723
+ (keydown)="actionKeydown($event, action)"
2360
2724
  (mousedown)="toggleActiveState(true, index)"
2361
2725
  (mouseup)="toggleActiveState(false, index)"
2362
2726
  >
2363
- {{ suggestion.text }}
2727
+ {{ action.title || action.value }}
2364
2728
  </span>
2365
2729
  </ng-container>
2366
2730
 
2367
- <ng-container *ngIf="suggestionTemplate?.templateRef">
2368
- <ng-container *ngFor="let suggestion of suggestions">
2369
- <ng-template
2370
- [ngTemplateOutlet]="suggestionTemplate.templateRef"
2371
- [ngTemplateOutletContext]="{ $implicit: suggestion }"
2731
+ <ng-container *ngIf="suggestions">
2732
+ <ng-container *ngIf="!suggestionTemplate?.templateRef">
2733
+ <span
2734
+ #item
2735
+ *ngFor="let suggestion of suggestions; index as index; first as first; last as last"
2736
+ class="k-suggestion"
2737
+ role="button"
2738
+ [class.k-selected]="isSelected(index)"
2739
+ [class.k-focus]="isSelected(index)"
2740
+ [class.k-active]="isActive(index)"
2741
+ [attr.tabindex]="0"
2742
+ (click)="suggestionClick(suggestion, index)"
2743
+ (keydown)="suggestionKeydown($event, suggestion)"
2744
+ (mousedown)="toggleActiveState(true, index)"
2745
+ (mouseup)="toggleActiveState(false, index)"
2372
2746
  >
2373
- </ng-template>
2747
+ {{ suggestion.text }}
2748
+ </span>
2749
+ </ng-container>
2750
+
2751
+ <ng-container *ngIf="suggestionTemplate?.templateRef">
2752
+ <ng-container *ngFor="let suggestion of suggestions">
2753
+ <ng-template
2754
+ [ngTemplateOutlet]="suggestionTemplate.templateRef"
2755
+ [ngTemplateOutletContext]="{ $implicit: suggestion }"
2756
+ >
2757
+ </ng-template>
2758
+ </ng-container>
2374
2759
  </ng-container>
2375
2760
  </ng-container>
2376
- </ng-container>
2377
- `, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
2761
+ </ng-template>
2762
+
2763
+ <span
2764
+ *ngIf="hasScrollButtons"
2765
+ #nextScrollButton
2766
+ kendoChatScrollableButton
2767
+ [prev]="false"
2768
+ [title]="getScrollButtonTitle('next')"
2769
+ class="k-button k-button-md k-button-solid k-button-solid-base k-rounded-md k-icon-button"
2770
+ (onClick)="scrollSuggestions($event)"
2771
+ >
2772
+ </span>
2773
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ChatScrollableButtonComponent, selector: "[kendoChatScrollableButton]", inputs: ["prev"], outputs: ["onClick"] }] });
2378
2774
  }
2379
2775
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SuggestedActionsComponent, decorators: [{
2380
2776
  type: Component,
@@ -2383,82 +2779,134 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2383
2779
  providers: [{
2384
2780
  provide: ChatItem,
2385
2781
  useExisting: forwardRef(() => SuggestedActionsComponent)
2386
- }],
2782
+ },
2783
+ SuggestionsScrollService],
2387
2784
  template: `
2388
- <ng-container *ngIf="actions">
2389
- <span
2390
- #item
2391
- *ngFor="let action of actions; index as index; first as first; last as last"
2392
- class="k-suggestion k-suggestion-primary"
2393
- role="button"
2394
- [class.k-selected]="isSelected(index)"
2395
- [class.k-focus]="isSelected(index)"
2396
- [class.k-active]="isActive(index)"
2397
- [attr.tabindex]="0"
2398
- (click)="actionClick(action, index)"
2399
- (keydown)="actionKeydown($event, action)"
2400
- (mousedown)="toggleActiveState(true, index)"
2401
- (mouseup)="toggleActiveState(false, index)"
2402
- >
2403
- {{ action.title || action.value }}
2404
- </span>
2405
- </ng-container>
2785
+ <span
2786
+ *ngIf="hasScrollButtons"
2787
+ #prevScrollButton
2788
+ kendoChatScrollableButton
2789
+ [prev]="true"
2790
+ [title]="getScrollButtonTitle('prev')"
2791
+ class="k-button k-button-md k-button-solid k-button-solid-base k-rounded-md k-icon-button"
2792
+ (onClick)="scrollSuggestions($event)"
2793
+ >
2794
+ </span>
2795
+ <div class="k-suggestion-group" *ngIf="hasScrollButtons; else noScrollButtons"
2796
+ #suggestionsContainer
2797
+ role="group"
2798
+ (scroll)="onScroll($event)">
2799
+ <ng-container *ngTemplateOutlet="suggestionsContent"></ng-container>
2800
+ </div>
2406
2801
 
2407
- <ng-container *ngIf="suggestions">
2408
- <ng-container *ngIf="!suggestionTemplate?.templateRef">
2802
+ <ng-template #noScrollButtons>
2803
+ <ng-container *ngTemplateOutlet="suggestionsContent"></ng-container>
2804
+ </ng-template>
2805
+
2806
+ <ng-template #suggestionsContent>
2807
+ <ng-container *ngIf="actions">
2409
2808
  <span
2410
2809
  #item
2411
- *ngFor="let suggestion of suggestions; index as index; first as first; last as last"
2412
- class="k-suggestion"
2810
+ *ngFor="let action of actions; index as index; first as first; last as last"
2811
+ class="k-suggestion k-suggestion-primary"
2413
2812
  role="button"
2414
2813
  [class.k-selected]="isSelected(index)"
2415
2814
  [class.k-focus]="isSelected(index)"
2416
2815
  [class.k-active]="isActive(index)"
2417
2816
  [attr.tabindex]="0"
2418
- (click)="suggestionClick(suggestion, index)"
2419
- (keydown)="suggestionKeydown($event, suggestion)"
2817
+ (click)="actionClick(action, index)"
2818
+ (keydown)="actionKeydown($event, action)"
2420
2819
  (mousedown)="toggleActiveState(true, index)"
2421
2820
  (mouseup)="toggleActiveState(false, index)"
2422
2821
  >
2423
- {{ suggestion.text }}
2822
+ {{ action.title || action.value }}
2424
2823
  </span>
2425
2824
  </ng-container>
2426
2825
 
2427
- <ng-container *ngIf="suggestionTemplate?.templateRef">
2428
- <ng-container *ngFor="let suggestion of suggestions">
2429
- <ng-template
2430
- [ngTemplateOutlet]="suggestionTemplate.templateRef"
2431
- [ngTemplateOutletContext]="{ $implicit: suggestion }"
2826
+ <ng-container *ngIf="suggestions">
2827
+ <ng-container *ngIf="!suggestionTemplate?.templateRef">
2828
+ <span
2829
+ #item
2830
+ *ngFor="let suggestion of suggestions; index as index; first as first; last as last"
2831
+ class="k-suggestion"
2832
+ role="button"
2833
+ [class.k-selected]="isSelected(index)"
2834
+ [class.k-focus]="isSelected(index)"
2835
+ [class.k-active]="isActive(index)"
2836
+ [attr.tabindex]="0"
2837
+ (click)="suggestionClick(suggestion, index)"
2838
+ (keydown)="suggestionKeydown($event, suggestion)"
2839
+ (mousedown)="toggleActiveState(true, index)"
2840
+ (mouseup)="toggleActiveState(false, index)"
2432
2841
  >
2433
- </ng-template>
2842
+ {{ suggestion.text }}
2843
+ </span>
2844
+ </ng-container>
2845
+
2846
+ <ng-container *ngIf="suggestionTemplate?.templateRef">
2847
+ <ng-container *ngFor="let suggestion of suggestions">
2848
+ <ng-template
2849
+ [ngTemplateOutlet]="suggestionTemplate.templateRef"
2850
+ [ngTemplateOutletContext]="{ $implicit: suggestion }"
2851
+ >
2852
+ </ng-template>
2853
+ </ng-container>
2434
2854
  </ng-container>
2435
2855
  </ng-container>
2436
- </ng-container>
2856
+ </ng-template>
2857
+
2858
+ <span
2859
+ *ngIf="hasScrollButtons"
2860
+ #nextScrollButton
2861
+ kendoChatScrollableButton
2862
+ [prev]="false"
2863
+ [title]="getScrollButtonTitle('next')"
2864
+ class="k-button k-button-md k-button-solid k-button-solid-base k-rounded-md k-icon-button"
2865
+ (onClick)="scrollSuggestions($event)"
2866
+ >
2867
+ </span>
2437
2868
  `,
2438
2869
  standalone: true,
2439
- imports: [NgFor, NgIf, NgTemplateOutlet]
2870
+ imports: [NgFor, NgIf, NgTemplateOutlet, ChatScrollableButtonComponent]
2440
2871
  }]
2441
- }], propDecorators: { actions: [{
2872
+ }], ctorParameters: () => [{ type: ChatService }, { type: i1.LocalizationService }, { type: SuggestionsScrollService }, { type: i0.NgZone }, { type: i0.Renderer2 }], propDecorators: { defaultClass: [{
2873
+ type: HostBinding,
2874
+ args: ['class.k-suggestion-group']
2875
+ }], scrollableClass: [{
2876
+ type: HostBinding,
2877
+ args: ['class.k-suggestion-group-scrollable']
2878
+ }], scrollButtonsClass: [{
2879
+ type: HostBinding,
2880
+ args: ['class.k-suggestion-scrollwrap']
2881
+ }], role: [{
2882
+ type: HostBinding,
2883
+ args: ['attr.role']
2884
+ }], actions: [{
2442
2885
  type: Input
2443
2886
  }], suggestions: [{
2444
2887
  type: Input
2445
2888
  }], tabbable: [{
2446
2889
  type: Input
2890
+ }], type: [{
2891
+ type: Input
2447
2892
  }], suggestionTemplate: [{
2448
2893
  type: Input
2449
2894
  }], dispatchAction: [{
2450
2895
  type: Output
2451
2896
  }], dispatchSuggestion: [{
2452
2897
  type: Output
2453
- }], defaultClass: [{
2454
- type: HostBinding,
2455
- args: ['class.k-suggestion-group']
2456
- }], role: [{
2457
- type: HostBinding,
2458
- args: ['attr.role']
2459
2898
  }], items: [{
2460
2899
  type: ViewChildren,
2461
2900
  args: ['item']
2901
+ }], suggestionsContainer: [{
2902
+ type: ViewChild,
2903
+ args: ['suggestionsContainer', { read: ElementRef, static: false }]
2904
+ }], prevScrollButton: [{
2905
+ type: ViewChild,
2906
+ args: ['prevScrollButton', { read: ElementRef }]
2907
+ }], nextScrollButton: [{
2908
+ type: ViewChild,
2909
+ args: ['nextScrollButton', { read: ElementRef }]
2462
2910
  }] } });
2463
2911
 
2464
2912
  /* eslint-disable @typescript-eslint/no-empty-function */
@@ -2499,7 +2947,7 @@ class ChatFileComponent extends ChatItem {
2499
2947
  useExisting: forwardRef(() => ChatFileComponent)
2500
2948
  }], usesInheritance: true, ngImport: i0, template: `
2501
2949
  <kendo-icon-wrapper
2502
- size="xxlarge"
2950
+ size="xlarge"
2503
2951
  [name]="fileGroupClass(chatFile.extension)"
2504
2952
  [svgIcon]="fileThumbnail(chatFile.extension)"
2505
2953
  >
@@ -2540,7 +2988,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2540
2988
  }],
2541
2989
  template: `
2542
2990
  <kendo-icon-wrapper
2543
- size="xxlarge"
2991
+ size="xlarge"
2544
2992
  [name]="fileGroupClass(chatFile.extension)"
2545
2993
  [svgIcon]="fileThumbnail(chatFile.extension)"
2546
2994
  >
@@ -2790,6 +3238,7 @@ class MessageBoxComponent {
2790
3238
  return;
2791
3239
  }
2792
3240
  const message = {
3241
+ id: guid(),
2793
3242
  text: this.inputValue,
2794
3243
  timestamp: new Date(),
2795
3244
  author: { id: this.authorId },
@@ -2927,6 +3376,7 @@ class MessageBoxComponent {
2927
3376
  *ngIf="suggestions?.length > 0"
2928
3377
  #suggestedActions
2929
3378
  [suggestions]="suggestions"
3379
+ type="suggestion"
2930
3380
  [suggestionTemplate]="suggestionTemplate"
2931
3381
  [tabbable]="true"
2932
3382
  (dispatchSuggestion)="dispatchSuggestion($event)"
@@ -3027,7 +3477,7 @@ class MessageBoxComponent {
3027
3477
  [showFileList]="false"
3028
3478
  (select)="handleFileSelect($event)"
3029
3479
  ></kendo-fileselect>
3030
- `, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: 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: "ngmodule", type: FormsModule }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: TextAreaComponent, selector: "kendo-textarea", inputs: ["focusableId", "flow", "inputAttributes", "adornmentsOrientation", "rows", "cols", "maxlength", "maxResizableRows", "tabindex", "tabIndex", "resizable", "size", "rounded", "fillMode", "showPrefixSeparator", "showSuffixSeparator"], outputs: ["focus", "blur", "valueChange"], exportAs: ["kendoTextArea"] }, { kind: "component", type: MessageReferenceComponent, selector: "chat-message-reference-content", inputs: ["message"] }, { kind: "component", type: TextAreaSuffixComponent, selector: "kendo-textarea-suffix", inputs: ["flow", "orientation"], exportAs: ["kendoTextAreaSuffix"] }, { kind: "component", type: TextAreaPrefixComponent, selector: "kendo-textarea-prefix", inputs: ["flow", "orientation"], exportAs: ["kendoTextAreaPrefix"] }, { kind: "component", type: SpeechToTextButtonComponent, selector: "button[kendoSpeechToTextButton]", inputs: ["disabled", "size", "rounded", "fillMode", "themeColor", "integrationMode", "lang", "continuous", "interimResults", "maxAlternatives"], outputs: ["start", "end", "result", "error", "click"], exportAs: ["kendoSpeechToTextButton"] }, { kind: "component", type: InputSpacerComponent, selector: "kendo-input-spacer, kendo-textbox-spacer", inputs: ["width"] }, { kind: "component", type: FileSelectComponent, selector: "kendo-fileselect", inputs: ["name"], outputs: ["valueChange"], exportAs: ["kendoFileSelect"] }, { kind: "component", type: SuggestedActionsComponent, selector: "kendo-chat-suggested-actions", inputs: ["actions", "suggestions", "tabbable", "suggestionTemplate"], outputs: ["dispatchAction", "dispatchSuggestion"] }, { kind: "component", type: ChatFileComponent, selector: "li[chatFile]", inputs: ["chatFile", "removable", "fileActions"], outputs: ["remove", "actionClick", "actionsToggle", "actionButtonClick"] }] });
3480
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: 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: "ngmodule", type: FormsModule }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: TextAreaComponent, selector: "kendo-textarea", inputs: ["focusableId", "flow", "inputAttributes", "adornmentsOrientation", "rows", "cols", "maxlength", "maxResizableRows", "tabindex", "tabIndex", "resizable", "size", "rounded", "fillMode", "showPrefixSeparator", "showSuffixSeparator"], outputs: ["focus", "blur", "valueChange"], exportAs: ["kendoTextArea"] }, { kind: "component", type: MessageReferenceComponent, selector: "chat-message-reference-content", inputs: ["message"] }, { kind: "component", type: TextAreaSuffixComponent, selector: "kendo-textarea-suffix", inputs: ["flow", "orientation"], exportAs: ["kendoTextAreaSuffix"] }, { kind: "component", type: TextAreaPrefixComponent, selector: "kendo-textarea-prefix", inputs: ["flow", "orientation"], exportAs: ["kendoTextAreaPrefix"] }, { kind: "component", type: SpeechToTextButtonComponent, selector: "button[kendoSpeechToTextButton]", inputs: ["disabled", "size", "rounded", "fillMode", "themeColor", "integrationMode", "lang", "continuous", "interimResults", "maxAlternatives"], outputs: ["start", "end", "result", "error", "click"], exportAs: ["kendoSpeechToTextButton"] }, { kind: "component", type: InputSpacerComponent, selector: "kendo-input-spacer, kendo-textbox-spacer", inputs: ["width"] }, { kind: "component", type: FileSelectComponent, selector: "kendo-fileselect", inputs: ["name"], outputs: ["valueChange"], exportAs: ["kendoFileSelect"] }, { kind: "component", type: SuggestedActionsComponent, selector: "kendo-chat-suggested-actions", inputs: ["actions", "suggestions", "tabbable", "type", "suggestionTemplate"], outputs: ["dispatchAction", "dispatchSuggestion"] }, { kind: "component", type: ChatFileComponent, selector: "li[chatFile]", inputs: ["chatFile", "removable", "fileActions"], outputs: ["remove", "actionClick", "actionsToggle", "actionButtonClick"] }] });
3031
3481
  }
3032
3482
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageBoxComponent, decorators: [{
3033
3483
  type: Component,
@@ -3038,6 +3488,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3038
3488
  *ngIf="suggestions?.length > 0"
3039
3489
  #suggestedActions
3040
3490
  [suggestions]="suggestions"
3491
+ type="suggestion"
3041
3492
  [suggestionTemplate]="suggestionTemplate"
3042
3493
  [tabbable]="true"
3043
3494
  (dispatchSuggestion)="dispatchSuggestion($event)"
@@ -3183,6 +3634,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3183
3634
  type: Output
3184
3635
  }] } });
3185
3636
 
3637
+ /**
3638
+ * Defines a template for displaying custom content inside the Chat messages.
3639
+ *
3640
+ * To define a message template, nest an `<ng-template>` tag with the `kendoChatMessageContentTemplate` directive inside the `<kendo-chat>` component.
3641
+ * The template context is set to the `message` instance.
3642
+ * For more information, refer to the article on [message templates](slug:message_templates_chat).
3643
+ *
3644
+ * @example
3645
+ * ```html
3646
+ * <kendo-chat>
3647
+ * <ng-template kendoChatMessageContentTemplate let-message>
3648
+ * <div>Message: {{ message.text }}</div>
3649
+ * </ng-template>
3650
+ * </kendo-chat>
3651
+ * ```
3652
+ */
3653
+ class MessageContentTemplateDirective {
3654
+ templateRef;
3655
+ constructor(templateRef) {
3656
+ this.templateRef = templateRef;
3657
+ }
3658
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageContentTemplateDirective, deps: [{ token: i0.TemplateRef, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
3659
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: MessageContentTemplateDirective, isStandalone: true, selector: "[kendoChatMessageContentTemplate]", ngImport: i0 });
3660
+ }
3661
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageContentTemplateDirective, decorators: [{
3662
+ type: Directive,
3663
+ args: [{
3664
+ selector: '[kendoChatMessageContentTemplate]',
3665
+ standalone: true
3666
+ }]
3667
+ }], ctorParameters: () => [{ type: i0.TemplateRef, decorators: [{
3668
+ type: Optional
3669
+ }] }] });
3670
+
3186
3671
  /**
3187
3672
  * Defines a template for displaying the chat timestamp.
3188
3673
  *
@@ -3248,6 +3733,176 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3248
3733
  }]
3249
3734
  }], ctorParameters: () => [{ type: i0.TemplateRef }] });
3250
3735
 
3736
+ /**
3737
+ * Defines a template for displaying fully custom Chat message bubbles.
3738
+ *
3739
+ * To define a message template, nest an `<ng-template>` tag with the `kendoChatMessageTemplate` directive inside the `<kendo-chat>` component.
3740
+ * The template context is set to the `message` instance.
3741
+ * For more information, refer to the article on [message templates](slug:message_templates_chat).
3742
+ *
3743
+ * @example
3744
+ * ```html
3745
+ * <kendo-chat>
3746
+ * <ng-template kendoChatMessageTemplate let-message>
3747
+ * <div>Message: {{ message.text }}</div>
3748
+ * </ng-template>
3749
+ * </kendo-chat>
3750
+ * ```
3751
+ */
3752
+ class MessageTemplateDirective {
3753
+ templateRef;
3754
+ constructor(templateRef) {
3755
+ this.templateRef = templateRef;
3756
+ }
3757
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageTemplateDirective, deps: [{ token: i0.TemplateRef, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
3758
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: MessageTemplateDirective, isStandalone: true, selector: "[kendoChatMessageTemplate]", ngImport: i0 });
3759
+ }
3760
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageTemplateDirective, decorators: [{
3761
+ type: Directive,
3762
+ args: [{
3763
+ selector: '[kendoChatMessageTemplate]',
3764
+ standalone: true
3765
+ }]
3766
+ }], ctorParameters: () => [{ type: i0.TemplateRef, decorators: [{
3767
+ type: Optional
3768
+ }] }] });
3769
+
3770
+ /**
3771
+ * Defines a template for displaying custom content inside the current user's messages in the Chat.
3772
+ *
3773
+ * To define a message template, nest an `<ng-template>` tag with the `kendoChatAuthorMessageContentTemplate` directive inside the `<kendo-chat>` component.
3774
+ * The template context is set to the `message` instance.
3775
+ * For more information, refer to the article on [message templates](slug:message_templates_chat).
3776
+ *
3777
+ * @example
3778
+ * ```html
3779
+ * <kendo-chat>
3780
+ * <ng-template kendoChatAuthorMessageContentTemplate let-message>
3781
+ * <div>Message: {{ message.text }}</div>
3782
+ * </ng-template>
3783
+ * </kendo-chat>
3784
+ * ```
3785
+ */
3786
+ class AuthorMessageContentTemplateDirective {
3787
+ templateRef;
3788
+ constructor(templateRef) {
3789
+ this.templateRef = templateRef;
3790
+ }
3791
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AuthorMessageContentTemplateDirective, deps: [{ token: i0.TemplateRef, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
3792
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: AuthorMessageContentTemplateDirective, isStandalone: true, selector: "[kendoChatAuthorMessageContentTemplate]", ngImport: i0 });
3793
+ }
3794
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AuthorMessageContentTemplateDirective, decorators: [{
3795
+ type: Directive,
3796
+ args: [{
3797
+ selector: '[kendoChatAuthorMessageContentTemplate]',
3798
+ standalone: true
3799
+ }]
3800
+ }], ctorParameters: () => [{ type: i0.TemplateRef, decorators: [{
3801
+ type: Optional
3802
+ }] }] });
3803
+
3804
+ /**
3805
+ * Defines a template for displaying custom content inside the other users' messages in the Chat.
3806
+ *
3807
+ * To define a message template, nest an `<ng-template>` tag with the `kendoChatReceiverMessageContentTemplate` directive inside the `<kendo-chat>` component.
3808
+ * The template context is set to the `message` instance.
3809
+ * For more information, refer to the article on [message templates](slug:message_templates_chat).
3810
+ *
3811
+ * @example
3812
+ * ```html
3813
+ * <kendo-chat>
3814
+ * <ng-template kendoChatReceiverMessageContentTemplate let-message>
3815
+ * <div>Message: {{ message.text }}</div>
3816
+ * </ng-template>
3817
+ * </kendo-chat>
3818
+ * ```
3819
+ */
3820
+ class ReceiverMessageContentTemplateDirective {
3821
+ templateRef;
3822
+ constructor(templateRef) {
3823
+ this.templateRef = templateRef;
3824
+ }
3825
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ReceiverMessageContentTemplateDirective, deps: [{ token: i0.TemplateRef, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
3826
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: ReceiverMessageContentTemplateDirective, isStandalone: true, selector: "[kendoChatReceiverMessageContentTemplate]", ngImport: i0 });
3827
+ }
3828
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ReceiverMessageContentTemplateDirective, decorators: [{
3829
+ type: Directive,
3830
+ args: [{
3831
+ selector: '[kendoChatReceiverMessageContentTemplate]',
3832
+ standalone: true
3833
+ }]
3834
+ }], ctorParameters: () => [{ type: i0.TemplateRef, decorators: [{
3835
+ type: Optional
3836
+ }] }] });
3837
+
3838
+ /**
3839
+ * Defines a template for displaying fully custom Chat message bubbles for the other users.
3840
+ *
3841
+ * To define a message template, nest an `<ng-template>` tag with the `kendoChatReceiverMessageTemplate` directive inside the `<kendo-chat>` component.
3842
+ * The template context is set to the `message` instance.
3843
+ * For more information, refer to the article on [message templates](slug:message_templates_chat).
3844
+ *
3845
+ * @example
3846
+ * ```html
3847
+ * <kendo-chat>
3848
+ * <ng-template kendoChatReceiverMessageTemplate let-message>
3849
+ * <div>Message: {{ message.text }}</div>
3850
+ * </ng-template>
3851
+ * </kendo-chat>
3852
+ * ```
3853
+ */
3854
+ class ReceiverMessageTemplateDirective {
3855
+ templateRef;
3856
+ constructor(templateRef) {
3857
+ this.templateRef = templateRef;
3858
+ }
3859
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ReceiverMessageTemplateDirective, deps: [{ token: i0.TemplateRef, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
3860
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: ReceiverMessageTemplateDirective, isStandalone: true, selector: "[kendoChatReceiverMessageTemplate]", ngImport: i0 });
3861
+ }
3862
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ReceiverMessageTemplateDirective, decorators: [{
3863
+ type: Directive,
3864
+ args: [{
3865
+ selector: '[kendoChatReceiverMessageTemplate]',
3866
+ standalone: true
3867
+ }]
3868
+ }], ctorParameters: () => [{ type: i0.TemplateRef, decorators: [{
3869
+ type: Optional
3870
+ }] }] });
3871
+
3872
+ /**
3873
+ * Defines a template for displaying fully custom Chat message bubbles for the current user.
3874
+ *
3875
+ * To define a message template, nest an `<ng-template>` tag with the `kendoChatAuthorMessageTemplate` directive inside the `<kendo-chat>` component.
3876
+ * The template context is set to the `message` instance.
3877
+ * For more information, refer to the article on [message templates](slug:message_templates_chat).
3878
+ *
3879
+ * @example
3880
+ * ```html
3881
+ * <kendo-chat>
3882
+ * <ng-template kendoChatAuthorMessageTemplate let-message>
3883
+ * <div>Message: {{ message.text }}</div>
3884
+ * </ng-template>
3885
+ * </kendo-chat>
3886
+ * ```
3887
+ */
3888
+ class AuthorMessageTemplateDirective {
3889
+ templateRef;
3890
+ constructor(templateRef) {
3891
+ this.templateRef = templateRef;
3892
+ }
3893
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AuthorMessageTemplateDirective, deps: [{ token: i0.TemplateRef, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
3894
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: AuthorMessageTemplateDirective, isStandalone: true, selector: "[kendoChatAuthorMessageTemplate]", ngImport: i0 });
3895
+ }
3896
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AuthorMessageTemplateDirective, decorators: [{
3897
+ type: Directive,
3898
+ args: [{
3899
+ selector: '[kendoChatAuthorMessageTemplate]',
3900
+ standalone: true
3901
+ }]
3902
+ }], ctorParameters: () => [{ type: i0.TemplateRef, decorators: [{
3903
+ type: Optional
3904
+ }] }] });
3905
+
3251
3906
  // eslint-disable no-forward-ref
3252
3907
  /**
3253
3908
  * @hidden
@@ -3257,13 +3912,23 @@ class MessageComponent extends ChatItem {
3257
3912
  intl;
3258
3913
  chatService;
3259
3914
  localization;
3260
- message;
3915
+ cdr;
3916
+ set message(value) {
3917
+ this._message = value;
3918
+ }
3919
+ get message() {
3920
+ return this._message;
3921
+ }
3261
3922
  tabbable;
3262
- template;
3923
+ authorMessageContentTemplate;
3924
+ receiverMessageContentTemplate;
3925
+ messageContentTemplate;
3926
+ authorMessageTemplate;
3927
+ receiverMessageTemplate;
3928
+ messageTemplate;
3263
3929
  statusTemplate;
3264
3930
  showMessageTime = true;
3265
3931
  authorId;
3266
- contextMenuVisibilityChange = new EventEmitter();
3267
3932
  cssClass = true;
3268
3933
  get removedClass() {
3269
3934
  return this.message.isDeleted;
@@ -3278,44 +3943,142 @@ class MessageComponent extends ChatItem {
3278
3943
  this.onExpandableKeydown(event);
3279
3944
  }
3280
3945
  selected;
3281
- active = false;
3282
- selectOnMenuClose = false;
3283
3946
  get tabIndex() {
3284
3947
  return this.tabbable ? '0' : '-1';
3285
3948
  }
3286
- onContextMenu(event) {
3287
- if (this.message.isDeleted) {
3288
- return;
3289
- }
3290
- this.active = true;
3291
- event.stopPropagation();
3292
- }
3293
3949
  expandIcon = chevronDownIcon;
3294
3950
  collapseIcon = chevronUpIcon;
3295
3951
  downloadIcon = downloadIcon;
3296
3952
  isMessageExpanded = false;
3297
- contextMenuActions = [];
3953
+ showExpandCollapseIcon = false;
3298
3954
  fileActions = [];
3955
+ toolbarActions = [];
3299
3956
  parts = [];
3300
- constructor(element, intl, chatService, localization) {
3957
+ get useCustomBubbleTemplate() {
3958
+ return !!(this.getActiveBubbleTemplate());
3959
+ }
3960
+ get useCustomContentTemplate() {
3961
+ return !!(this.getActiveContentTemplate());
3962
+ }
3963
+ get hasMessageContent() {
3964
+ return !!(this.message?.text || this.message?.files?.length > 0);
3965
+ }
3966
+ get hasFiles() {
3967
+ return this.message?.files?.length > 0;
3968
+ }
3969
+ get hasMultipleFiles() {
3970
+ return this.message?.files?.length > 1;
3971
+ }
3972
+ get isActiveMessage() {
3973
+ return this.chatService.active && this.message?.id === this.chatService.activeMessage?.id;
3974
+ }
3975
+ get isMessageExpandable() {
3976
+ const isOwn = this.isOwnMessage(this.message);
3977
+ const messageSettings = isOwn
3978
+ ? this.chatService.authorMessageSettings
3979
+ : this.chatService.receiverMessageSettings;
3980
+ if (isPresent(messageSettings?.allowMessageCollapse)) {
3981
+ return messageSettings.allowMessageCollapse;
3982
+ }
3983
+ return this.chatService.allowMessageCollapse || false;
3984
+ }
3985
+ get showToolbar() {
3986
+ if (this.message?.isDeleted) {
3987
+ return false;
3988
+ }
3989
+ const hasComponentActions = this.chatService.messageToolbarActions?.length > 0;
3990
+ const hasMessageActions = this.toolbarActions?.length > 0;
3991
+ return hasComponentActions || hasMessageActions;
3992
+ }
3993
+ subs = new Subscription();
3994
+ _message;
3995
+ constructor(element, intl, chatService, localization, cdr) {
3301
3996
  super();
3302
3997
  this.element = element;
3303
3998
  this.intl = intl;
3304
3999
  this.chatService = chatService;
3305
4000
  this.localization = localization;
4001
+ this.cdr = cdr;
3306
4002
  }
3307
4003
  ngOnInit() {
3308
- this.contextMenuActions = this.transformActions(this.chatService.messageContextMenuActions);
3309
- this.fileActions = this.transformActions(this.chatService.fileActions);
4004
+ this.fileActions = this.getFileActions();
4005
+ this.toolbarActions = this.getToolbarActions();
4006
+ const settingsChange$ = this.isOwnMessage(this.message)
4007
+ ? this.chatService.authorMessageSettingsChange$
4008
+ : this.chatService.receiverMessageSettingsChange$;
4009
+ this.subs.add(settingsChange$.subscribe(() => {
4010
+ this.fileActions = this.getFileActions();
4011
+ this.toolbarActions = this.getToolbarActions();
4012
+ setTimeout(() => {
4013
+ this.showExpandCollapseIcon = this.calculateExpandCollapseIconVisibility();
4014
+ });
4015
+ }));
4016
+ this.subs.add(this.chatService.allowMessageCollapseChange$.subscribe(() => {
4017
+ setTimeout(() => {
4018
+ this.showExpandCollapseIcon = this.calculateExpandCollapseIconVisibility();
4019
+ });
4020
+ }));
3310
4021
  if (this.message.id) {
3311
4022
  this.chatService.registerMessageElement(this.message.id, this.element);
3312
4023
  }
3313
4024
  this.parts = this.getFormattedTextParts(this.message.text);
3314
4025
  }
4026
+ ngAfterViewInit() {
4027
+ this.showExpandCollapseIcon = this.calculateExpandCollapseIconVisibility();
4028
+ this.cdr.detectChanges();
4029
+ }
3315
4030
  ngOnDestroy() {
3316
4031
  if (this.message.id) {
3317
4032
  this.chatService.unregisterMessageElement(this.message.id);
3318
4033
  }
4034
+ this.subs.unsubscribe();
4035
+ }
4036
+ calculateExpandCollapseIconVisibility() {
4037
+ if (this.isMessageExpanded) {
4038
+ return true;
4039
+ }
4040
+ const bubbleContent = this.element.nativeElement.querySelector('.k-bubble-content');
4041
+ if (!bubbleContent) {
4042
+ return false;
4043
+ }
4044
+ const hasVerticalOverflow = bubbleContent.scrollHeight > bubbleContent.clientHeight;
4045
+ const hasHorizontalOverflow = bubbleContent.scrollWidth > bubbleContent.clientWidth;
4046
+ if (this.useCustomContentTemplate) {
4047
+ return hasVerticalOverflow || hasHorizontalOverflow;
4048
+ }
4049
+ const messageText = this.element.nativeElement.querySelector('.k-chat-bubble-text');
4050
+ const hasTextOverflow = messageText?.scrollWidth > messageText?.clientWidth;
4051
+ return hasTextOverflow || hasVerticalOverflow || hasHorizontalOverflow;
4052
+ }
4053
+ getActiveBubbleTemplate() {
4054
+ const isOwn = this.isOwnMessage(this.message);
4055
+ if (isOwn && this.authorMessageTemplate) {
4056
+ return this.authorMessageTemplate;
4057
+ }
4058
+ if (!isOwn && this.receiverMessageTemplate) {
4059
+ return this.receiverMessageTemplate;
4060
+ }
4061
+ if (this.messageTemplate) {
4062
+ return this.messageTemplate;
4063
+ }
4064
+ return null;
4065
+ }
4066
+ getActiveContentTemplate() {
4067
+ const isOwn = this.isOwnMessage(this.message);
4068
+ if (isOwn && this.authorMessageContentTemplate) {
4069
+ return this.authorMessageContentTemplate;
4070
+ }
4071
+ if (!isOwn && this.receiverMessageContentTemplate) {
4072
+ return this.receiverMessageContentTemplate;
4073
+ }
4074
+ if (this.messageContentTemplate) {
4075
+ return this.messageContentTemplate;
4076
+ }
4077
+ return null;
4078
+ }
4079
+ getDeletedMessageText() {
4080
+ const isOwn = this.isOwnMessage(this.message);
4081
+ return isOwn ? this.textFor('deletedMessageSenderText') : this.textFor('deletedMessageReceiverText');
3319
4082
  }
3320
4083
  textFor(key) {
3321
4084
  return this.localization.get(key);
@@ -3343,66 +4106,19 @@ class MessageComponent extends ChatItem {
3343
4106
  this.toggleMessageState(event);
3344
4107
  }
3345
4108
  }
3346
- isToolbarVisible() {
3347
- if (!this.chatService.messageToolbarActions || this.chatService.messageToolbarActions.length === 0 || this.message.isDeleted) {
3348
- return false;
3349
- }
3350
- return this.chatService.messageToolbarVisibility === 'always';
3351
- }
3352
4109
  onToolbarAction(event, action, message) {
3353
4110
  event.stopImmediatePropagation();
3354
4111
  this.chatService.emit('toolbarAction', { action, message });
3355
4112
  }
3356
- onContextMenuAction(action) {
3357
- if (action.id === 'reply') {
3358
- this.chatService.reply = this.message;
3359
- }
3360
- if (action.id === 'copy') {
3361
- navigator.clipboard.writeText(this.message.text);
3362
- }
3363
- this.chatService.emit('contextMenuAction', { action, message: this.message });
3364
- }
3365
4113
  onFileAction(action, file) {
3366
4114
  if (action.originalAction.id === 'download') {
3367
4115
  this.chatService.emit('fileDownload', { files: [file], message: this.message });
3368
4116
  }
3369
4117
  this.chatService.emit('fileAction', { action: action.originalAction, file });
3370
4118
  }
3371
- transformActions(actions) {
3372
- return actions.map(action => ({
3373
- text: action.label,
3374
- icon: action.icon,
3375
- svgIcon: action.svgIcon,
3376
- disabled: action.disabled,
3377
- originalAction: action
3378
- }));
3379
- }
3380
4119
  getMessageById(id) {
3381
4120
  return this.chatService.getMessageById(id);
3382
4121
  }
3383
- // processes the message text to extract parts that are either plain text or links
3384
- // this allows us not to render an additional wrapper element for the text
3385
- getFormattedTextParts(text) {
3386
- if (!text) {
3387
- return [];
3388
- }
3389
- const parts = [];
3390
- const urlMatches = Array.from(text.matchAll(URL_REGEX));
3391
- let lastIndex = 0;
3392
- for (const match of urlMatches) {
3393
- const url = match[1];
3394
- const matchStart = match.index;
3395
- if (matchStart > lastIndex) {
3396
- parts.push({ type: 'text', content: text.substring(lastIndex, matchStart) });
3397
- }
3398
- parts.push({ type: 'link', content: url, href: url });
3399
- lastIndex = matchStart + match[0].length;
3400
- }
3401
- if (lastIndex < text.length) {
3402
- parts.push({ type: 'text', content: text.substring(lastIndex) });
3403
- }
3404
- return parts;
3405
- }
3406
4122
  onReplyReferenceClick(event, replyToId) {
3407
4123
  event.stopPropagation();
3408
4124
  this.chatService.emit('replyReferenceClick', replyToId);
@@ -3414,9 +4130,9 @@ class MessageComponent extends ChatItem {
3414
4130
  this.onActionButtonClick(originalEvent);
3415
4131
  }
3416
4132
  }
3417
- this.active = false;
3418
- this.contextMenuVisibilityChange.emit(false);
3419
- if (this.selectOnMenuClose) {
4133
+ this.chatService.active = false;
4134
+ this.chatService.emit('contextMenuVisibilityChange', false);
4135
+ if (this.chatService.selectOnMenuClose) {
3420
4136
  this.selected = true;
3421
4137
  this.focus();
3422
4138
  }
@@ -3425,16 +4141,16 @@ class MessageComponent extends ChatItem {
3425
4141
  const clickOutsideMessage = event instanceof MouseEvent && !event.target?.closest('.k-chat-bubble');
3426
4142
  const menuItemClick = event instanceof MouseEvent && event.target?.closest(MENU_ITEM_SELECTOR);
3427
4143
  if (clickOutsideMessage && !menuItemClick) {
3428
- this.selectOnMenuClose = false;
4144
+ this.chatService.selectOnMenuClose = false;
3429
4145
  }
3430
4146
  }
3431
4147
  handleMenuOpen() {
3432
- this.selectOnMenuClose = this.selected;
3433
- this.contextMenuVisibilityChange.emit(true);
4148
+ this.chatService.selectOnMenuClose = this.selected;
4149
+ this.chatService.emit('contextMenuVisibilityChange', true);
3434
4150
  }
3435
4151
  onActionPopupChange(expanded) {
3436
4152
  if (expanded) {
3437
- this.active = true;
4153
+ this.chatService.active = true;
3438
4154
  this.handleMenuOpen();
3439
4155
  }
3440
4156
  else {
@@ -3444,124 +4160,211 @@ class MessageComponent extends ChatItem {
3444
4160
  isOwnMessage(msg) {
3445
4161
  return isAuthor(this.authorId, msg);
3446
4162
  }
3447
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageComponent, deps: [{ token: i0.ElementRef }, { token: i1$1.IntlService }, { token: ChatService }, { token: i1.LocalizationService }], target: i0.ɵɵFactoryTarget.Component });
3448
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MessageComponent, isStandalone: true, selector: "kendo-chat-message", inputs: { message: "message", tabbable: "tabbable", template: "template", statusTemplate: "statusTemplate", showMessageTime: "showMessageTime", authorId: "authorId" }, outputs: { contextMenuVisibilityChange: "contextMenuVisibilityChange" }, host: { listeners: { "keydown": "onKeyDown($event)", "contextmenu": "onContextMenu($event)" }, properties: { "class.k-message": "this.cssClass", "class.k-message-removed": "this.removedClass", "attr.tabIndex": "this.tabIndex" } }, providers: [
4163
+ getFormattedTextParts(text) {
4164
+ if (!text) {
4165
+ return [];
4166
+ }
4167
+ const parts = [];
4168
+ const urlMatches = Array.from(text.matchAll(URL_REGEX));
4169
+ let lastIndex = 0;
4170
+ for (const match of urlMatches) {
4171
+ const url = match[1];
4172
+ const matchStart = match.index;
4173
+ if (!isPresent(matchStart)) {
4174
+ continue;
4175
+ }
4176
+ if (matchStart > lastIndex) {
4177
+ parts.push({ type: 'text', content: text.substring(lastIndex, matchStart) });
4178
+ }
4179
+ parts.push({ type: 'link', content: url, href: url });
4180
+ lastIndex = matchStart + match[0].length;
4181
+ }
4182
+ if (lastIndex < text.length) {
4183
+ parts.push({ type: 'text', content: text.substring(lastIndex) });
4184
+ }
4185
+ return parts;
4186
+ }
4187
+ getMessageSettings() {
4188
+ return this.isOwnMessage(this.message)
4189
+ ? this.chatService.authorMessageSettings
4190
+ : this.chatService.receiverMessageSettings;
4191
+ }
4192
+ getToolbarActions() {
4193
+ const messageSettings = this.getMessageSettings();
4194
+ return messageSettings?.messageToolbarActions?.length
4195
+ ? messageSettings.messageToolbarActions
4196
+ : this.chatService.messageToolbarActions || [];
4197
+ }
4198
+ getFileActions() {
4199
+ const messageSettings = this.getMessageSettings();
4200
+ const actions = messageSettings?.fileActions?.length
4201
+ ? messageSettings.fileActions
4202
+ : this.chatService.fileActions || [];
4203
+ return transformActions(actions);
4204
+ }
4205
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageComponent, deps: [{ token: i0.ElementRef }, { token: i1$1.IntlService }, { token: ChatService }, { token: i1.LocalizationService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
4206
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MessageComponent, isStandalone: true, selector: "kendo-chat-message", inputs: { message: "message", tabbable: "tabbable", authorMessageContentTemplate: "authorMessageContentTemplate", receiverMessageContentTemplate: "receiverMessageContentTemplate", messageContentTemplate: "messageContentTemplate", authorMessageTemplate: "authorMessageTemplate", receiverMessageTemplate: "receiverMessageTemplate", messageTemplate: "messageTemplate", statusTemplate: "statusTemplate", showMessageTime: "showMessageTime", authorId: "authorId" }, host: { listeners: { "keydown": "onKeyDown($event)" }, properties: { "class.k-message": "this.cssClass", "class.k-message-removed": "this.removedClass", "attr.tabIndex": "this.tabIndex" } }, providers: [
3449
4207
  {
3450
4208
  provide: ChatItem,
3451
4209
  useExisting: forwardRef(() => MessageComponent)
3452
4210
  }
3453
4211
  ], usesInheritance: true, ngImport: i0, template: `
3454
- <time
3455
- [attr.aria-hidden]="!selected"
3456
- class="k-message-time"
3457
- *ngIf="message.timestamp"
3458
- >
3459
- {{ formatTimeStamp(message.timestamp) }}
3460
- </time>
3461
-
3462
- <ng-container *ngIf="!message.typing; else typing">
3463
- <div
3464
- class="k-chat-bubble k-bubble"
3465
- *ngIf="template"
3466
- [attr.tabindex]="0"
3467
- [ngClass]="{
3468
- 'k-selected': selected,
3469
- 'k-focus': selected,
3470
- 'k-active': active
3471
- }"
3472
- >
3473
- <div class="k-bubble-content">
3474
- <ng-container
3475
- *ngTemplateOutlet="template.templateRef; context: { $implicit: message };"
3476
- >
3477
- </ng-container>
3478
- </div>
3479
- </div>
4212
+ <ng-container *ngIf="useCustomBubbleTemplate">
4213
+ <ng-container *ngTemplateOutlet="getActiveBubbleTemplate()?.templateRef; context: { $implicit: message };"></ng-container>
4214
+ </ng-container>
3480
4215
 
3481
- <div
3482
- class="k-chat-bubble k-bubble"
3483
- *ngIf="!template && (message.text || message.files)"
3484
- [attr.tabindex]="0"
3485
- [ngClass]="{
3486
- 'k-bubble-expandable': chatService.allowMessageCollapse,
3487
- 'k-expanded': isMessageExpanded,
3488
- 'k-selected': selected,
3489
- 'k-focus': selected,
3490
- 'k-active': active
3491
- }"
4216
+ <ng-container *ngIf="!useCustomBubbleTemplate">
4217
+ <time
4218
+ [attr.aria-hidden]="!selected"
4219
+ class="k-message-time"
4220
+ *ngIf="chatService.timestampVisibility === 'focus' && message.timestamp"
3492
4221
  >
3493
- <div class="k-bubble-content">
3494
- <span class="k-chat-bubble-text" *ngIf="message.text || message.isDeleted">
3495
- <div class="k-message-reference k-message-reference-receiver" *ngIf="message.replyToId && !message.isDeleted" (click)="onReplyReferenceClick($event, message.replyToId)">
3496
- <chat-message-reference-content [message]="getMessageById(message.replyToId)"></chat-message-reference-content>
3497
- </div>
3498
- <ng-container *ngIf="message.isDeleted && isOwnMessage(message)">{{ textFor('deletedMessageSenderText') }}</ng-container>
3499
- <ng-container *ngIf="message.isDeleted && !isOwnMessage(message)">{{ textFor('deletedMessageReceiverText') }}</ng-container>
3500
- <ng-container *ngIf="!message.isDeleted && parts.length">
3501
- <ng-container *ngFor="let part of parts">
3502
- <ng-container *ngIf="part.type === 'text'">{{part.content}}</ng-container>
3503
- <a *ngIf="part.type === 'link'" [href]="part.href" target="_blank">{{part.content}}</a>
3504
- </ng-container>
3505
- </ng-container>
3506
- </span>
3507
- <ul class="k-chat-file-wrapper" *ngIf="message.files && message.files.length > 0">
3508
- <li
3509
- *ngFor="let file of message.files"
3510
- class="k-chat-file"
3511
- [chatFile]="file"
3512
- [fileActions]="fileActions"
3513
- (actionClick)="onFileAction($event, file)"
3514
- (actionsToggle)="onActionPopupChange($event)"
3515
- (actionButtonClick)="onActionButtonClick($event)"
3516
- ></li>
3517
- </ul>
3518
- <div class="k-chat-download-button-wrapper" *ngIf="message.files?.length > 1">
3519
- <button
3520
- kendoButton
3521
- class="k-chat-download-button"
3522
- fillMode="flat"
3523
- icon="download"
3524
- [svgIcon]="downloadIcon"
3525
- (click)="onDownloadAll()"
3526
- >{{ textFor('downloadAllFilesText') }}</button>
4222
+ {{ formatTimeStamp(message.timestamp) }}
4223
+ </time>
4224
+
4225
+ <ng-container *ngIf="message.typing">
4226
+ <div class="k-chat-bubble k-bubble">
4227
+ <div class="k-typing-indicator" [attr.tabindex]="'-1'">
4228
+ <span></span>
4229
+ <span></span>
4230
+ <span></span>
3527
4231
  </div>
3528
4232
  </div>
3529
- <span
3530
- class="k-bubble-expandable-indicator"
3531
- *ngIf="chatService.allowMessageCollapse"
3532
- [attr.role]="'button'"
3533
- [attr.title]="isMessageExpanded ? textFor('collapseTitle') : textFor('expandTitle')"
3534
- (mousedown)="chatService.toggleMessageState = true"
3535
- (click)="toggleMessageState($event)"
4233
+ </ng-container>
4234
+
4235
+ <ng-container *ngIf="!message.typing">
4236
+ <div
4237
+ class="k-chat-bubble k-bubble"
4238
+ *ngIf="useCustomContentTemplate"
4239
+ [attr.tabindex]="0"
4240
+ [ngClass]="{
4241
+ 'k-bubble-expandable': isMessageExpandable,
4242
+ 'k-expanded': isMessageExpanded,
4243
+ 'k-selected': selected,
4244
+ 'k-focus': selected,
4245
+ 'k-active': isActiveMessage
4246
+ }"
3536
4247
  >
3537
- <kendo-icon-wrapper
3538
- [name]="isMessageExpanded ? 'chevron-up' : 'chevron-down'"
3539
- [svgIcon]="isMessageExpanded ? collapseIcon : expandIcon"
4248
+ <div class="k-bubble-content">
4249
+ <ng-container *ngTemplateOutlet="getActiveContentTemplate()?.templateRef; context: { $implicit: message };"></ng-container>
4250
+ </div>
4251
+ <span
4252
+ class="k-bubble-expandable-indicator"
4253
+ *ngIf="isMessageExpandable && showExpandCollapseIcon"
4254
+ [attr.tabindex]="'0'"
4255
+ [attr.role]="'button'"
4256
+ [attr.title]="isMessageExpanded ? textFor('collapseTitle') : textFor('expandTitle')"
4257
+ (mousedown)="chatService.toggleMessageState = true"
4258
+ (click)="toggleMessageState($event)"
3540
4259
  >
3541
- </kendo-icon-wrapper>
3542
- </span>
3543
- </div>
3544
- </ng-container>
4260
+ <kendo-icon-wrapper
4261
+ [name]="isMessageExpanded ? 'chevron-up' : 'chevron-down'"
4262
+ [svgIcon]="isMessageExpanded ? collapseIcon : expandIcon"
4263
+ >
4264
+ </kendo-icon-wrapper>
4265
+ </span>
4266
+ </div>
3545
4267
 
3546
- <span
3547
- class="k-message-status"
3548
- *ngIf="message.status"
3549
- >
3550
- <ng-container *ngIf="statusTemplate?.templateRef">
3551
- <ng-template
3552
- [ngTemplateOutlet]="statusTemplate.templateRef"
3553
- [ngTemplateOutletContext]="{ $implicit: message.status, message }"
4268
+ <div
4269
+ class="k-chat-bubble k-bubble"
4270
+ *ngIf="!useCustomContentTemplate && hasMessageContent"
4271
+ [attr.tabindex]="0"
4272
+ [ngClass]="{
4273
+ 'k-bubble-expandable': isMessageExpandable,
4274
+ 'k-expanded': isMessageExpanded,
4275
+ 'k-selected': selected,
4276
+ 'k-focus': selected,
4277
+ 'k-active': isActiveMessage
4278
+ }"
3554
4279
  >
3555
- </ng-template>
3556
- </ng-container>
3557
- <ng-container *ngIf="!statusTemplate?.templateRef">
3558
- {{ message.status }}
4280
+ <div class="k-bubble-content">
4281
+ <ng-container *ngIf="message.text || message.isDeleted">
4282
+ <div
4283
+ class="k-message-reference k-message-reference-receiver"
4284
+ *ngIf="message.replyToId && !message.isDeleted"
4285
+ (click)="onReplyReferenceClick($event, message.replyToId)"
4286
+ >
4287
+ <chat-message-reference-content [message]="getMessageById(message.replyToId)"></chat-message-reference-content>
4288
+ </div>
4289
+
4290
+ <span class="k-chat-bubble-text" *ngIf="message.isDeleted">
4291
+ {{ getDeletedMessageText() }}
4292
+ </span>
4293
+
4294
+ <span class="k-chat-bubble-text" *ngIf="!message.isDeleted && parts.length > 0">
4295
+ <ng-container *ngFor="let part of parts">
4296
+ <ng-container *ngIf="part.type === 'text'">{{part.content}}</ng-container>
4297
+ <a *ngIf="part.type === 'link'" [href]="part.href" target="_blank">{{part.content}}</a>
4298
+ </ng-container>
4299
+ </span>
4300
+ </ng-container>
4301
+
4302
+ <ul
4303
+ class="k-chat-file-wrapper"
4304
+ *ngIf="hasFiles && !message.isDeleted"
4305
+ [ngClass]="{
4306
+ 'k-chat-files-wrap': chatService.messageFilesLayout === 'wrap',
4307
+ 'k-chat-files-horizontal': chatService.messageFilesLayout === 'horizontal'
4308
+ }"
4309
+ >
4310
+ <li
4311
+ *ngFor="let file of message.files"
4312
+ class="k-chat-file"
4313
+ [chatFile]="file"
4314
+ [fileActions]="fileActions"
4315
+ (actionClick)="onFileAction($event, file)"
4316
+ (actionsToggle)="onActionPopupChange($event)"
4317
+ (actionButtonClick)="onActionButtonClick($event)"
4318
+ ></li>
4319
+ </ul>
4320
+
4321
+ <div class="k-chat-download-button-wrapper" *ngIf="hasMultipleFiles && !message.isDeleted">
4322
+ <button
4323
+ kendoButton
4324
+ class="k-chat-download-button"
4325
+ fillMode="flat"
4326
+ icon="download"
4327
+ [svgIcon]="downloadIcon"
4328
+ [attr.title]="textFor('downloadAllFilesText')"
4329
+ (click)="onDownloadAll()"
4330
+ >{{ textFor('downloadAllFilesText') }}</button>
4331
+ </div>
4332
+ </div>
4333
+
4334
+ <span
4335
+ class="k-bubble-expandable-indicator"
4336
+ *ngIf="isMessageExpandable && showExpandCollapseIcon"
4337
+ [attr.tabindex]="'0'"
4338
+ [attr.role]="'button'"
4339
+ [attr.title]="isMessageExpanded ? textFor('collapseTitle') : textFor('expandTitle')"
4340
+ (mousedown)="chatService.toggleMessageState = true"
4341
+ (click)="toggleMessageState($event)"
4342
+ >
4343
+ <kendo-icon-wrapper
4344
+ [name]="isMessageExpanded ? 'chevron-up' : 'chevron-down'"
4345
+ [svgIcon]="isMessageExpanded ? collapseIcon : expandIcon"
4346
+ >
4347
+ </kendo-icon-wrapper>
4348
+ </span>
4349
+ </div>
3559
4350
  </ng-container>
3560
- </span>
3561
4351
 
3562
- <kendo-toolbar *ngIf="isToolbarVisible()" class="k-chat-message-toolbar" fillMode="flat">
4352
+ <span class="k-message-status" *ngIf="message.status">
4353
+ <ng-container *ngIf="statusTemplate?.templateRef">
4354
+ <ng-template
4355
+ [ngTemplateOutlet]="statusTemplate.templateRef"
4356
+ [ngTemplateOutletContext]="{ $implicit: message.status, message }"
4357
+ >
4358
+ </ng-template>
4359
+ </ng-container>
4360
+ <ng-container *ngIf="!statusTemplate?.templateRef">
4361
+ {{ message.status }}
4362
+ </ng-container>
4363
+ </span>
4364
+ </ng-container>
4365
+ <kendo-toolbar *ngIf="showToolbar" class="k-chat-message-toolbar" fillMode="flat">
3563
4366
  <kendo-toolbar-button
3564
- *ngFor="let action of chatService.messageToolbarActions"
4367
+ *ngFor="let action of toolbarActions"
3565
4368
  fillMode="flat"
3566
4369
  [icon]="action.icon"
3567
4370
  [svgIcon]="action.svgIcon"
@@ -3571,28 +4374,7 @@ class MessageComponent extends ChatItem {
3571
4374
  >
3572
4375
  </kendo-toolbar-button>
3573
4376
  </kendo-toolbar>
3574
-
3575
- <ng-template #typing>
3576
- <div class="k-chat-bubble k-bubble">
3577
- <div class="k-typing-indicator">
3578
- <span></span>
3579
- <span></span>
3580
- <span></span>
3581
- </div>
3582
- </div>
3583
- </ng-template>
3584
-
3585
- <kendo-contextmenu
3586
- *ngIf="!message.isDeleted"
3587
- [target]="element"
3588
- [items]="contextMenuActions"
3589
- (popupOpen)="handleMenuOpen()"
3590
- (popupClose)="handleMenuClose($event)"
3591
- (select)="onContextMenuAction($event.item.originalAction)"
3592
- [popupAlign]="{ horizontal: 'right', vertical: 'top' }"
3593
- [collision]="{ horizontal: 'flip', vertical: 'flip'}"
3594
- ></kendo-contextmenu>
3595
- `, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { 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: i2.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: ChatFileComponent, selector: "li[chatFile]", inputs: ["chatFile", "removable", "fileActions"], outputs: ["remove", "actionClick", "actionsToggle", "actionButtonClick"] }, { 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"] }, { kind: "component", type: ToolBarComponent, selector: "kendo-toolbar", inputs: ["overflow", "resizable", "popupSettings", "fillMode", "tabindex", "size", "tabIndex", "showIcon", "showText"], outputs: ["open", "close"], exportAs: ["kendoToolBar"] }, { kind: "component", type: ToolBarButtonComponent, selector: "kendo-toolbar-button", inputs: ["showText", "showIcon", "text", "style", "className", "title", "disabled", "toggleable", "look", "togglable", "selected", "fillMode", "rounded", "themeColor", "icon", "iconClass", "svgIcon", "imageUrl"], outputs: ["click", "pointerdown", "selectedChange"], exportAs: ["kendoToolBarButton"] }, { kind: "component", type: MessageReferenceComponent, selector: "chat-message-reference-content", inputs: ["message"] }] });
4377
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { 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: i2.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: ChatFileComponent, selector: "li[chatFile]", inputs: ["chatFile", "removable", "fileActions"], outputs: ["remove", "actionClick", "actionsToggle", "actionButtonClick"] }, { kind: "component", type: ToolBarComponent, selector: "kendo-toolbar", inputs: ["overflow", "resizable", "popupSettings", "fillMode", "tabindex", "size", "tabIndex", "showIcon", "showText"], outputs: ["open", "close"], exportAs: ["kendoToolBar"] }, { kind: "component", type: ToolBarButtonComponent, selector: "kendo-toolbar-button", inputs: ["showText", "showIcon", "text", "style", "className", "title", "disabled", "toggleable", "look", "togglable", "selected", "fillMode", "rounded", "themeColor", "icon", "iconClass", "svgIcon", "imageUrl"], outputs: ["click", "pointerdown", "selectedChange"], exportAs: ["kendoToolBarButton"] }, { kind: "component", type: MessageReferenceComponent, selector: "chat-message-reference-content", inputs: ["message"] }] });
3596
4378
  }
3597
4379
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageComponent, decorators: [{
3598
4380
  type: Component,
@@ -3605,117 +4387,162 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3605
4387
  }
3606
4388
  ],
3607
4389
  template: `
3608
- <time
3609
- [attr.aria-hidden]="!selected"
3610
- class="k-message-time"
3611
- *ngIf="message.timestamp"
3612
- >
3613
- {{ formatTimeStamp(message.timestamp) }}
3614
- </time>
3615
-
3616
- <ng-container *ngIf="!message.typing; else typing">
3617
- <div
3618
- class="k-chat-bubble k-bubble"
3619
- *ngIf="template"
3620
- [attr.tabindex]="0"
3621
- [ngClass]="{
3622
- 'k-selected': selected,
3623
- 'k-focus': selected,
3624
- 'k-active': active
3625
- }"
3626
- >
3627
- <div class="k-bubble-content">
3628
- <ng-container
3629
- *ngTemplateOutlet="template.templateRef; context: { $implicit: message };"
3630
- >
3631
- </ng-container>
3632
- </div>
3633
- </div>
4390
+ <ng-container *ngIf="useCustomBubbleTemplate">
4391
+ <ng-container *ngTemplateOutlet="getActiveBubbleTemplate()?.templateRef; context: { $implicit: message };"></ng-container>
4392
+ </ng-container>
3634
4393
 
3635
- <div
3636
- class="k-chat-bubble k-bubble"
3637
- *ngIf="!template && (message.text || message.files)"
3638
- [attr.tabindex]="0"
3639
- [ngClass]="{
3640
- 'k-bubble-expandable': chatService.allowMessageCollapse,
3641
- 'k-expanded': isMessageExpanded,
3642
- 'k-selected': selected,
3643
- 'k-focus': selected,
3644
- 'k-active': active
3645
- }"
4394
+ <ng-container *ngIf="!useCustomBubbleTemplate">
4395
+ <time
4396
+ [attr.aria-hidden]="!selected"
4397
+ class="k-message-time"
4398
+ *ngIf="chatService.timestampVisibility === 'focus' && message.timestamp"
3646
4399
  >
3647
- <div class="k-bubble-content">
3648
- <span class="k-chat-bubble-text" *ngIf="message.text || message.isDeleted">
3649
- <div class="k-message-reference k-message-reference-receiver" *ngIf="message.replyToId && !message.isDeleted" (click)="onReplyReferenceClick($event, message.replyToId)">
3650
- <chat-message-reference-content [message]="getMessageById(message.replyToId)"></chat-message-reference-content>
3651
- </div>
3652
- <ng-container *ngIf="message.isDeleted && isOwnMessage(message)">{{ textFor('deletedMessageSenderText') }}</ng-container>
3653
- <ng-container *ngIf="message.isDeleted && !isOwnMessage(message)">{{ textFor('deletedMessageReceiverText') }}</ng-container>
3654
- <ng-container *ngIf="!message.isDeleted && parts.length">
3655
- <ng-container *ngFor="let part of parts">
3656
- <ng-container *ngIf="part.type === 'text'">{{part.content}}</ng-container>
3657
- <a *ngIf="part.type === 'link'" [href]="part.href" target="_blank">{{part.content}}</a>
3658
- </ng-container>
3659
- </ng-container>
3660
- </span>
3661
- <ul class="k-chat-file-wrapper" *ngIf="message.files && message.files.length > 0">
3662
- <li
3663
- *ngFor="let file of message.files"
3664
- class="k-chat-file"
3665
- [chatFile]="file"
3666
- [fileActions]="fileActions"
3667
- (actionClick)="onFileAction($event, file)"
3668
- (actionsToggle)="onActionPopupChange($event)"
3669
- (actionButtonClick)="onActionButtonClick($event)"
3670
- ></li>
3671
- </ul>
3672
- <div class="k-chat-download-button-wrapper" *ngIf="message.files?.length > 1">
3673
- <button
3674
- kendoButton
3675
- class="k-chat-download-button"
3676
- fillMode="flat"
3677
- icon="download"
3678
- [svgIcon]="downloadIcon"
3679
- (click)="onDownloadAll()"
3680
- >{{ textFor('downloadAllFilesText') }}</button>
4400
+ {{ formatTimeStamp(message.timestamp) }}
4401
+ </time>
4402
+
4403
+ <ng-container *ngIf="message.typing">
4404
+ <div class="k-chat-bubble k-bubble">
4405
+ <div class="k-typing-indicator" [attr.tabindex]="'-1'">
4406
+ <span></span>
4407
+ <span></span>
4408
+ <span></span>
3681
4409
  </div>
3682
4410
  </div>
3683
- <span
3684
- class="k-bubble-expandable-indicator"
3685
- *ngIf="chatService.allowMessageCollapse"
3686
- [attr.role]="'button'"
3687
- [attr.title]="isMessageExpanded ? textFor('collapseTitle') : textFor('expandTitle')"
3688
- (mousedown)="chatService.toggleMessageState = true"
3689
- (click)="toggleMessageState($event)"
4411
+ </ng-container>
4412
+
4413
+ <ng-container *ngIf="!message.typing">
4414
+ <div
4415
+ class="k-chat-bubble k-bubble"
4416
+ *ngIf="useCustomContentTemplate"
4417
+ [attr.tabindex]="0"
4418
+ [ngClass]="{
4419
+ 'k-bubble-expandable': isMessageExpandable,
4420
+ 'k-expanded': isMessageExpanded,
4421
+ 'k-selected': selected,
4422
+ 'k-focus': selected,
4423
+ 'k-active': isActiveMessage
4424
+ }"
3690
4425
  >
3691
- <kendo-icon-wrapper
3692
- [name]="isMessageExpanded ? 'chevron-up' : 'chevron-down'"
3693
- [svgIcon]="isMessageExpanded ? collapseIcon : expandIcon"
4426
+ <div class="k-bubble-content">
4427
+ <ng-container *ngTemplateOutlet="getActiveContentTemplate()?.templateRef; context: { $implicit: message };"></ng-container>
4428
+ </div>
4429
+ <span
4430
+ class="k-bubble-expandable-indicator"
4431
+ *ngIf="isMessageExpandable && showExpandCollapseIcon"
4432
+ [attr.tabindex]="'0'"
4433
+ [attr.role]="'button'"
4434
+ [attr.title]="isMessageExpanded ? textFor('collapseTitle') : textFor('expandTitle')"
4435
+ (mousedown)="chatService.toggleMessageState = true"
4436
+ (click)="toggleMessageState($event)"
3694
4437
  >
3695
- </kendo-icon-wrapper>
3696
- </span>
3697
- </div>
3698
- </ng-container>
4438
+ <kendo-icon-wrapper
4439
+ [name]="isMessageExpanded ? 'chevron-up' : 'chevron-down'"
4440
+ [svgIcon]="isMessageExpanded ? collapseIcon : expandIcon"
4441
+ >
4442
+ </kendo-icon-wrapper>
4443
+ </span>
4444
+ </div>
3699
4445
 
3700
- <span
3701
- class="k-message-status"
3702
- *ngIf="message.status"
3703
- >
3704
- <ng-container *ngIf="statusTemplate?.templateRef">
3705
- <ng-template
3706
- [ngTemplateOutlet]="statusTemplate.templateRef"
3707
- [ngTemplateOutletContext]="{ $implicit: message.status, message }"
4446
+ <div
4447
+ class="k-chat-bubble k-bubble"
4448
+ *ngIf="!useCustomContentTemplate && hasMessageContent"
4449
+ [attr.tabindex]="0"
4450
+ [ngClass]="{
4451
+ 'k-bubble-expandable': isMessageExpandable,
4452
+ 'k-expanded': isMessageExpanded,
4453
+ 'k-selected': selected,
4454
+ 'k-focus': selected,
4455
+ 'k-active': isActiveMessage
4456
+ }"
3708
4457
  >
3709
- </ng-template>
3710
- </ng-container>
3711
- <ng-container *ngIf="!statusTemplate?.templateRef">
3712
- {{ message.status }}
4458
+ <div class="k-bubble-content">
4459
+ <ng-container *ngIf="message.text || message.isDeleted">
4460
+ <div
4461
+ class="k-message-reference k-message-reference-receiver"
4462
+ *ngIf="message.replyToId && !message.isDeleted"
4463
+ (click)="onReplyReferenceClick($event, message.replyToId)"
4464
+ >
4465
+ <chat-message-reference-content [message]="getMessageById(message.replyToId)"></chat-message-reference-content>
4466
+ </div>
4467
+
4468
+ <span class="k-chat-bubble-text" *ngIf="message.isDeleted">
4469
+ {{ getDeletedMessageText() }}
4470
+ </span>
4471
+
4472
+ <span class="k-chat-bubble-text" *ngIf="!message.isDeleted && parts.length > 0">
4473
+ <ng-container *ngFor="let part of parts">
4474
+ <ng-container *ngIf="part.type === 'text'">{{part.content}}</ng-container>
4475
+ <a *ngIf="part.type === 'link'" [href]="part.href" target="_blank">{{part.content}}</a>
4476
+ </ng-container>
4477
+ </span>
4478
+ </ng-container>
4479
+
4480
+ <ul
4481
+ class="k-chat-file-wrapper"
4482
+ *ngIf="hasFiles && !message.isDeleted"
4483
+ [ngClass]="{
4484
+ 'k-chat-files-wrap': chatService.messageFilesLayout === 'wrap',
4485
+ 'k-chat-files-horizontal': chatService.messageFilesLayout === 'horizontal'
4486
+ }"
4487
+ >
4488
+ <li
4489
+ *ngFor="let file of message.files"
4490
+ class="k-chat-file"
4491
+ [chatFile]="file"
4492
+ [fileActions]="fileActions"
4493
+ (actionClick)="onFileAction($event, file)"
4494
+ (actionsToggle)="onActionPopupChange($event)"
4495
+ (actionButtonClick)="onActionButtonClick($event)"
4496
+ ></li>
4497
+ </ul>
4498
+
4499
+ <div class="k-chat-download-button-wrapper" *ngIf="hasMultipleFiles && !message.isDeleted">
4500
+ <button
4501
+ kendoButton
4502
+ class="k-chat-download-button"
4503
+ fillMode="flat"
4504
+ icon="download"
4505
+ [svgIcon]="downloadIcon"
4506
+ [attr.title]="textFor('downloadAllFilesText')"
4507
+ (click)="onDownloadAll()"
4508
+ >{{ textFor('downloadAllFilesText') }}</button>
4509
+ </div>
4510
+ </div>
4511
+
4512
+ <span
4513
+ class="k-bubble-expandable-indicator"
4514
+ *ngIf="isMessageExpandable && showExpandCollapseIcon"
4515
+ [attr.tabindex]="'0'"
4516
+ [attr.role]="'button'"
4517
+ [attr.title]="isMessageExpanded ? textFor('collapseTitle') : textFor('expandTitle')"
4518
+ (mousedown)="chatService.toggleMessageState = true"
4519
+ (click)="toggleMessageState($event)"
4520
+ >
4521
+ <kendo-icon-wrapper
4522
+ [name]="isMessageExpanded ? 'chevron-up' : 'chevron-down'"
4523
+ [svgIcon]="isMessageExpanded ? collapseIcon : expandIcon"
4524
+ >
4525
+ </kendo-icon-wrapper>
4526
+ </span>
4527
+ </div>
3713
4528
  </ng-container>
3714
- </span>
3715
4529
 
3716
- <kendo-toolbar *ngIf="isToolbarVisible()" class="k-chat-message-toolbar" fillMode="flat">
4530
+ <span class="k-message-status" *ngIf="message.status">
4531
+ <ng-container *ngIf="statusTemplate?.templateRef">
4532
+ <ng-template
4533
+ [ngTemplateOutlet]="statusTemplate.templateRef"
4534
+ [ngTemplateOutletContext]="{ $implicit: message.status, message }"
4535
+ >
4536
+ </ng-template>
4537
+ </ng-container>
4538
+ <ng-container *ngIf="!statusTemplate?.templateRef">
4539
+ {{ message.status }}
4540
+ </ng-container>
4541
+ </span>
4542
+ </ng-container>
4543
+ <kendo-toolbar *ngIf="showToolbar" class="k-chat-message-toolbar" fillMode="flat">
3717
4544
  <kendo-toolbar-button
3718
- *ngFor="let action of chatService.messageToolbarActions"
4545
+ *ngFor="let action of toolbarActions"
3719
4546
  fillMode="flat"
3720
4547
  [icon]="action.icon"
3721
4548
  [svgIcon]="action.svgIcon"
@@ -3725,36 +4552,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3725
4552
  >
3726
4553
  </kendo-toolbar-button>
3727
4554
  </kendo-toolbar>
3728
-
3729
- <ng-template #typing>
3730
- <div class="k-chat-bubble k-bubble">
3731
- <div class="k-typing-indicator">
3732
- <span></span>
3733
- <span></span>
3734
- <span></span>
3735
- </div>
3736
- </div>
3737
- </ng-template>
3738
-
3739
- <kendo-contextmenu
3740
- *ngIf="!message.isDeleted"
3741
- [target]="element"
3742
- [items]="contextMenuActions"
3743
- (popupOpen)="handleMenuOpen()"
3744
- (popupClose)="handleMenuClose($event)"
3745
- (select)="onContextMenuAction($event.item.originalAction)"
3746
- [popupAlign]="{ horizontal: 'right', vertical: 'top' }"
3747
- [collision]="{ horizontal: 'flip', vertical: 'flip'}"
3748
- ></kendo-contextmenu>
3749
4555
  `,
3750
4556
  standalone: true,
3751
- imports: [NgIf, NgClass, NgFor, NgTemplateOutlet, IconWrapperComponent, KENDO_BUTTONS, ChatFileComponent, ContextMenuComponent, ToolBarComponent, ToolBarButtonComponent, MessageReferenceComponent],
4557
+ imports: [NgIf, NgClass, NgFor, NgTemplateOutlet, IconWrapperComponent, KENDO_BUTTONS, ChatFileComponent, ToolBarComponent, ToolBarButtonComponent, MessageReferenceComponent],
3752
4558
  }]
3753
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1$1.IntlService }, { type: ChatService }, { type: i1.LocalizationService }], propDecorators: { message: [{
4559
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1$1.IntlService }, { type: ChatService }, { type: i1.LocalizationService }, { type: i0.ChangeDetectorRef }], propDecorators: { message: [{
3754
4560
  type: Input
3755
4561
  }], tabbable: [{
3756
4562
  type: Input
3757
- }], template: [{
4563
+ }], authorMessageContentTemplate: [{
4564
+ type: Input
4565
+ }], receiverMessageContentTemplate: [{
4566
+ type: Input
4567
+ }], messageContentTemplate: [{
4568
+ type: Input
4569
+ }], authorMessageTemplate: [{
4570
+ type: Input
4571
+ }], receiverMessageTemplate: [{
4572
+ type: Input
4573
+ }], messageTemplate: [{
3758
4574
  type: Input
3759
4575
  }], statusTemplate: [{
3760
4576
  type: Input
@@ -3762,8 +4578,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3762
4578
  type: Input
3763
4579
  }], authorId: [{
3764
4580
  type: Input
3765
- }], contextMenuVisibilityChange: [{
3766
- type: Output
3767
4581
  }], cssClass: [{
3768
4582
  type: HostBinding,
3769
4583
  args: ['class.k-message']
@@ -3776,9 +4590,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3776
4590
  }], tabIndex: [{
3777
4591
  type: HostBinding,
3778
4592
  args: ['attr.tabIndex']
3779
- }], onContextMenu: [{
3780
- type: HostListener,
3781
- args: ['contextmenu', ['$event']]
3782
4593
  }] } });
3783
4594
 
3784
4595
  /**
@@ -3894,10 +4705,10 @@ class MessageAttachmentsComponent extends ChatItem {
3894
4705
  scrollSubscription;
3895
4706
  direction;
3896
4707
  get showLeftArrow() {
3897
- return this.carousel && this.direction === 'rtl' ? this.scrollPosition > -1 : this.scrollPosition > 0;
4708
+ return this.carousel && (this.direction === 'rtl' ? this.scrollPosition > -1 : this.scrollPosition > 0);
3898
4709
  }
3899
4710
  get showRightArrow() {
3900
- return this.carousel && this.direction === 'rtl' ? this.scrollPosition < 0 : this.scrollPosition < 1;
4711
+ return this.carousel && (this.direction === 'rtl' ? this.scrollPosition < 0 : this.scrollPosition < 1);
3901
4712
  }
3902
4713
  carouselKeyHandlers = {
3903
4714
  [Keys.ArrowLeft]: (e) => this.navigateTo(e, this.direction === 'rtl' ? 1 : -1),
@@ -4100,6 +4911,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4100
4911
  args: ['item', { read: ElementRef }]
4101
4912
  }] } });
4102
4913
 
4914
+ /**
4915
+ * Defines a template for displaying user status in the Chat.
4916
+ *
4917
+ * To define a user status template, nest an `<ng-template>` tag with the `kendoChatUserStatusTemplate` directive inside the `<kendo-chat>` component.
4918
+ * For more information, refer to the article on [message templates](slug:message_templates_chat).
4919
+ *
4920
+ * @example
4921
+ * ```html
4922
+ * <kendo-chat>
4923
+ * <ng-template kendoChatUserStatusTemplate let-status>
4924
+ * <div>{{ status }}</div>
4925
+ * </ng-template>
4926
+ * </kendo-chat>
4927
+ * ```
4928
+ */
4929
+ class ChatUserStatusTemplateDirective {
4930
+ templateRef;
4931
+ constructor(templateRef) {
4932
+ this.templateRef = templateRef;
4933
+ }
4934
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatUserStatusTemplateDirective, deps: [{ token: i0.TemplateRef, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
4935
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: ChatUserStatusTemplateDirective, isStandalone: true, selector: "[kendoChatUserStatusTemplate]", ngImport: i0 });
4936
+ }
4937
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatUserStatusTemplateDirective, decorators: [{
4938
+ type: Directive,
4939
+ args: [{
4940
+ selector: '[kendoChatUserStatusTemplate]',
4941
+ standalone: true
4942
+ }]
4943
+ }], ctorParameters: () => [{ type: i0.TemplateRef, decorators: [{
4944
+ type: Optional
4945
+ }] }] });
4946
+
4103
4947
  /* eslint-disable @typescript-eslint/no-unused-vars */
4104
4948
  /* eslint-disable @typescript-eslint/no-explicit-any */
4105
4949
  /**
@@ -4120,9 +4964,15 @@ class MessageListComponent {
4120
4964
  return this._messages;
4121
4965
  }
4122
4966
  attachmentTemplate;
4967
+ authorMessageContentTemplate;
4968
+ receiverMessageContentTemplate;
4969
+ messageContentTemplate;
4970
+ authorMessageTemplate;
4971
+ receiverMessageTemplate;
4123
4972
  messageTemplate;
4124
4973
  timestampTemplate;
4125
4974
  statusTemplate;
4975
+ userStatusTemplate;
4126
4976
  localization;
4127
4977
  authorId;
4128
4978
  executeAction = new EventEmitter();
@@ -4140,6 +4990,7 @@ class MessageListComponent {
4140
4990
  [Keys.ArrowUp]: (e) => this.navigateTo(e, -1),
4141
4991
  [Keys.ArrowDown]: (e) => this.navigateTo(e, 1),
4142
4992
  [Keys.Tab]: (e) => this.onTabKeyDown(e),
4993
+ [Keys.F10]: (e) => e.shiftKey && this.openContextMenu(e),
4143
4994
  };
4144
4995
  constructor(element, intl, renderer, chatService, cdr) {
4145
4996
  this.element = element;
@@ -4152,9 +5003,25 @@ class MessageListComponent {
4152
5003
  const elRef = this.element.nativeElement;
4153
5004
  this.subs.add(this.renderer.listen(elRef, 'keydown', event => this.onKeydown(event)));
4154
5005
  this.subs.add(this.renderer.listen(elRef, 'focusout', event => this.onBlur(event)));
5006
+ this.subs.add(this.renderer.listen(elRef, 'click', event => {
5007
+ const messageComponent = this.findMessageComponentFromEvent(event);
5008
+ if (messageComponent) {
5009
+ this.select(messageComponent, event);
5010
+ }
5011
+ }));
5012
+ this.subs.add(this.renderer.listen(elRef, 'contextmenu', event => {
5013
+ event.preventDefault();
5014
+ const messageComponent = this.findMessageComponentFromEvent(event);
5015
+ if (messageComponent) {
5016
+ this.onContextMenuClick(messageComponent.message, event, messageComponent);
5017
+ }
5018
+ }));
4155
5019
  this.subs.add(this.chatService.replyReferenceClick$.subscribe((messageId) => {
4156
5020
  this.scrollToAndSelectMessage(messageId);
4157
5021
  }));
5022
+ this.subs.add(this.chatService.contextMenuVisibilityChange$.subscribe((isVisible) => {
5023
+ this.handleMenuClose(isVisible);
5024
+ }));
4158
5025
  }
4159
5026
  ngAfterViewInit() {
4160
5027
  this.selectedItem = this.items.last;
@@ -4168,9 +5035,45 @@ class MessageListComponent {
4168
5035
  onClick(message, event) {
4169
5036
  this.select(message, event);
4170
5037
  }
5038
+ showGroupAuthor(group) {
5039
+ const messageSettings = this.isOwnMessage(group.messages[0]) ? this.chatService.authorMessageSettings : this.chatService.receiverMessageSettings;
5040
+ if (isPresent(messageSettings?.showUsername)) {
5041
+ return messageSettings.showUsername && group.author.name;
5042
+ }
5043
+ return this.chatService.showUsername && group.author.name;
5044
+ }
5045
+ showGroupAvatar(group) {
5046
+ const messageSettings = this.isOwnMessage(group.messages[0]) ? this.chatService.authorMessageSettings : this.chatService.receiverMessageSettings;
5047
+ if (isPresent(messageSettings?.showAvatar)) {
5048
+ return messageSettings.showAvatar && group.author.avatarUrl;
5049
+ }
5050
+ return this.chatService.showAvatar && group.author.avatarUrl;
5051
+ }
5052
+ onContextMenuClick(message, event, messageElement) {
5053
+ this.chatService.calculateContextMenuActions(this.isOwnMessage(message));
5054
+ if (this.chatService.calculatedContextMenuActions.length > 0) {
5055
+ this.chatService.messagesContextMenu.show({
5056
+ left: event.pageX,
5057
+ top: event.pageY,
5058
+ });
5059
+ this.chatService.activeMessageElement = messageElement;
5060
+ this.chatService.activeMessage = message;
5061
+ this.chatService.active = true;
5062
+ this.chatService.selectOnMenuClose = this.chatService.activeMessageElement?.selected;
5063
+ this.chatService.emit('contextMenuVisibilityChange', true);
5064
+ }
5065
+ }
4171
5066
  formatTimeStamp(date) {
4172
5067
  return this.intl.formatDate(date, { date: 'full' });
4173
5068
  }
5069
+ calculateMessageWidthMode(message) {
5070
+ const isOwn = this.isOwnMessage(message);
5071
+ const messageSettings = isOwn ? this.chatService.authorMessageSettings : this.chatService.receiverMessageSettings;
5072
+ if (messageSettings?.messageWidthMode) {
5073
+ return messageSettings.messageWidthMode === 'full';
5074
+ }
5075
+ return this.chatService.messageWidthMode === 'full';
5076
+ }
4174
5077
  onKeydown(e) {
4175
5078
  // On some keyboards Numpad keys are used for Home/End/PageUp/PageDown.
4176
5079
  const code = normalizeNumpadKeys(e);
@@ -4230,6 +5133,9 @@ class MessageListComponent {
4230
5133
  this.select(null);
4231
5134
  }
4232
5135
  }
5136
+ textFor(key) {
5137
+ return this.localization.get(key);
5138
+ }
4233
5139
  onHomeOrEndKeyDown(key) {
4234
5140
  const items = this.items.toArray();
4235
5141
  if (key === 'home') {
@@ -4276,11 +5182,49 @@ class MessageListComponent {
4276
5182
  this.select(chatItem);
4277
5183
  }
4278
5184
  }
4279
- textFor(key) {
4280
- return this.localization.get(key);
5185
+ openContextMenu(event) {
5186
+ event.preventDefault();
5187
+ const messageComponent = this.findMessageComponentFromActiveElement();
5188
+ if (messageComponent) {
5189
+ const messageContentElement = messageComponent.element.nativeElement.querySelector('.k-chat-bubble');
5190
+ if (messageContentElement) {
5191
+ const rect = messageContentElement?.getBoundingClientRect();
5192
+ this.onContextMenuClick(messageComponent.message, {
5193
+ pageX: rect.left + (rect.width / 2) + window.scrollX,
5194
+ pageY: rect.top + (rect.height / 2) + window.scrollY
5195
+ }, messageComponent);
5196
+ }
5197
+ else {
5198
+ const messageElement = messageComponent.element.nativeElement;
5199
+ const rect = messageElement?.getBoundingClientRect();
5200
+ this.onContextMenuClick(messageComponent.message, {
5201
+ pageX: rect.left + (rect.width / 2) + window.scrollX,
5202
+ pageY: rect.top + (rect.height / 2) + window.scrollY
5203
+ }, messageComponent);
5204
+ }
5205
+ }
5206
+ }
5207
+ findMessageComponentFromActiveElement() {
5208
+ const activeElement = document.activeElement;
5209
+ const messageComponents = this.items.filter(item => item instanceof MessageComponent);
5210
+ return messageComponents.find(component => {
5211
+ const componentElement = component.element?.nativeElement;
5212
+ return componentElement && (componentElement === activeElement || componentElement.contains(activeElement));
5213
+ });
5214
+ }
5215
+ findMessageComponentFromEvent(event) {
5216
+ const target = event.target;
5217
+ const clickedElement = target?.closest('.k-message');
5218
+ if (!clickedElement)
5219
+ return undefined;
5220
+ const messageComponents = this.items.filter(item => item instanceof MessageComponent);
5221
+ return messageComponents.find(component => {
5222
+ const componentElement = component.element?.nativeElement;
5223
+ return componentElement && (componentElement === clickedElement || componentElement.contains(clickedElement));
5224
+ });
4281
5225
  }
4282
5226
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageListComponent, deps: [{ token: i0.ElementRef }, { token: i1$1.IntlService }, { token: i0.Renderer2 }, { token: ChatService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
4283
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MessageListComponent, isStandalone: true, selector: "kendo-chat-message-list", inputs: { messages: "messages", attachmentTemplate: "attachmentTemplate", messageTemplate: "messageTemplate", timestampTemplate: "timestampTemplate", statusTemplate: "statusTemplate", localization: "localization", authorId: "authorId" }, outputs: { executeAction: "executeAction", navigate: "navigate", resize: "resize" }, host: { properties: { "class.k-message-list-content": "this.cssClass" } }, viewQueries: [{ propertyName: "items", predicate: ChatItem, descendants: true }], ngImport: i0, template: `
5227
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MessageListComponent, isStandalone: true, selector: "kendo-chat-message-list", inputs: { messages: "messages", attachmentTemplate: "attachmentTemplate", authorMessageContentTemplate: "authorMessageContentTemplate", receiverMessageContentTemplate: "receiverMessageContentTemplate", messageContentTemplate: "messageContentTemplate", authorMessageTemplate: "authorMessageTemplate", receiverMessageTemplate: "receiverMessageTemplate", messageTemplate: "messageTemplate", timestampTemplate: "timestampTemplate", statusTemplate: "statusTemplate", userStatusTemplate: "userStatusTemplate", localization: "localization", authorId: "authorId" }, outputs: { executeAction: "executeAction", navigate: "navigate", resize: "resize" }, host: { properties: { "class.k-message-list-content": "this.cssClass" } }, viewQueries: [{ propertyName: "items", predicate: ChatItem, descendants: true }], ngImport: i0, template: `
4284
5228
  <ng-container *ngFor="let group of view; last as lastGroup; trackBy: trackGroup">
4285
5229
  <ng-container [ngSwitch]="group.type">
4286
5230
  <div
@@ -4300,11 +5244,11 @@ class MessageListComponent {
4300
5244
  class="k-message-group"
4301
5245
  [class.k-message-group-sender]="isOwnMessage(group.messages[0])"
4302
5246
  [class.k-message-group-receiver]="!isOwnMessage(group.messages[0])"
4303
- [class.k-no-avatar]="!group.author.avatarUrl"
4304
- [class.k-message-group-full-width]="chatService.messageWidthMode === 'full'"
5247
+ [class.k-no-avatar]="!showGroupAvatar(group)"
5248
+ [class.k-message-group-full-width]="calculateMessageWidthMode(group.messages[0])"
4305
5249
  >
4306
5250
  <div
4307
- *ngIf="group.author.avatarUrl"
5251
+ *ngIf="!userStatusTemplate?.templateRef && showGroupAvatar(group)"
4308
5252
  class="k-avatar k-avatar-md k-avatar-solid k-avatar-solid-primary k-rounded-full"
4309
5253
  >
4310
5254
  <span class="k-avatar-image">
@@ -4314,29 +5258,41 @@ class MessageListComponent {
4314
5258
  />
4315
5259
  </span>
4316
5260
  </div>
5261
+ <div class="k-chat-user-status-wrapper" *ngIf="userStatusTemplate?.templateRef && showGroupAvatar(group)">
5262
+ <div
5263
+ class="k-avatar k-avatar-md k-avatar-solid k-avatar-solid-primary k-rounded-full"
5264
+ >
5265
+ <span class="k-avatar-image">
5266
+ <img
5267
+ [attr.src]="group.author.avatarUrl"
5268
+ [alt]="group.author.avatarAltText"
5269
+ />
5270
+ </span>
5271
+ </div>
5272
+ <div class="k-chat-user-status" *ngIf="userStatusTemplate?.templateRef">
5273
+ <ng-template
5274
+ [ngTemplateOutlet]="userStatusTemplate.templateRef"
5275
+ [ngTemplateOutletContext]="{ $implicit: group.messages.at(-1) }"
5276
+ >
5277
+ </ng-template>
5278
+ </div>
5279
+ </div>
4317
5280
  <div class="k-message-group-content">
4318
- <p *ngIf="group.author.name" class="k-message-author">{{ group.author.name }}</p>
5281
+ <p *ngIf="showGroupAuthor(group)" class="k-message-author">{{ group.author.name }}</p>
4319
5282
  <ng-container
4320
5283
  *ngFor="let msg of group.messages; first as firstMessage; last as lastMessage"
4321
5284
  >
4322
- <div
4323
- *ngIf="msg.user?.avatarUrl"
4324
- class="k-avatar k-avatar-md k-avatar-solid k-avatar-solid-primary k-rounded-full"
4325
- >
4326
- <span class="k-avatar-image">
4327
- <img [src]="msg.user?.avatarUrl">
4328
- </span>
4329
- </div>
4330
5285
  <kendo-chat-message #message
4331
5286
  [message]="msg"
4332
5287
  [tabbable]="lastGroup && lastMessage"
4333
- [template]="messageTemplate"
5288
+ [authorMessageContentTemplate]="authorMessageContentTemplate"
5289
+ [receiverMessageContentTemplate]="receiverMessageContentTemplate"
5290
+ [messageContentTemplate]="messageContentTemplate"
5291
+ [authorMessageTemplate]="authorMessageTemplate"
5292
+ [receiverMessageTemplate]="receiverMessageTemplate"
5293
+ [messageTemplate]="messageTemplate"
4334
5294
  [statusTemplate]="statusTemplate"
4335
5295
  [authorId]="authorId"
4336
- (click)="onClick(message, $event)"
4337
- (contextMenuVisibilityChange)="handleMenuClose($event)"
4338
- [class.k-first]="group.messages.length > 1 && firstMessage"
4339
- [class.k-last]="group.messages.length > 1 && lastMessage"
4340
5296
  >
4341
5297
  </kendo-chat-message>
4342
5298
 
@@ -4365,6 +5321,7 @@ class MessageListComponent {
4365
5321
  <kendo-chat-suggested-actions #actions
4366
5322
  *ngSwitchCase="'action-group'"
4367
5323
  [actions]="group.actions"
5324
+ type="action"
4368
5325
  [tabbable]="lastGroup"
4369
5326
  (dispatchAction)="dispatchAction($event, last(view))"
4370
5327
  (click)="select(actions, $event)"
@@ -4375,7 +5332,7 @@ class MessageListComponent {
4375
5332
  </ng-container>
4376
5333
  <kendo-resize-sensor (resize)="onResize()">
4377
5334
  </kendo-resize-sensor>
4378
- `, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MessageComponent, selector: "kendo-chat-message", inputs: ["message", "tabbable", "template", "statusTemplate", "showMessageTime", "authorId"], outputs: ["contextMenuVisibilityChange"] }, { kind: "component", type: AttachmentComponent, selector: "kendo-chat-attachment", inputs: ["attachment", "template"] }, { kind: "component", type: MessageAttachmentsComponent, selector: "kendo-chat-message-attachments", inputs: ["attachments", "layout", "tabbable", "template", "localization"] }, { kind: "component", type: SuggestedActionsComponent, selector: "kendo-chat-suggested-actions", inputs: ["actions", "suggestions", "tabbable", "suggestionTemplate"], outputs: ["dispatchAction", "dispatchSuggestion"] }, { kind: "component", type: ResizeSensorComponent, selector: "kendo-resize-sensor", inputs: ["rateLimit"], outputs: ["resize"] }] });
5335
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MessageComponent, selector: "kendo-chat-message", inputs: ["message", "tabbable", "authorMessageContentTemplate", "receiverMessageContentTemplate", "messageContentTemplate", "authorMessageTemplate", "receiverMessageTemplate", "messageTemplate", "statusTemplate", "showMessageTime", "authorId"] }, { kind: "component", type: AttachmentComponent, selector: "kendo-chat-attachment", inputs: ["attachment", "template"] }, { kind: "component", type: MessageAttachmentsComponent, selector: "kendo-chat-message-attachments", inputs: ["attachments", "layout", "tabbable", "template", "localization"] }, { kind: "component", type: SuggestedActionsComponent, selector: "kendo-chat-suggested-actions", inputs: ["actions", "suggestions", "tabbable", "type", "suggestionTemplate"], outputs: ["dispatchAction", "dispatchSuggestion"] }, { kind: "component", type: ResizeSensorComponent, selector: "kendo-resize-sensor", inputs: ["rateLimit"], outputs: ["resize"] }] });
4379
5336
  }
4380
5337
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageListComponent, decorators: [{
4381
5338
  type: Component,
@@ -4401,11 +5358,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4401
5358
  class="k-message-group"
4402
5359
  [class.k-message-group-sender]="isOwnMessage(group.messages[0])"
4403
5360
  [class.k-message-group-receiver]="!isOwnMessage(group.messages[0])"
4404
- [class.k-no-avatar]="!group.author.avatarUrl"
4405
- [class.k-message-group-full-width]="chatService.messageWidthMode === 'full'"
5361
+ [class.k-no-avatar]="!showGroupAvatar(group)"
5362
+ [class.k-message-group-full-width]="calculateMessageWidthMode(group.messages[0])"
4406
5363
  >
4407
5364
  <div
4408
- *ngIf="group.author.avatarUrl"
5365
+ *ngIf="!userStatusTemplate?.templateRef && showGroupAvatar(group)"
4409
5366
  class="k-avatar k-avatar-md k-avatar-solid k-avatar-solid-primary k-rounded-full"
4410
5367
  >
4411
5368
  <span class="k-avatar-image">
@@ -4415,29 +5372,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4415
5372
  />
4416
5373
  </span>
4417
5374
  </div>
5375
+ <div class="k-chat-user-status-wrapper" *ngIf="userStatusTemplate?.templateRef && showGroupAvatar(group)">
5376
+ <div
5377
+ class="k-avatar k-avatar-md k-avatar-solid k-avatar-solid-primary k-rounded-full"
5378
+ >
5379
+ <span class="k-avatar-image">
5380
+ <img
5381
+ [attr.src]="group.author.avatarUrl"
5382
+ [alt]="group.author.avatarAltText"
5383
+ />
5384
+ </span>
5385
+ </div>
5386
+ <div class="k-chat-user-status" *ngIf="userStatusTemplate?.templateRef">
5387
+ <ng-template
5388
+ [ngTemplateOutlet]="userStatusTemplate.templateRef"
5389
+ [ngTemplateOutletContext]="{ $implicit: group.messages.at(-1) }"
5390
+ >
5391
+ </ng-template>
5392
+ </div>
5393
+ </div>
4418
5394
  <div class="k-message-group-content">
4419
- <p *ngIf="group.author.name" class="k-message-author">{{ group.author.name }}</p>
5395
+ <p *ngIf="showGroupAuthor(group)" class="k-message-author">{{ group.author.name }}</p>
4420
5396
  <ng-container
4421
5397
  *ngFor="let msg of group.messages; first as firstMessage; last as lastMessage"
4422
5398
  >
4423
- <div
4424
- *ngIf="msg.user?.avatarUrl"
4425
- class="k-avatar k-avatar-md k-avatar-solid k-avatar-solid-primary k-rounded-full"
4426
- >
4427
- <span class="k-avatar-image">
4428
- <img [src]="msg.user?.avatarUrl">
4429
- </span>
4430
- </div>
4431
5399
  <kendo-chat-message #message
4432
5400
  [message]="msg"
4433
5401
  [tabbable]="lastGroup && lastMessage"
4434
- [template]="messageTemplate"
5402
+ [authorMessageContentTemplate]="authorMessageContentTemplate"
5403
+ [receiverMessageContentTemplate]="receiverMessageContentTemplate"
5404
+ [messageContentTemplate]="messageContentTemplate"
5405
+ [authorMessageTemplate]="authorMessageTemplate"
5406
+ [receiverMessageTemplate]="receiverMessageTemplate"
5407
+ [messageTemplate]="messageTemplate"
4435
5408
  [statusTemplate]="statusTemplate"
4436
5409
  [authorId]="authorId"
4437
- (click)="onClick(message, $event)"
4438
- (contextMenuVisibilityChange)="handleMenuClose($event)"
4439
- [class.k-first]="group.messages.length > 1 && firstMessage"
4440
- [class.k-last]="group.messages.length > 1 && lastMessage"
4441
5410
  >
4442
5411
  </kendo-chat-message>
4443
5412
 
@@ -4466,6 +5435,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4466
5435
  <kendo-chat-suggested-actions #actions
4467
5436
  *ngSwitchCase="'action-group'"
4468
5437
  [actions]="group.actions"
5438
+ type="action"
4469
5439
  [tabbable]="lastGroup"
4470
5440
  (dispatchAction)="dispatchAction($event, last(view))"
4471
5441
  (click)="select(actions, $event)"
@@ -4484,12 +5454,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4484
5454
  type: Input
4485
5455
  }], attachmentTemplate: [{
4486
5456
  type: Input
5457
+ }], authorMessageContentTemplate: [{
5458
+ type: Input
5459
+ }], receiverMessageContentTemplate: [{
5460
+ type: Input
5461
+ }], messageContentTemplate: [{
5462
+ type: Input
5463
+ }], authorMessageTemplate: [{
5464
+ type: Input
5465
+ }], receiverMessageTemplate: [{
5466
+ type: Input
4487
5467
  }], messageTemplate: [{
4488
5468
  type: Input
4489
5469
  }], timestampTemplate: [{
4490
5470
  type: Input
4491
5471
  }], statusTemplate: [{
4492
5472
  type: Input
5473
+ }], userStatusTemplate: [{
5474
+ type: Input
4493
5475
  }], localization: [{
4494
5476
  type: Input
4495
5477
  }], authorId: [{
@@ -4652,8 +5634,16 @@ let Messages$1 = class Messages extends ComponentMessages {
4652
5634
  * Sets the title of the DropDownButton that opens the File actions.
4653
5635
  */
4654
5636
  fileActionsTitle;
5637
+ /**
5638
+ * Sets the title of the button that shows the **Scroll right** when the suggestions list is scrollable with buttons.
5639
+ */
5640
+ nextSuggestionsButtonTitle;
5641
+ /**
5642
+ * Sets the title of the button that shows the **Scroll left** when the suggestions list is scrollable with buttons.
5643
+ */
5644
+ previousSuggestionsButtonTitle;
4655
5645
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: Messages, deps: null, target: i0.ɵɵFactoryTarget.Directive });
4656
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: Messages, selector: "kendoConversationalUIMessages", inputs: { deletedMessageSenderText: "deletedMessageSenderText", deletedMessageReceiverText: "deletedMessageReceiverText", downloadAllFilesText: "downloadAllFilesText", messagePlaceholder: "messagePlaceholder", send: "send", messageListLabel: "messageListLabel", messageBoxInputLabel: "messageBoxInputLabel", messageAttachmentLeftArrow: "messageAttachmentLeftArrow", messageAttachmentRightArrow: "messageAttachmentRightArrow", speechToTextButtonTitle: "speechToTextButtonTitle", fileSelectButtonTitle: "fileSelectButtonTitle", removeReplyTitle: "removeReplyTitle", removeFileTitle: "removeFileTitle", expandTitle: "expandTitle", collapseTitle: "collapseTitle", fileActionsTitle: "fileActionsTitle" }, usesInheritance: true, ngImport: i0 });
5646
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: Messages, selector: "kendoConversationalUIMessages", inputs: { deletedMessageSenderText: "deletedMessageSenderText", deletedMessageReceiverText: "deletedMessageReceiverText", downloadAllFilesText: "downloadAllFilesText", messagePlaceholder: "messagePlaceholder", send: "send", messageListLabel: "messageListLabel", messageBoxInputLabel: "messageBoxInputLabel", messageAttachmentLeftArrow: "messageAttachmentLeftArrow", messageAttachmentRightArrow: "messageAttachmentRightArrow", speechToTextButtonTitle: "speechToTextButtonTitle", fileSelectButtonTitle: "fileSelectButtonTitle", removeReplyTitle: "removeReplyTitle", removeFileTitle: "removeFileTitle", expandTitle: "expandTitle", collapseTitle: "collapseTitle", fileActionsTitle: "fileActionsTitle", nextSuggestionsButtonTitle: "nextSuggestionsButtonTitle", previousSuggestionsButtonTitle: "previousSuggestionsButtonTitle" }, usesInheritance: true, ngImport: i0 });
4657
5647
  };
4658
5648
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: Messages$1, decorators: [{
4659
5649
  type: Directive,
@@ -4693,6 +5683,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4693
5683
  type: Input
4694
5684
  }], fileActionsTitle: [{
4695
5685
  type: Input
5686
+ }], nextSuggestionsButtonTitle: [{
5687
+ type: Input
5688
+ }], previousSuggestionsButtonTitle: [{
5689
+ type: Input
4696
5690
  }] } });
4697
5691
 
4698
5692
  // eslint-disable no-forward-ref
@@ -4755,6 +5749,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4755
5749
  }]
4756
5750
  }], ctorParameters: () => [{ type: i0.TemplateRef }] });
4757
5751
 
5752
+ /**
5753
+ * Defines a template for displaying custom content when there are no messages in the Chat.
5754
+ *
5755
+ * To define an empty Chat template, nest an `<ng-template>` tag with the `kendoChatNoDataTemplate` directive inside the `<kendo-chat>` component.
5756
+ * For more information, refer to the article on [message templates](slug:message_templates_chat).
5757
+ *
5758
+ * @example
5759
+ * ```html
5760
+ * <kendo-chat>
5761
+ * <ng-template kendoChatNoDataTemplate>
5762
+ * <div>No messages.</div>
5763
+ * </ng-template>
5764
+ * </kendo-chat>
5765
+ * ```
5766
+ */
5767
+ class NoDataTemplateDirective {
5768
+ templateRef;
5769
+ constructor(templateRef) {
5770
+ this.templateRef = templateRef;
5771
+ }
5772
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NoDataTemplateDirective, deps: [{ token: i0.TemplateRef, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
5773
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: NoDataTemplateDirective, isStandalone: true, selector: "[kendoChatNoDataTemplate]", ngImport: i0 });
5774
+ }
5775
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NoDataTemplateDirective, decorators: [{
5776
+ type: Directive,
5777
+ args: [{
5778
+ selector: '[kendoChatNoDataTemplate]',
5779
+ standalone: true
5780
+ }]
5781
+ }], ctorParameters: () => [{ type: i0.TemplateRef, decorators: [{
5782
+ type: Optional
5783
+ }] }] });
5784
+
4758
5785
  /**
4759
5786
  * Represents the [Kendo UI Chat component for Angular](slug:overview_convui).
4760
5787
  *
@@ -4772,7 +5799,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4772
5799
  * ```
4773
5800
  *
4774
5801
  * @remarks
4775
- * Supported children components are: {@link CustomMessagesComponent}, {@link HeroCardComponent}, {@link AttachmentTemplateDirective}, {@link ChatHeaderTemplateDirective}, {@link ChatMessageBoxTemplateDirective}, {@link MessageTemplateDirective}, {@link ChatStatusTemplateDirective}, {@link ChatSuggestionTemplateDirective}, {@link ChatTimestampTemplateDirective}.
5802
+ * 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}.
4776
5803
  */
4777
5804
  class ChatComponent {
4778
5805
  localization;
@@ -4781,13 +5808,13 @@ class ChatComponent {
4781
5808
  element;
4782
5809
  chatService;
4783
5810
  /**
4784
- * Defines the array of messages displayed in the Chat.
4785
- * Each message can include a timestamp for unique identification.
4786
- * For more information, see [ngFor - Change Tracking](link:site.data.urls.angular['ngforof']#change-propagation).
5811
+ * Sets the Chat messages.
5812
+ * Accepts an array of `Message` objects, but can also accept custom data structures.
5813
+ * For more information, check [Data Binding](slug:databinding_chat) section in the documentation.
4787
5814
  */
4788
5815
  messages;
4789
5816
  /**
4790
- * Specifies the id representing the local user.
5817
+ * Sets the ID that represents the local user.
4791
5818
  */
4792
5819
  authorId;
4793
5820
  /**
@@ -4815,13 +5842,32 @@ class ChatComponent {
4815
5842
  */
4816
5843
  placeholder;
4817
5844
  /**
4818
- * Switches the width of the message between the predefined options.
5845
+ * Controls the width of the message between the predefined options.
4819
5846
  *
4820
5847
  * @default 'standard'
4821
5848
  */
4822
5849
  messageWidthMode = 'standard';
5850
+ /**
5851
+ * Controls the visibility of timestamps in messages.
5852
+ *
5853
+ * @default 'focus'
5854
+ */
5855
+ timestampVisibility = 'focus';
5856
+ /**
5857
+ * Controls the visibility of the author's avatar for the message group.
5858
+ *
5859
+ * @default true
5860
+ */
5861
+ showUsername = true;
5862
+ /**
5863
+ * Controls the visibility of the author's username for the message group.
5864
+ *
5865
+ * @default true
5866
+ */
5867
+ showAvatar = true;
4823
5868
  /**
4824
5869
  * Enables the expand or collapse functionality for messages.
5870
+ *
4825
5871
  * @default false
4826
5872
  */
4827
5873
  allowMessageCollapse = false;
@@ -4838,24 +5884,34 @@ class ChatComponent {
4838
5884
  */
4839
5885
  enableFileSelect = true;
4840
5886
  /**
4841
- * Specifies the actions available in the message toolbar.
5887
+ * Sets the actions of the message toolbar.
4842
5888
  * These actions display in the message toolbar and let you perform specific operations on the message.
5889
+ *
4843
5890
  * @default []
4844
5891
  */
4845
5892
  messageToolbarActions = [];
4846
5893
  /**
4847
5894
  * Sets the value of the Message Box.
5895
+ *
4848
5896
  * @default ''
4849
5897
  */
4850
5898
  inputValue = '';
4851
5899
  /**
4852
- * Specifies the default actions available in the message context menu.
5900
+ * Sets the settings for the author's messages.
5901
+ */
5902
+ authorMessageSettings;
5903
+ /**
5904
+ * Sets the settings for the receivers' messages.
5905
+ */
5906
+ receiverMessageSettings;
5907
+ /**
5908
+ * Sets the default actions that display in the message context menu.
4853
5909
  *
4854
5910
  * @default [{ id: 'copy', label: 'Copy', icon: 'copy', svgIcon: copyIcon, disabled: false }, { id: 'reply', label: 'Reply', icon: 'undo', svgIcon: undoIcon, disabled: false }]
4855
5911
  */
4856
5912
  defaultContextMenuActions = CONTEXT_MENU_ACTIONS;
4857
5913
  /**
4858
- * Specifies the actions available in the message as a context menu.
5914
+ * Sets the actions that display in the message as a context menu.
4859
5915
  * These actions display as menu items and let you perform specific operations on the message.
4860
5916
  * The default actions are `copy` and `reply` and are defined by their `id`.
4861
5917
  */
@@ -4866,11 +5922,13 @@ class ChatComponent {
4866
5922
  return this._messageContextMenuActions;
4867
5923
  }
4868
5924
  /**
4869
- * Specifies the default actions available in the file actions DropDownButton.
5925
+ * Sets the default actions that display in the file actions DropDownButton.
5926
+ *
5927
+ * @default [{ id: 'download', label: 'Download', icon: 'download', svgIcon: downloadIcon, disabled: false }]
4870
5928
  */
4871
5929
  defaultFileActions = FILE_ACTIONS;
4872
5930
  /**
4873
- * Specifies the actions available in the file as items of a DropDownButton.
5931
+ * Sets the actions that display in the file as items of a DropDownButton.
4874
5932
  * These actions display when you click the file DropDownButton and let you perform specific operations on the file.
4875
5933
  * The default action is `download` and is defined by its `id`.
4876
5934
  *
@@ -4883,17 +5941,40 @@ class ChatComponent {
4883
5941
  return this._fileActions;
4884
5942
  }
4885
5943
  /**
4886
- * Sets the suggestions to display in the message input box.
4887
- * Suggestions display as a list of clickable items that let you quickly insert predefined text into the message input.
4888
- * @default []
5944
+ * Sets the layout of the files in the message bubble.
5945
+ *
5946
+ * @default 'vertical'
4889
5947
  */
4890
- suggestions = [];
5948
+ set messageFilesLayout(layout) {
5949
+ this.chatService.messageFilesLayout = layout;
5950
+ }
4891
5951
  /**
4892
- * Sets the visibility of the message toolbar.
5952
+ * Sets the layout of the suggestions above the message input box.
4893
5953
  *
4894
- * @default 'hidden'
5954
+ * @default 'scroll'
4895
5955
  */
4896
- messageToolbarVisibility = 'hidden';
5956
+ set suggestionsLayout(layoutMode) {
5957
+ if (layoutMode) {
5958
+ this.chatService.suggestionsLayout = layoutMode;
5959
+ }
5960
+ }
5961
+ /**
5962
+ * Sets the layout of the quick actions suggested below the messages.
5963
+ *
5964
+ * @default 'scroll'
5965
+ */
5966
+ set quickActionsLayout(layoutMode) {
5967
+ if (layoutMode) {
5968
+ this.chatService.quickActionsLayout = layoutMode;
5969
+ }
5970
+ }
5971
+ /**
5972
+ * Sets the suggestions that display in the message input box.
5973
+ * Suggestions display as a list of clickable items that let you quickly insert predefined text into the message input.
5974
+ *
5975
+ * @default []
5976
+ */
5977
+ suggestions = [];
4897
5978
  /**
4898
5979
  * Sets the send button settings for the Chat component.
4899
5980
  * Allows customization of the send button appearance, icons and disabled state.
@@ -4912,53 +5993,53 @@ class ChatComponent {
4912
5993
  return this._modelFields;
4913
5994
  }
4914
5995
  /**
4915
- * Emits when the user sends a message by clicking the **Send** button or pressing **Enter**.
5996
+ * Fires when the user sends a message by clicking the **Send** button or pressing **Enter**.
4916
5997
  *
4917
5998
  * The message is not automatically added to the `messages` array.
4918
5999
  */
4919
6000
  sendMessage = new EventEmitter();
4920
6001
  /**
4921
- * Emits when the user clicks a quick action button in the message toolbar.
6002
+ * Fires when the user clicks a quick action button in the message toolbar.
4922
6003
  */
4923
6004
  toolbarActionClick = new EventEmitter();
4924
6005
  /**
4925
- * Emits when the user clicks an action in the message context menu.
6006
+ * Fires when the user clicks an action in the message context menu.
4926
6007
  */
4927
6008
  contextMenuActionClick = new EventEmitter();
4928
6009
  /**
4929
- * Emits when the user clicks an action in the file context menu.
6010
+ * Fires when the user clicks an action in the file context menu.
4930
6011
  */
4931
6012
  fileActionClick = new EventEmitter();
4932
6013
  /**
4933
- * Emits when the user clicks an action in the file context menu.
6014
+ * Fires when the user clicks an action in the file context menu.
4934
6015
  */
4935
6016
  download = new EventEmitter();
4936
6017
  /**
4937
- * Emits when the user clicks a quick action button.
6018
+ * Fires when the user clicks a quick action button.
4938
6019
  * The Chat internally handles [known actions](slug:api_conversational-ui_actiontype) such as `reply`, `openUrl`, and `call`.
4939
6020
  *
4940
6021
  * The event is preventable. Calling [`preventDefault`](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault) suppresses the built-in action.
4941
6022
  */
4942
6023
  executeAction = new EventEmitter();
4943
6024
  /**
4944
- * Emits when the user clicks a suggestion in the message input box.
6025
+ * Fires when the user clicks a suggestion in the message input box.
4945
6026
  */
4946
6027
  suggestionExecute = new EventEmitter();
4947
6028
  /**
4948
- * Emits when the user selects a file in the message input box.
6029
+ * Fires when the user selects a file in the message input box.
4949
6030
  */
4950
6031
  fileSelect = new EventEmitter();
4951
6032
  /**
4952
- * Emits when the user removes a file from the message input box.
6033
+ * Fires when the user removes a file from the message input box.
4953
6034
  */
4954
6035
  fileRemove = new EventEmitter();
4955
6036
  /**
4956
- * Emits when the user unpins the pinned message.
6037
+ * Fires when the user unpins the pinned message.
4957
6038
  * This event triggers when the user clicks the delete button on the pinned message.
4958
6039
  */
4959
6040
  unpin = new EventEmitter();
4960
6041
  /**
4961
- * Emits when the user types in the message input box.
6042
+ * Fires when the user types in the message input box.
4962
6043
  */
4963
6044
  inputValueChange = new EventEmitter();
4964
6045
  get className() {
@@ -4967,13 +6048,23 @@ class ChatComponent {
4967
6048
  get dirAttr() {
4968
6049
  return this.direction;
4969
6050
  }
6051
+ set messagesContextMenu(contextMenu) {
6052
+ this.chatService.messagesContextMenu = contextMenu;
6053
+ }
4970
6054
  attachmentTemplate;
4971
6055
  chatHeaderTemplate;
6056
+ chatNoDataTemplate;
6057
+ authorMessageContentTemplate;
6058
+ receiverMessageContentTemplate;
6059
+ messageContentTemplate;
6060
+ authorMessageTemplate;
6061
+ receiverMessageTemplate;
4972
6062
  messageTemplate;
4973
6063
  timestampTemplate;
4974
6064
  suggestionTemplate;
4975
6065
  statusTemplate;
4976
6066
  messageBoxTemplate;
6067
+ userStatusTemplate;
4977
6068
  messageBox;
4978
6069
  /**
4979
6070
  * @hidden
@@ -4981,17 +6072,30 @@ class ChatComponent {
4981
6072
  messageList;
4982
6073
  /**
4983
6074
  * @hidden
4984
- * Returns processed messages when model fields are used, otherwise returns original messages
6075
+ * Returns processed messages when model fields are used, otherwise returns original messages.
4985
6076
  */
4986
6077
  get processedMessages() {
4987
6078
  if (!this.messages || this.messages.length === 0) {
4988
6079
  return [];
4989
6080
  }
4990
6081
  if (this._modelFields && Object.keys(this._modelFields).some(key => this._modelFields[key] !== defaultModelFields[key])) {
4991
- return processMessages(this.messages, this._modelFields);
6082
+ if (this.messages !== this._lastMessagesReference || this._modelFields !== this._lastModelFields) {
6083
+ this._cachedProcessedMessages = processMessages(this.messages, this._modelFields);
6084
+ this._lastMessagesReference = this.messages;
6085
+ this._lastModelFields = this._modelFields;
6086
+ }
6087
+ return this._cachedProcessedMessages;
4992
6088
  }
4993
6089
  return this.messages;
4994
6090
  }
6091
+ /**
6092
+ * Gets the actions available in the message context menu.
6093
+ *
6094
+ * @hidden
6095
+ */
6096
+ get contextMenuActions() {
6097
+ return transformActions(this.chatService.calculatedContextMenuActions);
6098
+ }
4995
6099
  /**
4996
6100
  * @hidden
4997
6101
  */
@@ -5019,6 +6123,9 @@ class ChatComponent {
5019
6123
  _modelFields = defaultModelFields;
5020
6124
  _messageContextMenuActions = CONTEXT_MENU_ACTIONS;
5021
6125
  _fileActions = FILE_ACTIONS;
6126
+ _cachedProcessedMessages = [];
6127
+ _lastMessagesReference = null;
6128
+ _lastModelFields = null;
5022
6129
  constructor(localization, zone, renderer, element, chatService) {
5023
6130
  this.localization = localization;
5024
6131
  this.zone = zone;
@@ -5036,6 +6143,9 @@ class ChatComponent {
5036
6143
  */
5037
6144
  ngOnInit() {
5038
6145
  this.chatService.messageWidthMode = this.messageWidthMode;
6146
+ this.chatService.timestampVisibility = this.timestampVisibility;
6147
+ this.chatService.showUsername = this.showUsername;
6148
+ this.chatService.showAvatar = this.showAvatar;
5039
6149
  this.chatService.allowMessageCollapse = this.allowMessageCollapse;
5040
6150
  this.chatService.enableSpeechToText = this.enableSpeechToText;
5041
6151
  this.chatService.sendButtonSettings = this.sendButtonSettings;
@@ -5043,7 +6153,8 @@ class ChatComponent {
5043
6153
  this.chatService.messageToolbarActions = this.messageToolbarActions;
5044
6154
  this.chatService.messageContextMenuActions = this.messageContextMenuActions;
5045
6155
  this.chatService.fileActions = this.fileActions;
5046
- this.chatService.messageToolbarVisibility = this.messageToolbarVisibility;
6156
+ this.chatService.authorMessageSettings = this.authorMessageSettings;
6157
+ this.chatService.receiverMessageSettings = this.receiverMessageSettings;
5047
6158
  this.chatService.messages = this.processedMessages || [];
5048
6159
  this.chatService.chatElement = this.messageList;
5049
6160
  this.subs.add(this.chatService.toolbarAction$.subscribe((actionEvent) => {
@@ -5084,6 +6195,9 @@ class ChatComponent {
5084
6195
  this.updateChatServiceProperties([
5085
6196
  'authorId',
5086
6197
  'messageWidthMode',
6198
+ 'timestampVisibility',
6199
+ 'showUsername',
6200
+ 'showAvatar',
5087
6201
  'allowMessageCollapse',
5088
6202
  'enableSpeechToText',
5089
6203
  'sendButtonSettings',
@@ -5091,7 +6205,8 @@ class ChatComponent {
5091
6205
  'messageToolbarActions',
5092
6206
  'messageContextMenuActions',
5093
6207
  'fileActions',
5094
- 'messageToolbarVisibility'
6208
+ 'authorMessageSettings',
6209
+ 'receiverMessageSettings'
5095
6210
  ], changes);
5096
6211
  }
5097
6212
  /**
@@ -5140,6 +6255,43 @@ class ChatComponent {
5140
6255
  this.chatService.scrollToMessage(this.pinnedMessage?.id);
5141
6256
  }
5142
6257
  }
6258
+ /**
6259
+ * @hidden
6260
+ */
6261
+ onContextMenuAction(action) {
6262
+ if (action.id === 'reply') {
6263
+ this.chatService.reply = this.chatService.activeMessage;
6264
+ }
6265
+ if (action.id === 'copy') {
6266
+ navigator.clipboard.writeText(this.chatService.activeMessage.text);
6267
+ }
6268
+ this.chatService.emit('contextMenuAction', { action, message: this.chatService.activeMessage });
6269
+ }
6270
+ /**
6271
+ * @hidden
6272
+ */
6273
+ handleMenuClose(event) {
6274
+ if (event) {
6275
+ const originalEvent = event.originalEvent;
6276
+ originalEvent && this.onActionButtonClick(originalEvent);
6277
+ }
6278
+ this.chatService.activeMessage = null;
6279
+ this.chatService.emit('contextMenuVisibilityChange', false);
6280
+ if (this.chatService.selectOnMenuClose) {
6281
+ this.chatService.activeMessageElement.selected = true;
6282
+ this.chatService.focusActiveMessageElement();
6283
+ }
6284
+ }
6285
+ /**
6286
+ * @hidden
6287
+ */
6288
+ onActionButtonClick(event) {
6289
+ const clickOutsideMessage = event instanceof MouseEvent && !event.target?.closest('.k-chat-bubble');
6290
+ const menuItemClick = event instanceof MouseEvent && event.target?.closest(MENU_ITEM_SELECTOR);
6291
+ if (clickOutsideMessage && !menuItemClick) {
6292
+ this.chatService.selectOnMenuClose = false;
6293
+ }
6294
+ }
5143
6295
  findLastPinnedMessage() {
5144
6296
  return [...this.processedMessages].reverse().find((message) => message.isPinned);
5145
6297
  }
@@ -5163,14 +6315,15 @@ class ChatComponent {
5163
6315
  });
5164
6316
  }
5165
6317
  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: ChatService }], target: i0.ɵɵFactoryTarget.Component });
5166
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.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", allowMessageCollapse: "allowMessageCollapse", enableSpeechToText: "enableSpeechToText", enableFileSelect: "enableFileSelect", messageToolbarActions: "messageToolbarActions", inputValue: "inputValue", messageContextMenuActions: "messageContextMenuActions", fileActions: "fileActions", suggestions: "suggestions", messageToolbarVisibility: "messageToolbarVisibility", 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: [
6318
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.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: [
5167
6319
  LocalizationService,
5168
6320
  ChatService,
6321
+ SuggestionsScrollService,
5169
6322
  {
5170
6323
  provide: L10N_PREFIX,
5171
6324
  useValue: 'kendo.chat'
5172
6325
  }
5173
- ], queries: [{ propertyName: "attachmentTemplate", first: true, predicate: AttachmentTemplateDirective, descendants: true }, { propertyName: "chatHeaderTemplate", first: true, predicate: ChatHeaderTemplateDirective, 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 }], viewQueries: [{ 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: `
6326
+ ], 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: `
5174
6327
  <ng-container kendoChatLocalizedMessages
5175
6328
  i18n-deletedMessageSenderText="kendo.chat.deletedMessageSenderText|The text that is displayed when the sender deletes a message"
5176
6329
  deletedMessageSenderText="You removed this message."
@@ -5219,10 +6372,16 @@ class ChatComponent {
5219
6372
 
5220
6373
  i18n-downloadAllFilesText="kendo.chat.downloadAllFilesText|Sets the text that is displayed in the download section of the message."
5221
6374
  downloadAllFilesText="Download all"
6375
+
6376
+ i18n-previousSuggestionsButtonTitle="kendo.chat.previousSuggestionsButtonTitle|The title of the button that scrolls to the previous suggestions"
6377
+ previousSuggestionsButtonTitle="Scroll left"
6378
+
6379
+ i18n-nextSuggestionsButtonTitle="kendo.chat.nextSuggestionsButtonTitle|The title of the button that scrolls to the next suggestions"
6380
+ nextSuggestionsButtonTitle="Scroll right"
5222
6381
  >
5223
6382
  </ng-container>
5224
6383
 
5225
- <kendo-appbar *ngIf="chatHeaderTemplate" class="k-chat-header" positionMode="sticky">
6384
+ <kendo-appbar *ngIf="chatHeaderTemplate" class="k-chat-header" positionMode="sticky" themeColor="inherit">
5226
6385
  <ng-container *ngTemplateOutlet="chatHeaderTemplate.templateRef"></ng-container>
5227
6386
  </kendo-appbar>
5228
6387
  <div class="k-message-reference k-message-reference-receiver k-message-pinned" *ngIf="pinnedMessage" (click)="scrollToPinnedMessage()">
@@ -5238,7 +6397,7 @@ class ChatComponent {
5238
6397
  </div>
5239
6398
  <div
5240
6399
  #messageList
5241
- class="k-message-list k-avatars"
6400
+ class="k-message-list"
5242
6401
  aria-live="polite"
5243
6402
  role="log"
5244
6403
  kendoChatScrollAnchor
@@ -5246,19 +6405,32 @@ class ChatComponent {
5246
6405
  #anchor="scrollAnchor"
5247
6406
  [(autoScroll)]="autoScroll"
5248
6407
  >
5249
- <kendo-chat-message-list
5250
- [messages]="processedMessages"
5251
- [messageTemplate]="messageTemplate"
5252
- [timestampTemplate]="timestampTemplate"
5253
- [statusTemplate]="statusTemplate"
5254
- [localization]="localizationText"
5255
- [attachmentTemplate]="attachmentTemplate"
5256
- [authorId]="authorId"
5257
- (executeAction)="dispatchAction($event)"
5258
- (resize)="anchor.scrollToBottom()"
5259
- (navigate)="this.autoScroll = false"
5260
- >
5261
- </kendo-chat-message-list>
6408
+ <div *ngIf="processedMessages && processedMessages.length === 0; else chatMessageList" class="k-message-list-content k-message-list-content-empty">
6409
+ <ng-template
6410
+ [ngTemplateOutlet]="chatNoDataTemplate?.templateRef">
6411
+ </ng-template>
6412
+ </div>
6413
+ <ng-template #chatMessageList>
6414
+ <kendo-chat-message-list
6415
+ [messages]="processedMessages"
6416
+ [authorMessageContentTemplate]="authorMessageContentTemplate"
6417
+ [receiverMessageContentTemplate]="receiverMessageContentTemplate"
6418
+ [messageContentTemplate]="messageContentTemplate"
6419
+ [authorMessageTemplate]="authorMessageTemplate"
6420
+ [receiverMessageTemplate]="receiverMessageTemplate"
6421
+ [messageTemplate]="messageTemplate"
6422
+ [timestampTemplate]="timestampTemplate"
6423
+ [statusTemplate]="statusTemplate"
6424
+ [userStatusTemplate]="userStatusTemplate"
6425
+ [localization]="localizationText"
6426
+ [attachmentTemplate]="attachmentTemplate"
6427
+ [authorId]="authorId"
6428
+ (executeAction)="dispatchAction($event)"
6429
+ (resize)="anchor.scrollToBottom()"
6430
+ (navigate)="this.autoScroll = false"
6431
+ >
6432
+ </kendo-chat-message-list>
6433
+ </ng-template>
5262
6434
  </div>
5263
6435
  <kendo-message-box
5264
6436
  #messageBox
@@ -5276,7 +6448,16 @@ class ChatComponent {
5276
6448
  (fileRemove)="fileRemove.emit($event)"
5277
6449
  >
5278
6450
  </kendo-message-box>
5279
- `, isInline: true, dependencies: [{ kind: "directive", type: LocalizedMessagesDirective$1, 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", "messageTemplate", "timestampTemplate", "statusTemplate", "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: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IconWrapperComponent, selector: "kendo-icon-wrapper", inputs: ["name", "svgIcon", "innerCssClass", "customFontClass", "size"], exportAs: ["kendoIconWrapper"] }, { kind: "component", type: i2.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"] }] });
6451
+
6452
+ <kendo-contextmenu
6453
+ #messagesContextMenu
6454
+ [items]="contextMenuActions"
6455
+ [popupAlign]="{ horizontal: 'right', vertical: 'top' }"
6456
+ [collision]="{ horizontal: 'flip', vertical: 'flip'}"
6457
+ (popupClose)="handleMenuClose($event)"
6458
+ (select)="onContextMenuAction($event.item.originalAction)"
6459
+ ></kendo-contextmenu>
6460
+ `, isInline: true, dependencies: [{ kind: "directive", type: LocalizedMessagesDirective$1, 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: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IconWrapperComponent, selector: "kendo-icon-wrapper", inputs: ["name", "svgIcon", "innerCssClass", "customFontClass", "size"], exportAs: ["kendoIconWrapper"] }, { kind: "component", type: i2.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"] }] });
5280
6461
  }
5281
6462
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatComponent, decorators: [{
5282
6463
  type: Component,
@@ -5284,6 +6465,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5284
6465
  providers: [
5285
6466
  LocalizationService,
5286
6467
  ChatService,
6468
+ SuggestionsScrollService,
5287
6469
  {
5288
6470
  provide: L10N_PREFIX,
5289
6471
  useValue: 'kendo.chat'
@@ -5339,10 +6521,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5339
6521
 
5340
6522
  i18n-downloadAllFilesText="kendo.chat.downloadAllFilesText|Sets the text that is displayed in the download section of the message."
5341
6523
  downloadAllFilesText="Download all"
6524
+
6525
+ i18n-previousSuggestionsButtonTitle="kendo.chat.previousSuggestionsButtonTitle|The title of the button that scrolls to the previous suggestions"
6526
+ previousSuggestionsButtonTitle="Scroll left"
6527
+
6528
+ i18n-nextSuggestionsButtonTitle="kendo.chat.nextSuggestionsButtonTitle|The title of the button that scrolls to the next suggestions"
6529
+ nextSuggestionsButtonTitle="Scroll right"
5342
6530
  >
5343
6531
  </ng-container>
5344
6532
 
5345
- <kendo-appbar *ngIf="chatHeaderTemplate" class="k-chat-header" positionMode="sticky">
6533
+ <kendo-appbar *ngIf="chatHeaderTemplate" class="k-chat-header" positionMode="sticky" themeColor="inherit">
5346
6534
  <ng-container *ngTemplateOutlet="chatHeaderTemplate.templateRef"></ng-container>
5347
6535
  </kendo-appbar>
5348
6536
  <div class="k-message-reference k-message-reference-receiver k-message-pinned" *ngIf="pinnedMessage" (click)="scrollToPinnedMessage()">
@@ -5358,7 +6546,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5358
6546
  </div>
5359
6547
  <div
5360
6548
  #messageList
5361
- class="k-message-list k-avatars"
6549
+ class="k-message-list"
5362
6550
  aria-live="polite"
5363
6551
  role="log"
5364
6552
  kendoChatScrollAnchor
@@ -5366,19 +6554,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5366
6554
  #anchor="scrollAnchor"
5367
6555
  [(autoScroll)]="autoScroll"
5368
6556
  >
5369
- <kendo-chat-message-list
5370
- [messages]="processedMessages"
5371
- [messageTemplate]="messageTemplate"
5372
- [timestampTemplate]="timestampTemplate"
5373
- [statusTemplate]="statusTemplate"
5374
- [localization]="localizationText"
5375
- [attachmentTemplate]="attachmentTemplate"
5376
- [authorId]="authorId"
5377
- (executeAction)="dispatchAction($event)"
5378
- (resize)="anchor.scrollToBottom()"
5379
- (navigate)="this.autoScroll = false"
5380
- >
5381
- </kendo-chat-message-list>
6557
+ <div *ngIf="processedMessages && processedMessages.length === 0; else chatMessageList" class="k-message-list-content k-message-list-content-empty">
6558
+ <ng-template
6559
+ [ngTemplateOutlet]="chatNoDataTemplate?.templateRef">
6560
+ </ng-template>
6561
+ </div>
6562
+ <ng-template #chatMessageList>
6563
+ <kendo-chat-message-list
6564
+ [messages]="processedMessages"
6565
+ [authorMessageContentTemplate]="authorMessageContentTemplate"
6566
+ [receiverMessageContentTemplate]="receiverMessageContentTemplate"
6567
+ [messageContentTemplate]="messageContentTemplate"
6568
+ [authorMessageTemplate]="authorMessageTemplate"
6569
+ [receiverMessageTemplate]="receiverMessageTemplate"
6570
+ [messageTemplate]="messageTemplate"
6571
+ [timestampTemplate]="timestampTemplate"
6572
+ [statusTemplate]="statusTemplate"
6573
+ [userStatusTemplate]="userStatusTemplate"
6574
+ [localization]="localizationText"
6575
+ [attachmentTemplate]="attachmentTemplate"
6576
+ [authorId]="authorId"
6577
+ (executeAction)="dispatchAction($event)"
6578
+ (resize)="anchor.scrollToBottom()"
6579
+ (navigate)="this.autoScroll = false"
6580
+ >
6581
+ </kendo-chat-message-list>
6582
+ </ng-template>
5382
6583
  </div>
5383
6584
  <kendo-message-box
5384
6585
  #messageBox
@@ -5396,9 +6597,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5396
6597
  (fileRemove)="fileRemove.emit($event)"
5397
6598
  >
5398
6599
  </kendo-message-box>
6600
+
6601
+ <kendo-contextmenu
6602
+ #messagesContextMenu
6603
+ [items]="contextMenuActions"
6604
+ [popupAlign]="{ horizontal: 'right', vertical: 'top' }"
6605
+ [collision]="{ horizontal: 'flip', vertical: 'flip'}"
6606
+ (popupClose)="handleMenuClose($event)"
6607
+ (select)="onContextMenuAction($event.item.originalAction)"
6608
+ ></kendo-contextmenu>
5399
6609
  `,
5400
6610
  standalone: true,
5401
- imports: [LocalizedMessagesDirective$1, ScrollAnchorDirective, MessageListComponent, MessageBoxComponent, MessageReferenceComponent, AppBarComponent, NgTemplateOutlet, NgIf, IconWrapperComponent, KENDO_BUTTON]
6611
+ imports: [LocalizedMessagesDirective$1, ScrollAnchorDirective, MessageListComponent, MessageBoxComponent, MessageReferenceComponent, AppBarComponent, NgTemplateOutlet, NgIf, IconWrapperComponent, KENDO_BUTTON, ContextMenuComponent]
5402
6612
  }]
5403
6613
  }], ctorParameters: () => [{ type: i1.LocalizationService }, { type: i0.NgZone }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: ChatService }], propDecorators: { messages: [{
5404
6614
  type: Input
@@ -5414,6 +6624,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5414
6624
  type: Input
5415
6625
  }], messageWidthMode: [{
5416
6626
  type: Input
6627
+ }], timestampVisibility: [{
6628
+ type: Input
6629
+ }], showUsername: [{
6630
+ type: Input
6631
+ }], showAvatar: [{
6632
+ type: Input
5417
6633
  }], allowMessageCollapse: [{
5418
6634
  type: Input
5419
6635
  }], enableSpeechToText: [{
@@ -5424,13 +6640,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5424
6640
  type: Input
5425
6641
  }], inputValue: [{
5426
6642
  type: Input
6643
+ }], authorMessageSettings: [{
6644
+ type: Input
6645
+ }], receiverMessageSettings: [{
6646
+ type: Input
5427
6647
  }], messageContextMenuActions: [{
5428
6648
  type: Input
5429
6649
  }], fileActions: [{
5430
6650
  type: Input
5431
- }], suggestions: [{
6651
+ }], messageFilesLayout: [{
6652
+ type: Input
6653
+ }], suggestionsLayout: [{
5432
6654
  type: Input
5433
- }], messageToolbarVisibility: [{
6655
+ }], quickActionsLayout: [{
6656
+ type: Input
6657
+ }], suggestions: [{
5434
6658
  type: Input
5435
6659
  }], sendButtonSettings: [{
5436
6660
  type: Input
@@ -5464,12 +6688,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5464
6688
  }], dirAttr: [{
5465
6689
  type: HostBinding,
5466
6690
  args: ['attr.dir']
6691
+ }], messagesContextMenu: [{
6692
+ type: ViewChild,
6693
+ args: ['messagesContextMenu']
5467
6694
  }], attachmentTemplate: [{
5468
6695
  type: ContentChild,
5469
6696
  args: [AttachmentTemplateDirective]
5470
6697
  }], chatHeaderTemplate: [{
5471
6698
  type: ContentChild,
5472
6699
  args: [ChatHeaderTemplateDirective]
6700
+ }], chatNoDataTemplate: [{
6701
+ type: ContentChild,
6702
+ args: [NoDataTemplateDirective]
6703
+ }], authorMessageContentTemplate: [{
6704
+ type: ContentChild,
6705
+ args: [AuthorMessageContentTemplateDirective]
6706
+ }], receiverMessageContentTemplate: [{
6707
+ type: ContentChild,
6708
+ args: [ReceiverMessageContentTemplateDirective]
6709
+ }], messageContentTemplate: [{
6710
+ type: ContentChild,
6711
+ args: [MessageContentTemplateDirective]
6712
+ }], authorMessageTemplate: [{
6713
+ type: ContentChild,
6714
+ args: [AuthorMessageTemplateDirective]
6715
+ }], receiverMessageTemplate: [{
6716
+ type: ContentChild,
6717
+ args: [ReceiverMessageTemplateDirective]
5473
6718
  }], messageTemplate: [{
5474
6719
  type: ContentChild,
5475
6720
  args: [MessageTemplateDirective]
@@ -5485,6 +6730,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5485
6730
  }], messageBoxTemplate: [{
5486
6731
  type: ContentChild,
5487
6732
  args: [ChatMessageBoxTemplateDirective]
6733
+ }], userStatusTemplate: [{
6734
+ type: ContentChild,
6735
+ args: [ChatUserStatusTemplateDirective]
5488
6736
  }], messageBox: [{
5489
6737
  type: ViewChild,
5490
6738
  args: ['messageBox']
@@ -6724,13 +7972,20 @@ const KENDO_CHAT = [
6724
7972
  ChatComponent,
6725
7973
  CustomMessagesComponent,
6726
7974
  AttachmentTemplateDirective,
7975
+ AuthorMessageContentTemplateDirective,
7976
+ ReceiverMessageContentTemplateDirective,
7977
+ MessageContentTemplateDirective,
7978
+ AuthorMessageTemplateDirective,
7979
+ ReceiverMessageTemplateDirective,
6727
7980
  MessageTemplateDirective,
6728
7981
  HeroCardComponent,
6729
7982
  ChatMessageBoxTemplateDirective,
6730
7983
  ChatHeaderTemplateDirective,
7984
+ NoDataTemplateDirective,
6731
7985
  ChatTimestampTemplateDirective,
6732
7986
  ChatStatusTemplateDirective,
6733
- ChatSuggestionTemplateDirective
7987
+ ChatSuggestionTemplateDirective,
7988
+ ChatUserStatusTemplateDirective
6734
7989
  ];
6735
7990
  /**
6736
7991
  * Utility array that contains all InlineAIPrompt related components and directives.
@@ -6837,7 +8092,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
6837
8092
  */
6838
8093
  class ChatModule {
6839
8094
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
6840
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: ChatModule, imports: [ChatComponent, CustomMessagesComponent, AttachmentTemplateDirective, MessageTemplateDirective, HeroCardComponent, ChatMessageBoxTemplateDirective, ChatHeaderTemplateDirective, ChatTimestampTemplateDirective, ChatStatusTemplateDirective, ChatSuggestionTemplateDirective], exports: [ChatComponent, CustomMessagesComponent, AttachmentTemplateDirective, MessageTemplateDirective, HeroCardComponent, ChatMessageBoxTemplateDirective, ChatHeaderTemplateDirective, ChatTimestampTemplateDirective, ChatStatusTemplateDirective, ChatSuggestionTemplateDirective] });
8095
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: ChatModule, imports: [ChatComponent, CustomMessagesComponent, AttachmentTemplateDirective, AuthorMessageContentTemplateDirective, ReceiverMessageContentTemplateDirective, MessageContentTemplateDirective, AuthorMessageTemplateDirective, ReceiverMessageTemplateDirective, MessageTemplateDirective, HeroCardComponent, ChatMessageBoxTemplateDirective, ChatHeaderTemplateDirective, NoDataTemplateDirective, ChatTimestampTemplateDirective, ChatStatusTemplateDirective, ChatSuggestionTemplateDirective, ChatUserStatusTemplateDirective], exports: [ChatComponent, CustomMessagesComponent, AttachmentTemplateDirective, AuthorMessageContentTemplateDirective, ReceiverMessageContentTemplateDirective, MessageContentTemplateDirective, AuthorMessageTemplateDirective, ReceiverMessageTemplateDirective, MessageTemplateDirective, HeroCardComponent, ChatMessageBoxTemplateDirective, ChatHeaderTemplateDirective, NoDataTemplateDirective, ChatTimestampTemplateDirective, ChatStatusTemplateDirective, ChatSuggestionTemplateDirective, ChatUserStatusTemplateDirective] });
6841
8096
  static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatModule, providers: [IconsService, ResizeBatchService], imports: [ChatComponent, HeroCardComponent] });
6842
8097
  }
6843
8098
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatModule, decorators: [{
@@ -6870,7 +8125,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
6870
8125
  */
6871
8126
  class ConversationalUIModule {
6872
8127
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ConversationalUIModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
6873
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: ConversationalUIModule, imports: [AIPromptComponent, PromptViewComponent, OutputViewComponent, CommandViewComponent, CustomViewComponent, AIPromptCustomMessagesComponent, AIPromptToolbarActionsDirective, AIPromptToolbarFocusableDirective, AIPromptOutputTemplateDirective, AIPromptOutputBodyTemplateDirective, ChatComponent, CustomMessagesComponent, AttachmentTemplateDirective, MessageTemplateDirective, HeroCardComponent, ChatMessageBoxTemplateDirective, ChatHeaderTemplateDirective, ChatTimestampTemplateDirective, ChatStatusTemplateDirective, ChatSuggestionTemplateDirective, InlineAIPromptComponent, InlineAIPromptOutputTemplateDirective, InlineAIPromptCustomMessagesComponent], exports: [AIPromptComponent, PromptViewComponent, OutputViewComponent, CommandViewComponent, CustomViewComponent, AIPromptCustomMessagesComponent, AIPromptToolbarActionsDirective, AIPromptToolbarFocusableDirective, AIPromptOutputTemplateDirective, AIPromptOutputBodyTemplateDirective, ChatComponent, CustomMessagesComponent, AttachmentTemplateDirective, MessageTemplateDirective, HeroCardComponent, ChatMessageBoxTemplateDirective, ChatHeaderTemplateDirective, ChatTimestampTemplateDirective, ChatStatusTemplateDirective, ChatSuggestionTemplateDirective, InlineAIPromptComponent, InlineAIPromptOutputTemplateDirective, InlineAIPromptCustomMessagesComponent] });
8128
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: ConversationalUIModule, imports: [AIPromptComponent, PromptViewComponent, OutputViewComponent, CommandViewComponent, CustomViewComponent, AIPromptCustomMessagesComponent, AIPromptToolbarActionsDirective, AIPromptToolbarFocusableDirective, AIPromptOutputTemplateDirective, AIPromptOutputBodyTemplateDirective, ChatComponent, CustomMessagesComponent, AttachmentTemplateDirective, AuthorMessageContentTemplateDirective, ReceiverMessageContentTemplateDirective, MessageContentTemplateDirective, AuthorMessageTemplateDirective, ReceiverMessageTemplateDirective, MessageTemplateDirective, HeroCardComponent, ChatMessageBoxTemplateDirective, ChatHeaderTemplateDirective, NoDataTemplateDirective, ChatTimestampTemplateDirective, ChatStatusTemplateDirective, ChatSuggestionTemplateDirective, ChatUserStatusTemplateDirective, InlineAIPromptComponent, InlineAIPromptOutputTemplateDirective, InlineAIPromptCustomMessagesComponent], exports: [AIPromptComponent, PromptViewComponent, OutputViewComponent, CommandViewComponent, CustomViewComponent, AIPromptCustomMessagesComponent, AIPromptToolbarActionsDirective, AIPromptToolbarFocusableDirective, AIPromptOutputTemplateDirective, AIPromptOutputBodyTemplateDirective, ChatComponent, CustomMessagesComponent, AttachmentTemplateDirective, AuthorMessageContentTemplateDirective, ReceiverMessageContentTemplateDirective, MessageContentTemplateDirective, AuthorMessageTemplateDirective, ReceiverMessageTemplateDirective, MessageTemplateDirective, HeroCardComponent, ChatMessageBoxTemplateDirective, ChatHeaderTemplateDirective, NoDataTemplateDirective, ChatTimestampTemplateDirective, ChatStatusTemplateDirective, ChatSuggestionTemplateDirective, ChatUserStatusTemplateDirective, InlineAIPromptComponent, InlineAIPromptOutputTemplateDirective, InlineAIPromptCustomMessagesComponent] });
6874
8129
  static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ConversationalUIModule, providers: [IconsService, PopupService, ResizeBatchService, DialogContainerService, DialogService, WindowService, WindowContainerService], imports: [AIPromptComponent, PromptViewComponent, OutputViewComponent, CommandViewComponent, ChatComponent, HeroCardComponent, InlineAIPromptComponent] });
6875
8130
  }
6876
8131
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ConversationalUIModule, decorators: [{
@@ -7058,5 +8313,5 @@ class InlineAIPromptSettings {
7058
8313
  * Generated bundle index. Do not edit.
7059
8314
  */
7060
8315
 
7061
- export { AIPromptComponent, AIPromptCustomMessagesComponent, AIPromptModule, AIPromptOutputBodyTemplateDirective, AIPromptOutputTemplateDirective, AIPromptToolbarActionsDirective, AIPromptToolbarFocusableDirective, AttachmentTemplateDirective, ChatComponent, ChatHeaderTemplateDirective, ChatMessageBoxTemplateDirective, ChatModule, ChatStatusTemplateDirective, ChatSuggestionTemplateDirective, ChatTimestampTemplateDirective, CommandViewComponent, ConversationalUIModule, CustomMessagesComponent, CustomViewComponent, ExecuteActionEvent, HeroCardComponent, InlineAIPromptComponent, InlineAIPromptCustomMessagesComponent, InlineAIPromptModule, InlineAIPromptOutputTemplateDirective, InlineAIPromptService, InlineAIPromptSettings, KENDO_AIPROMPT, KENDO_CHAT, KENDO_CONVERSATIONALUI, KENDO_INLINEAIPROMPT, MessageTemplateDirective, OutputViewComponent, PromptViewComponent, SendMessageEvent };
8316
+ export { AIPromptComponent, AIPromptCustomMessagesComponent, AIPromptModule, AIPromptOutputBodyTemplateDirective, AIPromptOutputTemplateDirective, AIPromptToolbarActionsDirective, AIPromptToolbarFocusableDirective, AttachmentTemplateDirective, AuthorMessageContentTemplateDirective, AuthorMessageTemplateDirective, ChatComponent, ChatHeaderTemplateDirective, ChatMessageBoxTemplateDirective, ChatModule, ChatStatusTemplateDirective, ChatSuggestionTemplateDirective, ChatTimestampTemplateDirective, ChatUserStatusTemplateDirective, CommandViewComponent, ConversationalUIModule, CustomMessagesComponent, CustomViewComponent, ExecuteActionEvent, HeroCardComponent, InlineAIPromptComponent, InlineAIPromptCustomMessagesComponent, InlineAIPromptModule, InlineAIPromptOutputTemplateDirective, InlineAIPromptService, InlineAIPromptSettings, KENDO_AIPROMPT, KENDO_CHAT, KENDO_CONVERSATIONALUI, KENDO_INLINEAIPROMPT, MessageContentTemplateDirective, MessageTemplateDirective, NoDataTemplateDirective, OutputViewComponent, PromptViewComponent, ReceiverMessageContentTemplateDirective, ReceiverMessageTemplateDirective, SendMessageEvent };
7062
8317