@progress/kendo-angular-conversational-ui 20.1.2-develop.1 → 21.0.0-develop.10

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 (103) hide show
  1. package/chat/api/files-layout.d.ts +12 -0
  2. package/chat/api/index.d.ts +3 -1
  3. package/chat/api/message-settings.interface.d.ts +33 -0
  4. package/chat/api/message.interface.d.ts +5 -1
  5. package/chat/api/suggestions-layout.d.ts +20 -0
  6. package/chat/chat.component.d.ts +92 -34
  7. package/chat/chat.directives.d.ts +18 -0
  8. package/chat/chat.module.d.ts +15 -8
  9. package/chat/common/chat.service.d.ts +32 -3
  10. package/chat/common/models/model-fields.d.ts +0 -6
  11. package/chat/common/scroll-button.component.d.ts +32 -0
  12. package/chat/common/scroll.service.d.ts +39 -0
  13. package/chat/common/utils.d.ts +13 -1
  14. package/chat/l10n/messages.d.ts +9 -1
  15. package/chat/message-list.component.d.ts +20 -3
  16. package/chat/message.component.d.ts +40 -16
  17. package/chat/suggested-actions.component.d.ts +30 -5
  18. package/chat/templates/author-message-content-template.directive.d.ts +28 -0
  19. package/chat/templates/author-message-template.directive.d.ts +28 -0
  20. package/chat/templates/message-content-template.directive.d.ts +28 -0
  21. package/chat/templates/message-template.directive.d.ts +1 -1
  22. package/chat/templates/no-data-template.directive.d.ts +27 -0
  23. package/chat/templates/receiver-message-content-template.directive.d.ts +28 -0
  24. package/chat/templates/receiver-message-template.directive.d.ts +28 -0
  25. package/chat/templates/user-status-template.directive.d.ts +27 -0
  26. package/codemods/template-transformer/index.js +1 -2
  27. package/codemods/utils.js +2 -2
  28. package/codemods/v20/chat-user.js +1 -1
  29. package/conversational-ui.module.d.ts +18 -11
  30. package/directives.d.ts +9 -2
  31. package/esm2022/ai-prompt/aiprompt.component.mjs +13 -11
  32. package/esm2022/ai-prompt/aiprompt.module.mjs +4 -4
  33. package/esm2022/ai-prompt/common/aiprompt.service.mjs +3 -3
  34. package/esm2022/ai-prompt/common/output-card.component.mjs +4 -4
  35. package/esm2022/ai-prompt/common/toolbar-focusable.directive.mjs +4 -4
  36. package/esm2022/ai-prompt/common/toolbar-navigation.service.mjs +4 -4
  37. package/esm2022/ai-prompt/localization/custom-messages.component.mjs +4 -4
  38. package/esm2022/ai-prompt/localization/localized-messages.directive.mjs +4 -4
  39. package/esm2022/ai-prompt/localization/messages.mjs +3 -3
  40. package/esm2022/ai-prompt/templates/aiprompt-output-body-template.directive.mjs +4 -4
  41. package/esm2022/ai-prompt/templates/aiprompt-output-template.directive.mjs +4 -4
  42. package/esm2022/ai-prompt/templates/toolbar-actions.template.mjs +5 -5
  43. package/esm2022/ai-prompt/views/base-view.mjs +5 -5
  44. package/esm2022/ai-prompt/views/command-view.component.mjs +4 -4
  45. package/esm2022/ai-prompt/views/custom-view.component.mjs +4 -4
  46. package/esm2022/ai-prompt/views/output-view.component.mjs +4 -4
  47. package/esm2022/ai-prompt/views/prompt-view.component.mjs +4 -4
  48. package/esm2022/chat/api/index.mjs +3 -1
  49. package/{chat/api/message-toolbar-visibility.d.ts → esm2022/chat/api/message-settings.interface.mjs} +1 -4
  50. package/esm2022/chat/api/suggestions-layout.mjs +5 -0
  51. package/esm2022/chat/attachment.component.mjs +3 -3
  52. package/esm2022/chat/builtin-actions.mjs +2 -0
  53. package/esm2022/chat/cards/hero-card.component.mjs +3 -3
  54. package/esm2022/chat/chat-file.component.mjs +6 -6
  55. package/esm2022/chat/chat.component.mjs +268 -74
  56. package/esm2022/chat/chat.directives.mjs +18 -0
  57. package/esm2022/chat/chat.module.mjs +18 -11
  58. package/esm2022/chat/common/chat.service.mjs +86 -7
  59. package/esm2022/chat/common/models/default-model-fields.mjs +0 -1
  60. package/esm2022/chat/common/scroll-anchor.directive.mjs +4 -4
  61. package/esm2022/chat/common/scroll-button.component.mjs +81 -0
  62. package/esm2022/chat/common/scroll.service.mjs +110 -0
  63. package/esm2022/chat/common/utils.mjs +22 -3
  64. package/esm2022/chat/l10n/custom-messages.component.mjs +4 -4
  65. package/esm2022/chat/l10n/localized-messages.directive.mjs +4 -4
  66. package/esm2022/chat/l10n/messages.mjs +15 -3
  67. package/esm2022/chat/message-attachments.component.mjs +6 -6
  68. package/esm2022/chat/message-box.component.mjs +12 -7
  69. package/esm2022/chat/message-list.component.mjs +168 -22
  70. package/esm2022/chat/message-reference-content.component.mjs +4 -4
  71. package/esm2022/chat/message.component.mjs +491 -328
  72. package/esm2022/chat/suggested-actions.component.mjs +299 -81
  73. package/esm2022/chat/templates/attachment-template.directive.mjs +5 -5
  74. package/esm2022/chat/templates/author-message-content-template.directive.mjs +39 -0
  75. package/esm2022/chat/templates/author-message-template.directive.mjs +39 -0
  76. package/esm2022/chat/templates/header-template.directive.mjs +4 -4
  77. package/esm2022/chat/templates/message-box.directive.mjs +4 -4
  78. package/esm2022/chat/templates/message-content-template.directive.mjs +39 -0
  79. package/esm2022/chat/templates/message-template.directive.mjs +6 -6
  80. package/esm2022/chat/templates/no-data-template.directive.mjs +38 -0
  81. package/esm2022/chat/templates/receiver-message-content-template.directive.mjs +39 -0
  82. package/esm2022/chat/templates/receiver-message-template.directive.mjs +39 -0
  83. package/esm2022/chat/templates/status-template.directive.mjs +4 -4
  84. package/esm2022/chat/templates/suggestion-template.directive.mjs +4 -4
  85. package/esm2022/chat/templates/timestamp-template.directive.mjs +5 -5
  86. package/esm2022/chat/templates/user-status-template.directive.mjs +38 -0
  87. package/esm2022/conversational-ui.module.mjs +21 -14
  88. package/esm2022/directives.mjs +15 -1
  89. package/esm2022/index.mjs +7 -0
  90. package/esm2022/inline-ai-prompt/inlineaiprompt-content.component.mjs +5 -5
  91. package/esm2022/inline-ai-prompt/inlineaiprompt.component.mjs +4 -4
  92. package/esm2022/inline-ai-prompt/inlineaiprompt.module.mjs +4 -4
  93. package/esm2022/inline-ai-prompt/inlineaiprompt.service.mjs +4 -4
  94. package/esm2022/inline-ai-prompt/localization/custom-messages.component.mjs +4 -4
  95. package/esm2022/inline-ai-prompt/localization/localized-messages.directive.mjs +4 -4
  96. package/esm2022/inline-ai-prompt/localization/messages.mjs +3 -3
  97. package/esm2022/inline-ai-prompt/output-template.directive.mjs +5 -5
  98. package/esm2022/package-metadata.mjs +2 -2
  99. package/fesm2022/progress-kendo-angular-conversational-ui.mjs +1969 -739
  100. package/index.d.ts +7 -0
  101. package/package.json +18 -18
  102. package/schematics/ngAdd/index.js +1 -1
  103. /package/esm2022/chat/api/{message-toolbar-visibility.mjs → files-layout.mjs} +0 -0
@@ -5,7 +5,7 @@
5
5
  import { ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, Input, Output, Renderer2, ViewChild } from '@angular/core';
6
6
  import { NgIf, NgFor, NgTemplateOutlet } from '@angular/common';
7
7
  import { LocalizationService } from '@progress/kendo-angular-l10n';
8
- import { closest, isPresent, Keys } from '@progress/kendo-angular-common';
8
+ import { closest, guid, isPresent, Keys } from '@progress/kendo-angular-common';
9
9
  import { paperPlaneIcon, paperclipIcon, fileIcon, xIcon } from '@progress/kendo-svg-icons';
10
10
  import { ButtonComponent, SpeechToTextButtonComponent } from '@progress/kendo-angular-buttons';
11
11
  import { SendMessageEvent } from './api/post-message-event';
@@ -67,7 +67,9 @@ export class MessageBoxComponent {
67
67
  const elRef = this.element.nativeElement;
68
68
  this.subs.add(this.renderer.listen(elRef, 'focusout', event => this.onBlur(event)));
69
69
  this.subs.add(this.chatService.contextMenuAction$.subscribe((action) => {
70
- action.action.id === 'reply' && this.messageBoxInput.focus();
70
+ if (action.action.id === 'reply') {
71
+ this.messageBoxInput.focus();
72
+ }
71
73
  }));
72
74
  }
73
75
  ngOnDestroy() {
@@ -82,6 +84,7 @@ export class MessageBoxComponent {
82
84
  return;
83
85
  }
84
86
  const message = {
87
+ id: guid(),
85
88
  text: this.inputValue,
86
89
  timestamp: new Date(),
87
90
  author: { id: this.authorId },
@@ -213,12 +216,13 @@ export class MessageBoxComponent {
213
216
  dispatchSuggestion(suggestion) {
214
217
  this.executeSuggestion.emit(suggestion);
215
218
  }
216
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MessageBoxComponent, deps: [{ token: i1.ChatService }, { token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
217
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: MessageBoxComponent, isStandalone: true, selector: "kendo-message-box", inputs: { authorId: "authorId", autoScroll: "autoScroll", suggestions: "suggestions", placeholder: "placeholder", inputValue: "inputValue", localization: "localization", messageBoxTemplate: "messageBoxTemplate", suggestionTemplate: "suggestionTemplate" }, outputs: { sendMessage: "sendMessage", executeSuggestion: "executeSuggestion", fileSelect: "fileSelect", fileRemove: "fileRemove" }, host: { properties: { "style.border-color": "this.borderColor", "class.k-message-box-wrapper": "this.messageBoxWrapperClass" } }, viewQueries: [{ propertyName: "messageBoxInput", first: true, predicate: ["messageBoxInput"], descendants: true }, { propertyName: "fileSelectComponent", first: true, predicate: ["fileSelect"], descendants: true }, { propertyName: "suggestedActionsComponent", first: true, predicate: SuggestedActionsComponent, descendants: true }], ngImport: i0, template: `
219
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageBoxComponent, deps: [{ token: i1.ChatService }, { token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
220
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MessageBoxComponent, isStandalone: true, selector: "kendo-message-box", inputs: { authorId: "authorId", autoScroll: "autoScroll", suggestions: "suggestions", placeholder: "placeholder", inputValue: "inputValue", localization: "localization", messageBoxTemplate: "messageBoxTemplate", suggestionTemplate: "suggestionTemplate" }, outputs: { sendMessage: "sendMessage", executeSuggestion: "executeSuggestion", fileSelect: "fileSelect", fileRemove: "fileRemove" }, host: { properties: { "style.border-color": "this.borderColor", "class.k-message-box-wrapper": "this.messageBoxWrapperClass" } }, viewQueries: [{ propertyName: "messageBoxInput", first: true, predicate: ["messageBoxInput"], descendants: true }, { propertyName: "fileSelectComponent", first: true, predicate: ["fileSelect"], descendants: true }, { propertyName: "suggestedActionsComponent", first: true, predicate: SuggestedActionsComponent, descendants: true }], ngImport: i0, template: `
218
221
  <kendo-chat-suggested-actions
219
222
  *ngIf="suggestions?.length > 0"
220
223
  #suggestedActions
221
224
  [suggestions]="suggestions"
225
+ type="suggestion"
222
226
  [suggestionTemplate]="suggestionTemplate"
223
227
  [tabbable]="true"
224
228
  (dispatchSuggestion)="dispatchSuggestion($event)"
@@ -319,9 +323,9 @@ export class MessageBoxComponent {
319
323
  [showFileList]="false"
320
324
  (select)="handleFileSelect($event)"
321
325
  ></kendo-fileselect>
322
- `, 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"] }] });
326
+ `, 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"] }] });
323
327
  }
324
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MessageBoxComponent, decorators: [{
328
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageBoxComponent, decorators: [{
325
329
  type: Component,
326
330
  args: [{
327
331
  selector: 'kendo-message-box',
@@ -330,6 +334,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
330
334
  *ngIf="suggestions?.length > 0"
331
335
  #suggestedActions
332
336
  [suggestions]="suggestions"
337
+ type="suggestion"
333
338
  [suggestionTemplate]="suggestionTemplate"
334
339
  [tabbable]="true"
335
340
  (dispatchSuggestion)="dispatchSuggestion($event)"
@@ -434,7 +439,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
434
439
  standalone: true,
435
440
  imports: [NgIf, NgFor, ButtonComponent, FormsModule, NgTemplateOutlet, TextAreaComponent, MessageReferenceComponent, TextAreaSuffixComponent, TextAreaPrefixComponent, SpeechToTextButtonComponent, InputSpacerComponent, FileSelectComponent, SuggestedActionsComponent, ChatFileComponent]
436
441
  }]
437
- }], ctorParameters: function () { return [{ type: i1.ChatService }, { type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.Renderer2 }]; }, propDecorators: { borderColor: [{
442
+ }], ctorParameters: () => [{ type: i1.ChatService }, { type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { borderColor: [{
438
443
  type: HostBinding,
439
444
  args: ['style.border-color']
440
445
  }], messageBoxWrapperClass: [{
@@ -14,7 +14,7 @@ import { closest, DOWNLOAD_ALL_SELECTOR, FILE_ACTION_BTN_SELECTOR } from './comm
14
14
  import { ChatItem } from './chat-item';
15
15
  import { chatView, isAuthor } from './chat-view';
16
16
  import { AttachmentTemplateDirective } from './templates/attachment-template.directive';
17
- import { MessageTemplateDirective } from './templates/message-template.directive';
17
+ import { MessageContentTemplateDirective } from './templates/message-content-template.directive';
18
18
  import { ChatTimestampTemplateDirective } from './templates/timestamp-template.directive';
19
19
  import { Subscription } from 'rxjs';
20
20
  import { SuggestedActionsComponent } from './suggested-actions.component';
@@ -23,6 +23,12 @@ import { MessageAttachmentsComponent } from './message-attachments.component';
23
23
  import { AttachmentComponent } from './attachment.component';
24
24
  import { ChatService } from './common/chat.service';
25
25
  import { ChatStatusTemplateDirective } from './templates/status-template.directive';
26
+ import { ChatUserStatusTemplateDirective } from './templates/user-status-template.directive';
27
+ import { MessageTemplateDirective } from './templates/message-template.directive';
28
+ import { AuthorMessageContentTemplateDirective } from './templates/author-message-content-template.directive';
29
+ import { ReceiverMessageContentTemplateDirective } from './templates/receiver-message-content-template.directive';
30
+ import { AuthorMessageTemplateDirective } from './templates/author-message-template.directive';
31
+ import { ReceiverMessageTemplateDirective } from './templates/receiver-message-template.directive';
26
32
  import * as i0 from "@angular/core";
27
33
  import * as i1 from "@progress/kendo-angular-intl";
28
34
  import * as i2 from "./common/chat.service";
@@ -44,9 +50,15 @@ export class MessageListComponent {
44
50
  return this._messages;
45
51
  }
46
52
  attachmentTemplate;
53
+ authorMessageContentTemplate;
54
+ receiverMessageContentTemplate;
55
+ messageContentTemplate;
56
+ authorMessageTemplate;
57
+ receiverMessageTemplate;
47
58
  messageTemplate;
48
59
  timestampTemplate;
49
60
  statusTemplate;
61
+ userStatusTemplate;
50
62
  localization;
51
63
  authorId;
52
64
  executeAction = new EventEmitter();
@@ -64,6 +76,7 @@ export class MessageListComponent {
64
76
  [Keys.ArrowUp]: (e) => this.navigateTo(e, -1),
65
77
  [Keys.ArrowDown]: (e) => this.navigateTo(e, 1),
66
78
  [Keys.Tab]: (e) => this.onTabKeyDown(e),
79
+ [Keys.F10]: (e) => e.shiftKey && this.openContextMenu(e),
67
80
  };
68
81
  constructor(element, intl, renderer, chatService, cdr) {
69
82
  this.element = element;
@@ -76,9 +89,25 @@ export class MessageListComponent {
76
89
  const elRef = this.element.nativeElement;
77
90
  this.subs.add(this.renderer.listen(elRef, 'keydown', event => this.onKeydown(event)));
78
91
  this.subs.add(this.renderer.listen(elRef, 'focusout', event => this.onBlur(event)));
92
+ this.subs.add(this.renderer.listen(elRef, 'click', event => {
93
+ const messageComponent = this.findMessageComponentFromEvent(event);
94
+ if (messageComponent) {
95
+ this.select(messageComponent, event);
96
+ }
97
+ }));
98
+ this.subs.add(this.renderer.listen(elRef, 'contextmenu', event => {
99
+ event.preventDefault();
100
+ const messageComponent = this.findMessageComponentFromEvent(event);
101
+ if (messageComponent) {
102
+ this.onContextMenuClick(messageComponent.message, event, messageComponent);
103
+ }
104
+ }));
79
105
  this.subs.add(this.chatService.replyReferenceClick$.subscribe((messageId) => {
80
106
  this.scrollToAndSelectMessage(messageId);
81
107
  }));
108
+ this.subs.add(this.chatService.contextMenuVisibilityChange$.subscribe((isVisible) => {
109
+ this.handleMenuClose(isVisible);
110
+ }));
82
111
  }
83
112
  ngAfterViewInit() {
84
113
  this.selectedItem = this.items.last;
@@ -92,9 +121,31 @@ export class MessageListComponent {
92
121
  onClick(message, event) {
93
122
  this.select(message, event);
94
123
  }
124
+ onContextMenuClick(message, event, messageElement) {
125
+ this.chatService.calculateContextMenuActions(this.isOwnMessage(message));
126
+ if (this.chatService.calculatedContextMenuActions.length > 0) {
127
+ this.chatService.messagesContextMenu.show({
128
+ left: event.pageX,
129
+ top: event.pageY,
130
+ });
131
+ this.chatService.activeMessageElement = messageElement;
132
+ this.chatService.activeMessage = message;
133
+ this.chatService.active = true;
134
+ this.chatService.selectOnMenuClose = this.chatService.activeMessageElement?.selected;
135
+ this.chatService.emit('contextMenuVisibilityChange', true);
136
+ }
137
+ }
95
138
  formatTimeStamp(date) {
96
139
  return this.intl.formatDate(date, { date: 'full' });
97
140
  }
141
+ calculateMessageWidthMode(message) {
142
+ const isOwn = this.isOwnMessage(message);
143
+ const messageSettings = isOwn ? this.chatService.authorMessageSettings : this.chatService.receiverMessageSettings;
144
+ if (messageSettings?.messageWidthMode) {
145
+ return messageSettings.messageWidthMode === 'full';
146
+ }
147
+ return this.chatService.messageWidthMode === 'full';
148
+ }
98
149
  onKeydown(e) {
99
150
  // On some keyboards Numpad keys are used for Home/End/PageUp/PageDown.
100
151
  const code = normalizeNumpadKeys(e);
@@ -154,6 +205,9 @@ export class MessageListComponent {
154
205
  this.select(null);
155
206
  }
156
207
  }
208
+ textFor(key) {
209
+ return this.localization.get(key);
210
+ }
157
211
  onHomeOrEndKeyDown(key) {
158
212
  const items = this.items.toArray();
159
213
  if (key === 'home') {
@@ -200,11 +254,49 @@ export class MessageListComponent {
200
254
  this.select(chatItem);
201
255
  }
202
256
  }
203
- textFor(key) {
204
- return this.localization.get(key);
257
+ openContextMenu(event) {
258
+ event.preventDefault();
259
+ const messageComponent = this.findMessageComponentFromActiveElement();
260
+ if (messageComponent) {
261
+ const messageContentElement = messageComponent.element.nativeElement.querySelector('.k-chat-bubble');
262
+ if (messageContentElement) {
263
+ const rect = messageContentElement?.getBoundingClientRect();
264
+ this.onContextMenuClick(messageComponent.message, {
265
+ pageX: rect.left + (rect.width / 2) + window.scrollX,
266
+ pageY: rect.top + (rect.height / 2) + window.scrollY
267
+ }, messageComponent);
268
+ }
269
+ else {
270
+ const messageElement = messageComponent.element.nativeElement;
271
+ const rect = messageElement?.getBoundingClientRect();
272
+ this.onContextMenuClick(messageComponent.message, {
273
+ pageX: rect.left + (rect.width / 2) + window.scrollX,
274
+ pageY: rect.top + (rect.height / 2) + window.scrollY
275
+ }, messageComponent);
276
+ }
277
+ }
278
+ }
279
+ findMessageComponentFromActiveElement() {
280
+ const activeElement = document.activeElement;
281
+ const messageComponents = this.items.filter(item => item instanceof MessageComponent);
282
+ return messageComponents.find(component => {
283
+ const componentElement = component.element?.nativeElement;
284
+ return componentElement && (componentElement === activeElement || componentElement.contains(activeElement));
285
+ });
286
+ }
287
+ findMessageComponentFromEvent(event) {
288
+ const target = event.target;
289
+ const clickedElement = target?.closest('.k-message');
290
+ if (!clickedElement)
291
+ return undefined;
292
+ const messageComponents = this.items.filter(item => item instanceof MessageComponent);
293
+ return messageComponents.find(component => {
294
+ const componentElement = component.element?.nativeElement;
295
+ return componentElement && (componentElement === clickedElement || componentElement.contains(clickedElement));
296
+ });
205
297
  }
206
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MessageListComponent, deps: [{ token: i0.ElementRef }, { token: i1.IntlService }, { token: i0.Renderer2 }, { token: i2.ChatService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
207
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", 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: `
298
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageListComponent, deps: [{ token: i0.ElementRef }, { token: i1.IntlService }, { token: i0.Renderer2 }, { token: i2.ChatService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
299
+ 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: `
208
300
  <ng-container *ngFor="let group of view; last as lastGroup; trackBy: trackGroup">
209
301
  <ng-container [ngSwitch]="group.type">
210
302
  <div
@@ -225,10 +317,10 @@ export class MessageListComponent {
225
317
  [class.k-message-group-sender]="isOwnMessage(group.messages[0])"
226
318
  [class.k-message-group-receiver]="!isOwnMessage(group.messages[0])"
227
319
  [class.k-no-avatar]="!group.author.avatarUrl"
228
- [class.k-message-group-full-width]="chatService.messageWidthMode === 'full'"
320
+ [class.k-message-group-full-width]="calculateMessageWidthMode(group.messages[0])"
229
321
  >
230
322
  <div
231
- *ngIf="group.author.avatarUrl"
323
+ *ngIf="!userStatusTemplate?.templateRef && group.author.avatarUrl"
232
324
  class="k-avatar k-avatar-md k-avatar-solid k-avatar-solid-primary k-rounded-full"
233
325
  >
234
326
  <span class="k-avatar-image">
@@ -238,6 +330,25 @@ export class MessageListComponent {
238
330
  />
239
331
  </span>
240
332
  </div>
333
+ <div class="k-chat-user-status-wrapper" *ngIf="group.author.avatarUrl && userStatusTemplate?.templateRef">
334
+ <div
335
+ class="k-avatar k-avatar-md k-avatar-solid k-avatar-solid-primary k-rounded-full"
336
+ >
337
+ <span class="k-avatar-image">
338
+ <img
339
+ [attr.src]="group.author.avatarUrl"
340
+ [alt]="group.author.avatarAltText"
341
+ />
342
+ </span>
343
+ </div>
344
+ <div class="k-chat-user-status" *ngIf="userStatusTemplate?.templateRef">
345
+ <ng-template
346
+ [ngTemplateOutlet]="userStatusTemplate.templateRef"
347
+ [ngTemplateOutletContext]="{ $implicit: group.messages.at(-1) }"
348
+ >
349
+ </ng-template>
350
+ </div>
351
+ </div>
241
352
  <div class="k-message-group-content">
242
353
  <p *ngIf="group.author.name" class="k-message-author">{{ group.author.name }}</p>
243
354
  <ng-container
@@ -254,13 +365,14 @@ export class MessageListComponent {
254
365
  <kendo-chat-message #message
255
366
  [message]="msg"
256
367
  [tabbable]="lastGroup && lastMessage"
257
- [template]="messageTemplate"
368
+ [authorMessageContentTemplate]="authorMessageContentTemplate"
369
+ [receiverMessageContentTemplate]="receiverMessageContentTemplate"
370
+ [messageContentTemplate]="messageContentTemplate"
371
+ [authorMessageTemplate]="authorMessageTemplate"
372
+ [receiverMessageTemplate]="receiverMessageTemplate"
373
+ [messageTemplate]="messageTemplate"
258
374
  [statusTemplate]="statusTemplate"
259
375
  [authorId]="authorId"
260
- (click)="onClick(message, $event)"
261
- (contextMenuVisibilityChange)="handleMenuClose($event)"
262
- [class.k-first]="group.messages.length > 1 && firstMessage"
263
- [class.k-last]="group.messages.length > 1 && lastMessage"
264
376
  >
265
377
  </kendo-chat-message>
266
378
 
@@ -289,6 +401,7 @@ export class MessageListComponent {
289
401
  <kendo-chat-suggested-actions #actions
290
402
  *ngSwitchCase="'action-group'"
291
403
  [actions]="group.actions"
404
+ type="action"
292
405
  [tabbable]="lastGroup"
293
406
  (dispatchAction)="dispatchAction($event, last(view))"
294
407
  (click)="select(actions, $event)"
@@ -299,9 +412,9 @@ export class MessageListComponent {
299
412
  </ng-container>
300
413
  <kendo-resize-sensor (resize)="onResize()">
301
414
  </kendo-resize-sensor>
302
- `, 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"] }] });
415
+ `, 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"] }] });
303
416
  }
304
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MessageListComponent, decorators: [{
417
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageListComponent, decorators: [{
305
418
  type: Component,
306
419
  args: [{
307
420
  selector: 'kendo-chat-message-list',
@@ -326,10 +439,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
326
439
  [class.k-message-group-sender]="isOwnMessage(group.messages[0])"
327
440
  [class.k-message-group-receiver]="!isOwnMessage(group.messages[0])"
328
441
  [class.k-no-avatar]="!group.author.avatarUrl"
329
- [class.k-message-group-full-width]="chatService.messageWidthMode === 'full'"
442
+ [class.k-message-group-full-width]="calculateMessageWidthMode(group.messages[0])"
330
443
  >
331
444
  <div
332
- *ngIf="group.author.avatarUrl"
445
+ *ngIf="!userStatusTemplate?.templateRef && group.author.avatarUrl"
333
446
  class="k-avatar k-avatar-md k-avatar-solid k-avatar-solid-primary k-rounded-full"
334
447
  >
335
448
  <span class="k-avatar-image">
@@ -339,6 +452,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
339
452
  />
340
453
  </span>
341
454
  </div>
455
+ <div class="k-chat-user-status-wrapper" *ngIf="group.author.avatarUrl && userStatusTemplate?.templateRef">
456
+ <div
457
+ class="k-avatar k-avatar-md k-avatar-solid k-avatar-solid-primary k-rounded-full"
458
+ >
459
+ <span class="k-avatar-image">
460
+ <img
461
+ [attr.src]="group.author.avatarUrl"
462
+ [alt]="group.author.avatarAltText"
463
+ />
464
+ </span>
465
+ </div>
466
+ <div class="k-chat-user-status" *ngIf="userStatusTemplate?.templateRef">
467
+ <ng-template
468
+ [ngTemplateOutlet]="userStatusTemplate.templateRef"
469
+ [ngTemplateOutletContext]="{ $implicit: group.messages.at(-1) }"
470
+ >
471
+ </ng-template>
472
+ </div>
473
+ </div>
342
474
  <div class="k-message-group-content">
343
475
  <p *ngIf="group.author.name" class="k-message-author">{{ group.author.name }}</p>
344
476
  <ng-container
@@ -355,13 +487,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
355
487
  <kendo-chat-message #message
356
488
  [message]="msg"
357
489
  [tabbable]="lastGroup && lastMessage"
358
- [template]="messageTemplate"
490
+ [authorMessageContentTemplate]="authorMessageContentTemplate"
491
+ [receiverMessageContentTemplate]="receiverMessageContentTemplate"
492
+ [messageContentTemplate]="messageContentTemplate"
493
+ [authorMessageTemplate]="authorMessageTemplate"
494
+ [receiverMessageTemplate]="receiverMessageTemplate"
495
+ [messageTemplate]="messageTemplate"
359
496
  [statusTemplate]="statusTemplate"
360
497
  [authorId]="authorId"
361
- (click)="onClick(message, $event)"
362
- (contextMenuVisibilityChange)="handleMenuClose($event)"
363
- [class.k-first]="group.messages.length > 1 && firstMessage"
364
- [class.k-last]="group.messages.length > 1 && lastMessage"
365
498
  >
366
499
  </kendo-chat-message>
367
500
 
@@ -390,6 +523,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
390
523
  <kendo-chat-suggested-actions #actions
391
524
  *ngSwitchCase="'action-group'"
392
525
  [actions]="group.actions"
526
+ type="action"
393
527
  [tabbable]="lastGroup"
394
528
  (dispatchAction)="dispatchAction($event, last(view))"
395
529
  (click)="select(actions, $event)"
@@ -404,16 +538,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
404
538
  standalone: true,
405
539
  imports: [NgFor, NgSwitch, NgSwitchCase, NgIf, NgTemplateOutlet, MessageComponent, AttachmentComponent, MessageAttachmentsComponent, SuggestedActionsComponent, ResizeSensorComponent]
406
540
  }]
407
- }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1.IntlService }, { type: i0.Renderer2 }, { type: i2.ChatService }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { messages: [{
541
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.IntlService }, { type: i0.Renderer2 }, { type: i2.ChatService }, { type: i0.ChangeDetectorRef }], propDecorators: { messages: [{
408
542
  type: Input
409
543
  }], attachmentTemplate: [{
410
544
  type: Input
545
+ }], authorMessageContentTemplate: [{
546
+ type: Input
547
+ }], receiverMessageContentTemplate: [{
548
+ type: Input
549
+ }], messageContentTemplate: [{
550
+ type: Input
551
+ }], authorMessageTemplate: [{
552
+ type: Input
553
+ }], receiverMessageTemplate: [{
554
+ type: Input
411
555
  }], messageTemplate: [{
412
556
  type: Input
413
557
  }], timestampTemplate: [{
414
558
  type: Input
415
559
  }], statusTemplate: [{
416
560
  type: Input
561
+ }], userStatusTemplate: [{
562
+ type: Input
417
563
  }], localization: [{
418
564
  type: Input
419
565
  }], authorId: [{
@@ -33,8 +33,8 @@ export class MessageReferenceComponent extends ChatItem {
33
33
  return this.localization.get(key);
34
34
  }
35
35
  focus() { }
36
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MessageReferenceComponent, deps: [{ token: i1.LocalizationService }, { token: i2.ChatService }], target: i0.ɵɵFactoryTarget.Component });
37
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: MessageReferenceComponent, isStandalone: true, selector: "chat-message-reference-content", inputs: { message: "message" }, host: { properties: { "class.k-message-reference-content": "this.hostClass" } }, providers: [{
36
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageReferenceComponent, deps: [{ token: i1.LocalizationService }, { token: i2.ChatService }], target: i0.ɵɵFactoryTarget.Component });
37
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MessageReferenceComponent, isStandalone: true, selector: "chat-message-reference-content", inputs: { message: "message" }, host: { properties: { "class.k-message-reference-content": "this.hostClass" } }, providers: [{
38
38
  provide: ChatItem,
39
39
  useExisting: forwardRef(() => MessageReferenceComponent)
40
40
  }], usesInheritance: true, ngImport: i0, template: `
@@ -47,7 +47,7 @@ export class MessageReferenceComponent extends ChatItem {
47
47
  <ng-container *ngIf="message.isDeleted && !isOwnMessage(message)">{{ textFor('deletedMessageReceiverText') }}</ng-container>
48
48
  `, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ChatFileComponent, selector: "li[chatFile]", inputs: ["chatFile", "removable", "fileActions"], outputs: ["remove", "actionClick", "actionsToggle", "actionButtonClick"] }] });
49
49
  }
50
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MessageReferenceComponent, decorators: [{
50
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageReferenceComponent, decorators: [{
51
51
  type: Component,
52
52
  args: [{
53
53
  selector: 'chat-message-reference-content',
@@ -67,7 +67,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
67
67
  standalone: true,
68
68
  imports: [NgIf, ChatFileComponent]
69
69
  }]
70
- }], ctorParameters: function () { return [{ type: i1.LocalizationService }, { type: i2.ChatService }]; }, propDecorators: { hostClass: [{
70
+ }], ctorParameters: () => [{ type: i1.LocalizationService }, { type: i2.ChatService }], propDecorators: { hostClass: [{
71
71
  type: HostBinding,
72
72
  args: ['class.k-message-reference-content']
73
73
  }], message: [{