@progress/kendo-angular-conversational-ui 24.0.0-develop.11 → 24.0.0-develop.12
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.
- package/chat/api/chat-load-more-event.d.ts +18 -0
- package/chat/api/chat-referenced-message-click-event.d.ts +18 -0
- package/chat/api/index.d.ts +4 -0
- package/chat/api/scroll-mode.d.ts +12 -0
- package/chat/chat.component.d.ts +164 -2
- package/chat/common/chat.service.d.ts +12 -3
- package/chat/common/endless-scroll-state.d.ts +36 -0
- package/chat/common/models/pending-scroll-action.d.ts +11 -0
- package/chat/common/scroll-anchor.directive.d.ts +38 -1
- package/chat/common/utils.d.ts +15 -0
- package/chat/message.component.d.ts +4 -2
- package/fesm2022/progress-kendo-angular-conversational-ui.mjs +1175 -83
- package/package-metadata.mjs +2 -2
- package/package.json +14 -14
- package/promptbox/affixes/promptbox-end-affix.component.d.ts +1 -1
- package/promptbox/affixes/promptbox-start-affix.component.d.ts +1 -1
- package/promptbox/affixes/promptbox-top-affix.component.d.ts +1 -1
|
@@ -7,7 +7,7 @@ import { InjectionToken, Input, ViewChild, HostBinding, Inject, Directive, Injec
|
|
|
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, normalizeKeys, Keys, focusableSelector, guid, getter, isDocumentAvailable, hasObservers, EventsOutsideAngularDirective, closest as closest$1, ResizeSensorComponent, isChanged, processCssValue, ResizeBatchService } from '@progress/kendo-angular-common';
|
|
10
|
+
import { isPresent, normalizeKeys, Keys, focusableSelector, guid, getter, isDocumentAvailable, hasObservers, EventsOutsideAngularDirective, closest as closest$1, ResizeSensorComponent, getLicenseMessage, shouldShowValidationUI, isChanged, processCssValue, WatermarkOverlayComponent, ResizeBatchService } from '@progress/kendo-angular-common';
|
|
11
11
|
import { DialogContainerService, DialogService, WindowService, WindowContainerService } from '@progress/kendo-angular-dialog';
|
|
12
12
|
import { NgTemplateOutlet, NgClass } from '@angular/common';
|
|
13
13
|
import { Subject, Subscription, fromEvent } from 'rxjs';
|
|
@@ -215,8 +215,8 @@ const packageMetadata = {
|
|
|
215
215
|
productName: 'Kendo UI for Angular',
|
|
216
216
|
productCode: 'KENDOUIANGULAR',
|
|
217
217
|
productCodes: ['KENDOUIANGULAR'],
|
|
218
|
-
publishDate:
|
|
219
|
-
version: '24.0.0-develop.
|
|
218
|
+
publishDate: 1777466818,
|
|
219
|
+
version: '24.0.0-develop.12',
|
|
220
220
|
licensingDocsUrl: 'https://www.telerik.com/kendo-angular-ui/my-license/'
|
|
221
221
|
};
|
|
222
222
|
|
|
@@ -2349,6 +2349,86 @@ const defaultModelFields = {
|
|
|
2349
2349
|
failedField: 'failed'
|
|
2350
2350
|
};
|
|
2351
2351
|
|
|
2352
|
+
/**
|
|
2353
|
+
* @hidden
|
|
2354
|
+
*
|
|
2355
|
+
* Show the scroll-to-bottom button when the user is this many pixels from the bottom.
|
|
2356
|
+
*/
|
|
2357
|
+
const scrollButtonThreshold = 100;
|
|
2358
|
+
/**
|
|
2359
|
+
* @hidden
|
|
2360
|
+
*
|
|
2361
|
+
* Pins a scroll container to a computed target across layout shifts.
|
|
2362
|
+
* Observers extend the idle window until layout has settled; the initial and
|
|
2363
|
+
* final pins bracket the settle window so the viewport lands exactly on target.
|
|
2364
|
+
*/
|
|
2365
|
+
const settleScroll = (zone, el, computeTarget, onComplete, idleMs = 120, timeoutMs = 1000) => {
|
|
2366
|
+
const pin = () => {
|
|
2367
|
+
const t = computeTarget();
|
|
2368
|
+
if (t !== null) {
|
|
2369
|
+
el.scrollTop = t;
|
|
2370
|
+
}
|
|
2371
|
+
};
|
|
2372
|
+
pin();
|
|
2373
|
+
zone.runOutsideAngular(() => {
|
|
2374
|
+
let idleTimer = null;
|
|
2375
|
+
let safetyTimer = null;
|
|
2376
|
+
let mutationObserver = null;
|
|
2377
|
+
let resizeObserver = null;
|
|
2378
|
+
let finished = false;
|
|
2379
|
+
const finish = () => {
|
|
2380
|
+
if (finished) {
|
|
2381
|
+
return;
|
|
2382
|
+
}
|
|
2383
|
+
finished = true;
|
|
2384
|
+
if (mutationObserver) {
|
|
2385
|
+
mutationObserver.disconnect();
|
|
2386
|
+
}
|
|
2387
|
+
if (resizeObserver) {
|
|
2388
|
+
resizeObserver.disconnect();
|
|
2389
|
+
}
|
|
2390
|
+
clearTimeout(idleTimer);
|
|
2391
|
+
clearTimeout(safetyTimer);
|
|
2392
|
+
pin();
|
|
2393
|
+
zone.run(() => {
|
|
2394
|
+
onComplete();
|
|
2395
|
+
requestAnimationFrame(pin);
|
|
2396
|
+
});
|
|
2397
|
+
};
|
|
2398
|
+
const resetIdle = () => {
|
|
2399
|
+
clearTimeout(idleTimer);
|
|
2400
|
+
idleTimer = setTimeout(finish, idleMs);
|
|
2401
|
+
};
|
|
2402
|
+
const onChange = () => {
|
|
2403
|
+
pin();
|
|
2404
|
+
resetIdle();
|
|
2405
|
+
};
|
|
2406
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
2407
|
+
mutationObserver = new MutationObserver(onChange);
|
|
2408
|
+
mutationObserver.observe(el, {
|
|
2409
|
+
childList: true,
|
|
2410
|
+
subtree: true,
|
|
2411
|
+
characterData: true,
|
|
2412
|
+
attributes: true,
|
|
2413
|
+
attributeFilter: ['style', 'class', 'hidden']
|
|
2414
|
+
});
|
|
2415
|
+
}
|
|
2416
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
2417
|
+
resizeObserver = new ResizeObserver(onChange);
|
|
2418
|
+
resizeObserver.observe(el);
|
|
2419
|
+
for (const child of Array.from(el.children)) {
|
|
2420
|
+
resizeObserver.observe(child);
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
if (!mutationObserver && !resizeObserver) {
|
|
2424
|
+
finished = true;
|
|
2425
|
+
zone.run(onComplete);
|
|
2426
|
+
return;
|
|
2427
|
+
}
|
|
2428
|
+
safetyTimer = setTimeout(finish, timeoutMs);
|
|
2429
|
+
resetIdle();
|
|
2430
|
+
});
|
|
2431
|
+
};
|
|
2352
2432
|
/**
|
|
2353
2433
|
* @hidden
|
|
2354
2434
|
*/
|
|
@@ -2514,15 +2594,17 @@ const transformActions = (actions) => {
|
|
|
2514
2594
|
* @hidden
|
|
2515
2595
|
*/
|
|
2516
2596
|
class ChatService {
|
|
2597
|
+
zone;
|
|
2517
2598
|
authorId;
|
|
2518
2599
|
messageWidthMode;
|
|
2519
|
-
messageToolbarActions = [];
|
|
2520
2600
|
messageContextMenuActions = [];
|
|
2521
2601
|
calculatedContextMenuActions = [];
|
|
2522
2602
|
fileActions = [];
|
|
2523
2603
|
toggleMessageState = false;
|
|
2604
|
+
layoutChangeInProgress = false;
|
|
2524
2605
|
reply;
|
|
2525
2606
|
messages = [];
|
|
2607
|
+
repliedToMessages = [];
|
|
2526
2608
|
chatElement;
|
|
2527
2609
|
messageElementsMap = new Map();
|
|
2528
2610
|
messagesContextMenu;
|
|
@@ -2540,6 +2622,7 @@ class ChatService {
|
|
|
2540
2622
|
_messageBoxSettings = MESSAGE_BOX_SETTINGS;
|
|
2541
2623
|
_suggestionsLayout = SUGGESTIONS_LAYOUT_DEFAULT_SETTINGS;
|
|
2542
2624
|
_quickActionsLayout = SUGGESTIONS_LAYOUT_DEFAULT_SETTINGS;
|
|
2625
|
+
_messageToolbarActions = [];
|
|
2543
2626
|
_authorMessageSettings;
|
|
2544
2627
|
_receiverMessageSettings;
|
|
2545
2628
|
_allowMessageCollapse;
|
|
@@ -2557,6 +2640,7 @@ class ChatService {
|
|
|
2557
2640
|
authorMessageSettingsChange: new Subject(),
|
|
2558
2641
|
receiverMessageSettingsChange: new Subject(),
|
|
2559
2642
|
allowMessageCollapseChange: new Subject(),
|
|
2643
|
+
messageToolbarActionsChange: new Subject(),
|
|
2560
2644
|
};
|
|
2561
2645
|
toolbarAction$ = this.subjects.toolbarAction.asObservable();
|
|
2562
2646
|
contextMenuAction$ = this.subjects.contextMenuAction.asObservable();
|
|
@@ -2571,6 +2655,10 @@ class ChatService {
|
|
|
2571
2655
|
authorMessageSettingsChange$ = this.subjects.authorMessageSettingsChange.asObservable();
|
|
2572
2656
|
receiverMessageSettingsChange$ = this.subjects.receiverMessageSettingsChange.asObservable();
|
|
2573
2657
|
allowMessageCollapseChange$ = this.subjects.allowMessageCollapseChange.asObservable();
|
|
2658
|
+
messageToolbarActionsChange$ = this.subjects.messageToolbarActionsChange.asObservable();
|
|
2659
|
+
constructor(zone) {
|
|
2660
|
+
this.zone = zone;
|
|
2661
|
+
}
|
|
2574
2662
|
set authorMessageSettings(settings) {
|
|
2575
2663
|
const previousSettings = this._authorMessageSettings;
|
|
2576
2664
|
if (JSON.stringify(previousSettings) !== JSON.stringify(settings)) {
|
|
@@ -2629,6 +2717,13 @@ class ChatService {
|
|
|
2629
2717
|
get quickActionsLayout() {
|
|
2630
2718
|
return this._quickActionsLayout;
|
|
2631
2719
|
}
|
|
2720
|
+
set messageToolbarActions(value) {
|
|
2721
|
+
this._messageToolbarActions = value;
|
|
2722
|
+
this.emit('messageToolbarActionsChange', this._messageToolbarActions);
|
|
2723
|
+
}
|
|
2724
|
+
get messageToolbarActions() {
|
|
2725
|
+
return this._messageToolbarActions;
|
|
2726
|
+
}
|
|
2632
2727
|
set allowMessageCollapse(value) {
|
|
2633
2728
|
const previousValue = this._allowMessageCollapse;
|
|
2634
2729
|
if (previousValue !== value) {
|
|
@@ -2651,7 +2746,8 @@ class ChatService {
|
|
|
2651
2746
|
(this.subjects[subjectKey]).next(value);
|
|
2652
2747
|
}
|
|
2653
2748
|
getMessageById(id) {
|
|
2654
|
-
return this.messages.find(message => message.id === id)
|
|
2749
|
+
return this.messages.find(message => message.id === id)
|
|
2750
|
+
?? this.repliedToMessages.find(message => message.id === id);
|
|
2655
2751
|
}
|
|
2656
2752
|
registerMessageElement(messageId, elementRef) {
|
|
2657
2753
|
this.messageElementsMap.set(messageId, elementRef);
|
|
@@ -2662,26 +2758,25 @@ class ChatService {
|
|
|
2662
2758
|
this.reply = null;
|
|
2663
2759
|
}
|
|
2664
2760
|
}
|
|
2665
|
-
scrollToMessage(messageId) {
|
|
2666
|
-
|
|
2667
|
-
if (!elementRef?.nativeElement) {
|
|
2761
|
+
scrollToMessage(messageId, behavior = 'smooth') {
|
|
2762
|
+
if (!isDocumentAvailable()) {
|
|
2668
2763
|
return;
|
|
2669
2764
|
}
|
|
2670
|
-
const scrollContainer = this.
|
|
2765
|
+
const scrollContainer = this.getScrollContainer();
|
|
2671
2766
|
if (!scrollContainer) {
|
|
2672
2767
|
return;
|
|
2673
2768
|
}
|
|
2674
|
-
const
|
|
2675
|
-
const
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
}
|
|
2769
|
+
const computeTarget = () => this.computeScrollTarget(messageId);
|
|
2770
|
+
const scrollTop = computeTarget();
|
|
2771
|
+
if (scrollTop === null) {
|
|
2772
|
+
return;
|
|
2773
|
+
}
|
|
2774
|
+
if (behavior === 'auto') {
|
|
2775
|
+
settleScroll(this.zone, scrollContainer, computeTarget, () => { });
|
|
2776
|
+
}
|
|
2777
|
+
else {
|
|
2778
|
+
scrollContainer.scrollTo({ top: scrollTop, behavior });
|
|
2779
|
+
}
|
|
2685
2780
|
}
|
|
2686
2781
|
focusActiveMessageElement() {
|
|
2687
2782
|
if (this.activeMessageElement) {
|
|
@@ -2691,6 +2786,25 @@ class ChatService {
|
|
|
2691
2786
|
isOwnMessage(message) {
|
|
2692
2787
|
return this.authorId === message.author.id;
|
|
2693
2788
|
}
|
|
2789
|
+
getScrollContainer() {
|
|
2790
|
+
return this.chatElement?.element?.nativeElement ?? null;
|
|
2791
|
+
}
|
|
2792
|
+
computeScrollTarget(messageId) {
|
|
2793
|
+
const elementRef = this.messageElementsMap.get(messageId);
|
|
2794
|
+
if (!elementRef?.nativeElement) {
|
|
2795
|
+
return null;
|
|
2796
|
+
}
|
|
2797
|
+
const scrollContainer = this.getScrollContainer();
|
|
2798
|
+
if (!scrollContainer) {
|
|
2799
|
+
return null;
|
|
2800
|
+
}
|
|
2801
|
+
const targetElement = elementRef.nativeElement;
|
|
2802
|
+
const pinnedElement = scrollContainer.querySelector('.k-message-pinned');
|
|
2803
|
+
const pinnedHeight = pinnedElement?.offsetHeight || 0;
|
|
2804
|
+
const containerRect = scrollContainer.getBoundingClientRect();
|
|
2805
|
+
const targetRect = targetElement.getBoundingClientRect();
|
|
2806
|
+
return targetRect.top - containerRect.top + scrollContainer.scrollTop - pinnedHeight;
|
|
2807
|
+
}
|
|
2694
2808
|
updateComponentSettings(property, settings, defaultSettings) {
|
|
2695
2809
|
if (settings === true) {
|
|
2696
2810
|
this[property] = defaultSettings;
|
|
@@ -2702,17 +2816,17 @@ class ChatService {
|
|
|
2702
2816
|
this[property] = { ...defaultSettings, ...settings };
|
|
2703
2817
|
}
|
|
2704
2818
|
}
|
|
2705
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2819
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatService, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2706
2820
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatService });
|
|
2707
2821
|
}
|
|
2708
2822
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatService, decorators: [{
|
|
2709
2823
|
type: Injectable
|
|
2710
|
-
}] });
|
|
2824
|
+
}], ctorParameters: () => [{ type: i0.NgZone }] });
|
|
2711
2825
|
|
|
2712
2826
|
// Consider scroll to be at the bottom when within this number of pixels from the container height.
|
|
2713
2827
|
const maxDelta = 2;
|
|
2714
|
-
//
|
|
2715
|
-
const
|
|
2828
|
+
// Edge detection threshold for endless scroll — fire loadMore when within this many pixels.
|
|
2829
|
+
const edgeThreshold = 100;
|
|
2716
2830
|
/**
|
|
2717
2831
|
* @hidden
|
|
2718
2832
|
*/
|
|
@@ -2722,13 +2836,53 @@ class ScrollAnchorDirective {
|
|
|
2722
2836
|
renderer;
|
|
2723
2837
|
cdr;
|
|
2724
2838
|
autoScroll = true;
|
|
2839
|
+
autoScrollThreshold = '20%';
|
|
2840
|
+
set endlessMode(value) {
|
|
2841
|
+
const changed = this._endlessMode !== value;
|
|
2842
|
+
this._endlessMode = value;
|
|
2843
|
+
this._nearTopLocked = false;
|
|
2844
|
+
this._nearBottomLocked = false;
|
|
2845
|
+
this.scrolling = false;
|
|
2846
|
+
if (changed && !value && this._initialScrollDone && isDocumentAvailable()) {
|
|
2847
|
+
this.element.nativeElement.scrollTo({ top: 0, behavior: 'auto' });
|
|
2848
|
+
this._subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
2849
|
+
this.performScroll('smooth');
|
|
2850
|
+
}));
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
get endlessMode() {
|
|
2854
|
+
return this._endlessMode;
|
|
2855
|
+
}
|
|
2856
|
+
rangeIsAtEnd = true;
|
|
2725
2857
|
autoScrollChange = new EventEmitter();
|
|
2858
|
+
nearTop = new EventEmitter();
|
|
2859
|
+
nearBottom = new EventEmitter();
|
|
2726
2860
|
overflowAnchor = 'none';
|
|
2861
|
+
get scrollBehaviorStyle() {
|
|
2862
|
+
return this._endlessMode ? 'auto' : null;
|
|
2863
|
+
}
|
|
2727
2864
|
showScrollToBottomButton = false;
|
|
2728
2865
|
showMessageBoxSeparator = false;
|
|
2729
2866
|
scrolling = false;
|
|
2730
2867
|
unsubscribe;
|
|
2731
2868
|
scrollUpdate = null;
|
|
2869
|
+
_previousScrollHeight = 0;
|
|
2870
|
+
_previousScrollTop = 0;
|
|
2871
|
+
_pendingScrollPreservation = false;
|
|
2872
|
+
_endlessMode = false;
|
|
2873
|
+
_nearTopLocked = false;
|
|
2874
|
+
_nearBottomLocked = false;
|
|
2875
|
+
_scrollingToMessage = false;
|
|
2876
|
+
_initialScrollDone = false;
|
|
2877
|
+
_subs = new Subscription();
|
|
2878
|
+
// Need to ensure that the scroll follows a new streaming message
|
|
2879
|
+
// until the scroll threshold is reached.
|
|
2880
|
+
_streamingFollow = false;
|
|
2881
|
+
_thresholdScrollCap = null;
|
|
2882
|
+
_streamingPrevScrollTop = 0;
|
|
2883
|
+
_streamingPrevHeight = 0;
|
|
2884
|
+
_streamingPrevMsgCount = 0;
|
|
2885
|
+
_mutationObserver = null;
|
|
2732
2886
|
constructor(element, zone, renderer, cdr) {
|
|
2733
2887
|
this.element = element;
|
|
2734
2888
|
this.zone = zone;
|
|
@@ -2736,15 +2890,54 @@ class ScrollAnchorDirective {
|
|
|
2736
2890
|
this.cdr = cdr;
|
|
2737
2891
|
}
|
|
2738
2892
|
ngOnInit() {
|
|
2893
|
+
if (!isDocumentAvailable()) {
|
|
2894
|
+
return;
|
|
2895
|
+
}
|
|
2739
2896
|
this.zone.runOutsideAngular(() => {
|
|
2740
|
-
|
|
2897
|
+
const el = this.element.nativeElement;
|
|
2898
|
+
this.unsubscribe = this.renderer.listen(el, 'scroll', () => {
|
|
2899
|
+
this.detectStreamingScrollUp();
|
|
2900
|
+
this.setupScrollUpdate();
|
|
2901
|
+
}, { passive: true });
|
|
2902
|
+
this._mutationObserver = new MutationObserver(() => {
|
|
2903
|
+
if (!this._streamingFollow) {
|
|
2904
|
+
return;
|
|
2905
|
+
}
|
|
2906
|
+
const currentMsgCount = el.querySelectorAll('.k-message').length;
|
|
2907
|
+
const currentHeight = el.scrollHeight;
|
|
2908
|
+
if (currentMsgCount === this._streamingPrevMsgCount && currentHeight > this._streamingPrevHeight) {
|
|
2909
|
+
const bottom = currentHeight - el.clientHeight;
|
|
2910
|
+
const cap = this._thresholdScrollCap;
|
|
2911
|
+
const target = cap !== null ? Math.min(bottom, cap) : bottom;
|
|
2912
|
+
if (target > el.scrollTop) {
|
|
2913
|
+
el.scrollTop = target;
|
|
2914
|
+
}
|
|
2915
|
+
this._streamingPrevScrollTop = el.scrollTop;
|
|
2916
|
+
this.updateScrollToBottomButton(el);
|
|
2917
|
+
}
|
|
2918
|
+
this._streamingPrevMsgCount = currentMsgCount;
|
|
2919
|
+
this._streamingPrevHeight = currentHeight;
|
|
2920
|
+
});
|
|
2921
|
+
this._mutationObserver.observe(el, {
|
|
2922
|
+
childList: true,
|
|
2923
|
+
subtree: true,
|
|
2924
|
+
characterData: true,
|
|
2925
|
+
});
|
|
2741
2926
|
});
|
|
2742
2927
|
}
|
|
2743
2928
|
ngAfterViewInit() {
|
|
2744
|
-
this.
|
|
2745
|
-
|
|
2929
|
+
this._subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
2930
|
+
if (this.autoScroll) {
|
|
2931
|
+
if (this._endlessMode) {
|
|
2932
|
+
this.scrollToBottomWithSettle();
|
|
2933
|
+
}
|
|
2934
|
+
else {
|
|
2935
|
+
this.performScroll('smooth');
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2746
2938
|
this.calculateMessageBoxSeparator();
|
|
2747
|
-
|
|
2939
|
+
this._initialScrollDone = true;
|
|
2940
|
+
}));
|
|
2748
2941
|
}
|
|
2749
2942
|
ngOnDestroy() {
|
|
2750
2943
|
if (this.unsubscribe) {
|
|
@@ -2753,8 +2946,16 @@ class ScrollAnchorDirective {
|
|
|
2753
2946
|
if (this.scrollUpdate !== null) {
|
|
2754
2947
|
cancelAnimationFrame(this.scrollUpdate);
|
|
2755
2948
|
}
|
|
2949
|
+
if (this._mutationObserver) {
|
|
2950
|
+
this._mutationObserver.disconnect();
|
|
2951
|
+
this._mutationObserver = null;
|
|
2952
|
+
}
|
|
2953
|
+
this._subs.unsubscribe();
|
|
2756
2954
|
}
|
|
2757
2955
|
onScroll() {
|
|
2956
|
+
if (!isDocumentAvailable() || this._scrollingToMessage) {
|
|
2957
|
+
return;
|
|
2958
|
+
}
|
|
2758
2959
|
const el = this.element.nativeElement;
|
|
2759
2960
|
const scrollPosition = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
2760
2961
|
const atBottom = scrollPosition < maxDelta;
|
|
@@ -2764,7 +2965,8 @@ class ScrollAnchorDirective {
|
|
|
2764
2965
|
}
|
|
2765
2966
|
return;
|
|
2766
2967
|
}
|
|
2767
|
-
const showScrollButton = scrollPosition > scrollButtonThreshold
|
|
2968
|
+
const showScrollButton = scrollPosition > scrollButtonThreshold
|
|
2969
|
+
|| (this.endlessMode && !this.rangeIsAtEnd);
|
|
2768
2970
|
const autoScrollChanged = this.autoScroll !== atBottom;
|
|
2769
2971
|
const buttonChanged = this.showScrollToBottomButton !== showScrollButton;
|
|
2770
2972
|
if (autoScrollChanged || buttonChanged) {
|
|
@@ -2779,8 +2981,30 @@ class ScrollAnchorDirective {
|
|
|
2779
2981
|
}
|
|
2780
2982
|
});
|
|
2781
2983
|
}
|
|
2984
|
+
if (this.endlessMode) {
|
|
2985
|
+
const nearTopEdge = el.scrollTop < edgeThreshold;
|
|
2986
|
+
const nearBottomEdge = !this.rangeIsAtEnd && scrollPosition < edgeThreshold;
|
|
2987
|
+
if (!nearTopEdge) {
|
|
2988
|
+
this._nearTopLocked = false;
|
|
2989
|
+
}
|
|
2990
|
+
if (!nearBottomEdge) {
|
|
2991
|
+
this._nearBottomLocked = false;
|
|
2992
|
+
}
|
|
2993
|
+
if (nearTopEdge && !this._nearTopLocked) {
|
|
2994
|
+
this._nearTopLocked = true;
|
|
2995
|
+
this.zone.run(() => this.nearTop.emit());
|
|
2996
|
+
}
|
|
2997
|
+
if (nearBottomEdge && !this._nearBottomLocked) {
|
|
2998
|
+
this._nearBottomLocked = true;
|
|
2999
|
+
this.zone.run(() => this.nearBottom.emit());
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
2782
3002
|
}
|
|
2783
3003
|
autoScrollToBottom() {
|
|
3004
|
+
if (this._streamingFollow) {
|
|
3005
|
+
this.followStreamingContent();
|
|
3006
|
+
return;
|
|
3007
|
+
}
|
|
2784
3008
|
if (!this.autoScroll) {
|
|
2785
3009
|
return;
|
|
2786
3010
|
}
|
|
@@ -2789,9 +3013,80 @@ class ScrollAnchorDirective {
|
|
|
2789
3013
|
scrollToBottom() {
|
|
2790
3014
|
this.autoScroll = true;
|
|
2791
3015
|
this.showScrollToBottomButton = false;
|
|
3016
|
+
this._streamingFollow = false;
|
|
3017
|
+
this._thresholdScrollCap = null;
|
|
2792
3018
|
this.performScroll();
|
|
2793
3019
|
}
|
|
3020
|
+
scrollToBottomWithSettle(onComplete) {
|
|
3021
|
+
if (!isDocumentAvailable()) {
|
|
3022
|
+
onComplete?.();
|
|
3023
|
+
return;
|
|
3024
|
+
}
|
|
3025
|
+
const el = this.element.nativeElement;
|
|
3026
|
+
this._scrollingToMessage = true;
|
|
3027
|
+
this._streamingFollow = false;
|
|
3028
|
+
this._thresholdScrollCap = null;
|
|
3029
|
+
settleScroll(this.zone, el, () => el.scrollHeight - el.clientHeight, () => {
|
|
3030
|
+
this.autoScroll = true;
|
|
3031
|
+
this.showScrollToBottomButton = false;
|
|
3032
|
+
this.scrolling = false;
|
|
3033
|
+
this._scrollingToMessage = false;
|
|
3034
|
+
this.cdr.markForCheck();
|
|
3035
|
+
onComplete?.();
|
|
3036
|
+
});
|
|
3037
|
+
}
|
|
3038
|
+
scrollWithThreshold(messageEl) {
|
|
3039
|
+
if (!isDocumentAvailable() || !messageEl) {
|
|
3040
|
+
this.performScroll();
|
|
3041
|
+
return;
|
|
3042
|
+
}
|
|
3043
|
+
const container = this.element.nativeElement;
|
|
3044
|
+
const pinnedHeight = this.getPinnedBarHeight();
|
|
3045
|
+
const thresholdPx = Math.max(0, this.parseThreshold(pinnedHeight));
|
|
3046
|
+
const containerRect = container.getBoundingClientRect();
|
|
3047
|
+
const messageRect = messageEl.getBoundingClientRect();
|
|
3048
|
+
const msgTopInContent = messageRect.top - containerRect.top + container.scrollTop;
|
|
3049
|
+
const targetScrollTop = Math.max(0, msgTopInContent - thresholdPx - pinnedHeight);
|
|
3050
|
+
const bottomScrollTop = Math.max(0, container.scrollHeight - container.clientHeight);
|
|
3051
|
+
const finalScrollTop = Math.min(targetScrollTop, bottomScrollTop);
|
|
3052
|
+
if (finalScrollTop > container.scrollTop) {
|
|
3053
|
+
container.scrollTo({ top: finalScrollTop, behavior: 'smooth' });
|
|
3054
|
+
}
|
|
3055
|
+
this._thresholdScrollCap = targetScrollTop;
|
|
3056
|
+
this._streamingFollow = true;
|
|
3057
|
+
this._streamingPrevScrollTop = container.scrollTop;
|
|
3058
|
+
this._streamingPrevHeight = container.scrollHeight;
|
|
3059
|
+
this._streamingPrevMsgCount = container.querySelectorAll('.k-message').length;
|
|
3060
|
+
const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
|
|
3061
|
+
this.showScrollToBottomButton = distanceFromBottom > scrollButtonThreshold;
|
|
3062
|
+
this.cdr.markForCheck();
|
|
3063
|
+
}
|
|
3064
|
+
recordScrollHeight() {
|
|
3065
|
+
if (!isDocumentAvailable()) {
|
|
3066
|
+
return;
|
|
3067
|
+
}
|
|
3068
|
+
const el = this.element.nativeElement;
|
|
3069
|
+
this._previousScrollHeight = el.scrollHeight;
|
|
3070
|
+
this._previousScrollTop = el.scrollTop;
|
|
3071
|
+
this._pendingScrollPreservation = true;
|
|
3072
|
+
this.cdr.markForCheck();
|
|
3073
|
+
}
|
|
3074
|
+
preserveScrollPosition() {
|
|
3075
|
+
if (!this._pendingScrollPreservation || !isDocumentAvailable()) {
|
|
3076
|
+
return;
|
|
3077
|
+
}
|
|
3078
|
+
const el = this.element.nativeElement;
|
|
3079
|
+
const delta = el.scrollHeight - this._previousScrollHeight;
|
|
3080
|
+
if (delta > 0) {
|
|
3081
|
+
el.scrollTop = this._previousScrollTop + delta;
|
|
3082
|
+
}
|
|
3083
|
+
this._pendingScrollPreservation = false;
|
|
3084
|
+
this.cdr.markForCheck();
|
|
3085
|
+
}
|
|
2794
3086
|
calculateMessageBoxSeparator() {
|
|
3087
|
+
if (!isDocumentAvailable()) {
|
|
3088
|
+
return;
|
|
3089
|
+
}
|
|
2795
3090
|
const el = this.element.nativeElement;
|
|
2796
3091
|
const shouldShow = el.scrollHeight > el.clientHeight;
|
|
2797
3092
|
if (this.showMessageBoxSeparator !== shouldShow) {
|
|
@@ -2799,6 +3094,90 @@ class ScrollAnchorDirective {
|
|
|
2799
3094
|
this.cdr.markForCheck();
|
|
2800
3095
|
}
|
|
2801
3096
|
}
|
|
3097
|
+
getDistanceFromBottom() {
|
|
3098
|
+
if (!isDocumentAvailable()) {
|
|
3099
|
+
return 0;
|
|
3100
|
+
}
|
|
3101
|
+
const el = this.element.nativeElement;
|
|
3102
|
+
return el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
3103
|
+
}
|
|
3104
|
+
setAriaLive(value) {
|
|
3105
|
+
if (!isDocumentAvailable()) {
|
|
3106
|
+
return;
|
|
3107
|
+
}
|
|
3108
|
+
this.renderer.setAttribute(this.element.nativeElement, 'aria-live', value);
|
|
3109
|
+
}
|
|
3110
|
+
lockForMessageScroll() {
|
|
3111
|
+
this._scrollingToMessage = true;
|
|
3112
|
+
}
|
|
3113
|
+
unlockForMessageScroll() {
|
|
3114
|
+
this._scrollingToMessage = false;
|
|
3115
|
+
this.setupScrollUpdate();
|
|
3116
|
+
}
|
|
3117
|
+
getAutoScrollThresholdPx() {
|
|
3118
|
+
return Math.max(0, this.parseThreshold(this.getPinnedBarHeight()));
|
|
3119
|
+
}
|
|
3120
|
+
get isFollowingThreshold() {
|
|
3121
|
+
return this._streamingFollow;
|
|
3122
|
+
}
|
|
3123
|
+
followStreamingContent() {
|
|
3124
|
+
if (!isDocumentAvailable()) {
|
|
3125
|
+
return;
|
|
3126
|
+
}
|
|
3127
|
+
const el = this.element.nativeElement;
|
|
3128
|
+
const bottom = el.scrollHeight - el.clientHeight;
|
|
3129
|
+
const cap = this._thresholdScrollCap;
|
|
3130
|
+
const target = cap !== null ? Math.min(bottom, cap) : bottom;
|
|
3131
|
+
if (target > el.scrollTop) {
|
|
3132
|
+
el.scrollTop = target;
|
|
3133
|
+
this._streamingPrevScrollTop = el.scrollTop;
|
|
3134
|
+
}
|
|
3135
|
+
this.updateScrollToBottomButton(el);
|
|
3136
|
+
}
|
|
3137
|
+
updateScrollToBottomButton(el) {
|
|
3138
|
+
const distance = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
3139
|
+
const shouldShow = distance > scrollButtonThreshold
|
|
3140
|
+
|| (this.endlessMode && !this.rangeIsAtEnd);
|
|
3141
|
+
if (this.showScrollToBottomButton !== shouldShow) {
|
|
3142
|
+
this.zone.run(() => {
|
|
3143
|
+
this.showScrollToBottomButton = shouldShow;
|
|
3144
|
+
this.cdr.markForCheck();
|
|
3145
|
+
});
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
detectStreamingScrollUp() {
|
|
3149
|
+
if (!this._streamingFollow) {
|
|
3150
|
+
return;
|
|
3151
|
+
}
|
|
3152
|
+
const st = this.element.nativeElement.scrollTop;
|
|
3153
|
+
if (st < this._streamingPrevScrollTop) {
|
|
3154
|
+
this._streamingFollow = false;
|
|
3155
|
+
this._thresholdScrollCap = null;
|
|
3156
|
+
}
|
|
3157
|
+
this._streamingPrevScrollTop = st;
|
|
3158
|
+
}
|
|
3159
|
+
parseThreshold(pinnedHeight = 0) {
|
|
3160
|
+
const threshold = this.autoScrollThreshold;
|
|
3161
|
+
if (typeof threshold === 'number') {
|
|
3162
|
+
return threshold;
|
|
3163
|
+
}
|
|
3164
|
+
if (typeof threshold === 'string') {
|
|
3165
|
+
if (threshold.endsWith('%')) {
|
|
3166
|
+
const percent = parseFloat(threshold) / 100;
|
|
3167
|
+
const visibleHeight = this.element.nativeElement.clientHeight - pinnedHeight;
|
|
3168
|
+
return visibleHeight * percent;
|
|
3169
|
+
}
|
|
3170
|
+
const parsed = parseFloat(threshold);
|
|
3171
|
+
if (!isNaN(parsed)) {
|
|
3172
|
+
return parsed;
|
|
3173
|
+
}
|
|
3174
|
+
}
|
|
3175
|
+
return 0;
|
|
3176
|
+
}
|
|
3177
|
+
getPinnedBarHeight() {
|
|
3178
|
+
const pinned = this.element.nativeElement.querySelector('.k-message-pinned');
|
|
3179
|
+
return pinned ? pinned.offsetHeight : 0;
|
|
3180
|
+
}
|
|
2802
3181
|
setupScrollUpdate() {
|
|
2803
3182
|
if (this.scrollUpdate !== null) {
|
|
2804
3183
|
return;
|
|
@@ -2808,13 +3187,20 @@ class ScrollAnchorDirective {
|
|
|
2808
3187
|
this.onScroll();
|
|
2809
3188
|
});
|
|
2810
3189
|
}
|
|
2811
|
-
performScroll() {
|
|
3190
|
+
performScroll(behavior = 'auto') {
|
|
3191
|
+
if (!isDocumentAvailable()) {
|
|
3192
|
+
return;
|
|
3193
|
+
}
|
|
2812
3194
|
const el = this.element.nativeElement;
|
|
2813
|
-
|
|
3195
|
+
const target = el.scrollHeight - el.clientHeight;
|
|
3196
|
+
if (Math.abs(el.scrollTop - target) < maxDelta) {
|
|
3197
|
+
return;
|
|
3198
|
+
}
|
|
3199
|
+
el.scrollTo({ top: target, behavior });
|
|
2814
3200
|
this.scrolling = true;
|
|
2815
3201
|
}
|
|
2816
3202
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ScrollAnchorDirective, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
|
|
2817
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.20", type: ScrollAnchorDirective, isStandalone: true, selector: "[kendoChatScrollAnchor]", inputs: { autoScroll: "autoScroll" }, outputs: { autoScrollChange: "autoScrollChange" }, host: { properties: { "style.overflow-anchor": "this.overflowAnchor" } }, exportAs: ["scrollAnchor"], ngImport: i0 });
|
|
3203
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.20", type: ScrollAnchorDirective, isStandalone: true, selector: "[kendoChatScrollAnchor]", inputs: { autoScroll: "autoScroll", autoScrollThreshold: "autoScrollThreshold", endlessMode: "endlessMode", rangeIsAtEnd: "rangeIsAtEnd" }, outputs: { autoScrollChange: "autoScrollChange", nearTop: "nearTop", nearBottom: "nearBottom" }, host: { properties: { "style.overflow-anchor": "this.overflowAnchor", "style.scroll-behavior": "this.scrollBehaviorStyle" } }, exportAs: ["scrollAnchor"], ngImport: i0 });
|
|
2818
3204
|
}
|
|
2819
3205
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ScrollAnchorDirective, decorators: [{
|
|
2820
3206
|
type: Directive,
|
|
@@ -2825,11 +3211,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
2825
3211
|
}]
|
|
2826
3212
|
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }], propDecorators: { autoScroll: [{
|
|
2827
3213
|
type: Input
|
|
3214
|
+
}], autoScrollThreshold: [{
|
|
3215
|
+
type: Input
|
|
3216
|
+
}], endlessMode: [{
|
|
3217
|
+
type: Input
|
|
3218
|
+
}], rangeIsAtEnd: [{
|
|
3219
|
+
type: Input
|
|
2828
3220
|
}], autoScrollChange: [{
|
|
2829
3221
|
type: Output
|
|
3222
|
+
}], nearTop: [{
|
|
3223
|
+
type: Output
|
|
3224
|
+
}], nearBottom: [{
|
|
3225
|
+
type: Output
|
|
2830
3226
|
}], overflowAnchor: [{
|
|
2831
3227
|
type: HostBinding,
|
|
2832
3228
|
args: ['style.overflow-anchor']
|
|
3229
|
+
}], scrollBehaviorStyle: [{
|
|
3230
|
+
type: HostBinding,
|
|
3231
|
+
args: ['style.scroll-behavior']
|
|
2833
3232
|
}] } });
|
|
2834
3233
|
|
|
2835
3234
|
const DEFAULT_SCROLL_BEHAVIOR = 'smooth';
|
|
@@ -2933,6 +3332,93 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
2933
3332
|
type: Injectable
|
|
2934
3333
|
}], ctorParameters: () => [{ type: i0.NgZone }, { type: i1.LocalizationService }] });
|
|
2935
3334
|
|
|
3335
|
+
/**
|
|
3336
|
+
* @hidden
|
|
3337
|
+
*/
|
|
3338
|
+
class EndlessScrollState {
|
|
3339
|
+
startIndex = 0;
|
|
3340
|
+
endIndex = 0;
|
|
3341
|
+
isLoading = false;
|
|
3342
|
+
get isAtStart() {
|
|
3343
|
+
return this.startIndex === 0;
|
|
3344
|
+
}
|
|
3345
|
+
get isAtEnd() {
|
|
3346
|
+
return this.endIndex >= this._total;
|
|
3347
|
+
}
|
|
3348
|
+
_total = 0;
|
|
3349
|
+
_pageSize = 50;
|
|
3350
|
+
get pageSize() {
|
|
3351
|
+
return this._pageSize;
|
|
3352
|
+
}
|
|
3353
|
+
init(total, pageSize) {
|
|
3354
|
+
this._total = total;
|
|
3355
|
+
this._pageSize = pageSize;
|
|
3356
|
+
return this.jumpToEnd();
|
|
3357
|
+
}
|
|
3358
|
+
extendUp() {
|
|
3359
|
+
if (this.isAtStart || this.isLoading) {
|
|
3360
|
+
return null;
|
|
3361
|
+
}
|
|
3362
|
+
const newStart = Math.max(0, this.startIndex - this._pageSize);
|
|
3363
|
+
if (newStart === this.startIndex) {
|
|
3364
|
+
return null;
|
|
3365
|
+
}
|
|
3366
|
+
this.startIndex = newStart;
|
|
3367
|
+
return { start: this.startIndex, end: this.endIndex };
|
|
3368
|
+
}
|
|
3369
|
+
extendDown() {
|
|
3370
|
+
if (this.isAtEnd || this.isLoading) {
|
|
3371
|
+
return null;
|
|
3372
|
+
}
|
|
3373
|
+
const newEnd = Math.min(this._total, this.endIndex + this._pageSize);
|
|
3374
|
+
if (newEnd === this.endIndex) {
|
|
3375
|
+
return null;
|
|
3376
|
+
}
|
|
3377
|
+
this.endIndex = newEnd;
|
|
3378
|
+
return { start: this.startIndex, end: this.endIndex };
|
|
3379
|
+
}
|
|
3380
|
+
jumpTo(targetIndex) {
|
|
3381
|
+
const half = Math.floor(this._pageSize / 2);
|
|
3382
|
+
let start = targetIndex - half;
|
|
3383
|
+
let end = start + this._pageSize;
|
|
3384
|
+
if (start < 0) {
|
|
3385
|
+
start = 0;
|
|
3386
|
+
end = Math.min(this._pageSize, this._total);
|
|
3387
|
+
}
|
|
3388
|
+
if (end > this._total) {
|
|
3389
|
+
end = this._total;
|
|
3390
|
+
start = Math.max(0, end - this._pageSize);
|
|
3391
|
+
}
|
|
3392
|
+
this.startIndex = start;
|
|
3393
|
+
this.endIndex = end;
|
|
3394
|
+
return { start, end };
|
|
3395
|
+
}
|
|
3396
|
+
jumpToEnd() {
|
|
3397
|
+
const start = Math.max(0, this._total - this._pageSize);
|
|
3398
|
+
const end = this._total;
|
|
3399
|
+
this.startIndex = start;
|
|
3400
|
+
this.endIndex = end;
|
|
3401
|
+
return { start, end };
|
|
3402
|
+
}
|
|
3403
|
+
updateTotal(total) {
|
|
3404
|
+
this._total = total;
|
|
3405
|
+
}
|
|
3406
|
+
reset() {
|
|
3407
|
+
this.startIndex = 0;
|
|
3408
|
+
this.endIndex = 0;
|
|
3409
|
+
this.isLoading = false;
|
|
3410
|
+
this._total = 0;
|
|
3411
|
+
}
|
|
3412
|
+
contains(index) {
|
|
3413
|
+
return index >= this.startIndex && index < this.endIndex;
|
|
3414
|
+
}
|
|
3415
|
+
syncFromInputs(start, end, total) {
|
|
3416
|
+
this.startIndex = start;
|
|
3417
|
+
this.endIndex = end;
|
|
3418
|
+
this._total = total;
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
|
|
2936
3422
|
/**
|
|
2937
3423
|
* @hidden
|
|
2938
3424
|
*/
|
|
@@ -3113,7 +3599,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
3113
3599
|
* ```html
|
|
3114
3600
|
* <kendo-promptbox>
|
|
3115
3601
|
* <kendo-promptbox-end-affix>
|
|
3116
|
-
* <button kendoButton
|
|
3602
|
+
* <button kendoButton fillMode="clear" [svgIcon]="imageIcon"></button>
|
|
3117
3603
|
* </kendo-promptbox-end-affix>
|
|
3118
3604
|
* </kendo-promptbox>
|
|
3119
3605
|
* ```
|
|
@@ -3146,7 +3632,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
3146
3632
|
* ```html
|
|
3147
3633
|
* <kendo-promptbox>
|
|
3148
3634
|
* <kendo-promptbox-start-affix>
|
|
3149
|
-
* <button kendoButton
|
|
3635
|
+
* <button kendoButton fillMode="clear" [svgIcon]="imageIcon"></button>
|
|
3150
3636
|
* </kendo-promptbox-start-affix>
|
|
3151
3637
|
* </kendo-promptbox>
|
|
3152
3638
|
* ```
|
|
@@ -3179,7 +3665,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
3179
3665
|
* ```html
|
|
3180
3666
|
* <kendo-promptbox>
|
|
3181
3667
|
* <kendo-promptbox-top-affix>
|
|
3182
|
-
* <button kendoButton
|
|
3668
|
+
* <button kendoButton fillMode="clear" [svgIcon]="imageIcon"></button>
|
|
3183
3669
|
* </kendo-promptbox-top-affix>
|
|
3184
3670
|
* </kendo-promptbox>
|
|
3185
3671
|
* ```
|
|
@@ -5538,7 +6024,7 @@ const groupMessages = (acc, msg, isLastMessage) => {
|
|
|
5538
6024
|
messages: [msg],
|
|
5539
6025
|
author: msg.author,
|
|
5540
6026
|
timestamp: msg.timestamp,
|
|
5541
|
-
trackBy: msg
|
|
6027
|
+
trackBy: msg.id
|
|
5542
6028
|
});
|
|
5543
6029
|
}
|
|
5544
6030
|
};
|
|
@@ -6763,8 +7249,16 @@ class MessageComponent extends ChatItem {
|
|
|
6763
7249
|
chatService;
|
|
6764
7250
|
localization;
|
|
6765
7251
|
cdr;
|
|
7252
|
+
zone;
|
|
6766
7253
|
set message(value) {
|
|
7254
|
+
const textChanged = this._message && value && this._message.text !== value.text;
|
|
6767
7255
|
this._message = value;
|
|
7256
|
+
if (textChanged) {
|
|
7257
|
+
this.parts = this.getFormattedTextParts(value.text);
|
|
7258
|
+
if (this.isMessageExpandable && !this.showExpandCollapseIcon) {
|
|
7259
|
+
this.updateExpandCollapseIconAfterStable();
|
|
7260
|
+
}
|
|
7261
|
+
}
|
|
6768
7262
|
}
|
|
6769
7263
|
get message() {
|
|
6770
7264
|
return this._message;
|
|
@@ -6856,13 +7350,14 @@ class MessageComponent extends ChatItem {
|
|
|
6856
7350
|
}
|
|
6857
7351
|
subs = new Subscription();
|
|
6858
7352
|
_message;
|
|
6859
|
-
constructor(element, intl, chatService, localization, cdr) {
|
|
7353
|
+
constructor(element, intl, chatService, localization, cdr, zone) {
|
|
6860
7354
|
super();
|
|
6861
7355
|
this.element = element;
|
|
6862
7356
|
this.intl = intl;
|
|
6863
7357
|
this.chatService = chatService;
|
|
6864
7358
|
this.localization = localization;
|
|
6865
7359
|
this.cdr = cdr;
|
|
7360
|
+
this.zone = zone;
|
|
6866
7361
|
}
|
|
6867
7362
|
ngOnInit() {
|
|
6868
7363
|
this.fileActions = this.getFileActions();
|
|
@@ -6873,14 +7368,20 @@ class MessageComponent extends ChatItem {
|
|
|
6873
7368
|
this.subs.add(settingsChange$.subscribe(() => {
|
|
6874
7369
|
this.fileActions = this.getFileActions();
|
|
6875
7370
|
this.toolbarActions = this.getToolbarActions();
|
|
6876
|
-
|
|
6877
|
-
this.
|
|
6878
|
-
}
|
|
7371
|
+
if (this.isMessageExpandable) {
|
|
7372
|
+
this.updateExpandCollapseIconAfterStable();
|
|
7373
|
+
}
|
|
6879
7374
|
}));
|
|
6880
7375
|
this.subs.add(this.chatService.allowMessageCollapseChange$.subscribe(() => {
|
|
6881
|
-
|
|
6882
|
-
this.
|
|
6883
|
-
}
|
|
7376
|
+
if (this.isMessageExpandable) {
|
|
7377
|
+
this.updateExpandCollapseIconAfterStable();
|
|
7378
|
+
}
|
|
7379
|
+
else {
|
|
7380
|
+
this.showExpandCollapseIcon = false;
|
|
7381
|
+
}
|
|
7382
|
+
}));
|
|
7383
|
+
this.subs.add(this.chatService.messageToolbarActionsChange$.subscribe(() => {
|
|
7384
|
+
this.toolbarActions = this.getToolbarActions();
|
|
6884
7385
|
}));
|
|
6885
7386
|
if (this.message.id) {
|
|
6886
7387
|
this.chatService.registerMessageElement(this.message.id, this.element);
|
|
@@ -6888,8 +7389,9 @@ class MessageComponent extends ChatItem {
|
|
|
6888
7389
|
this.parts = this.getFormattedTextParts(this.message.text);
|
|
6889
7390
|
}
|
|
6890
7391
|
ngAfterViewInit() {
|
|
6891
|
-
|
|
6892
|
-
|
|
7392
|
+
if (this.isMessageExpandable) {
|
|
7393
|
+
this.updateExpandCollapseIconAfterStable();
|
|
7394
|
+
}
|
|
6893
7395
|
}
|
|
6894
7396
|
ngOnDestroy() {
|
|
6895
7397
|
if (this.message.id) {
|
|
@@ -6960,6 +7462,7 @@ class MessageComponent extends ChatItem {
|
|
|
6960
7462
|
event.stopImmediatePropagation();
|
|
6961
7463
|
this.isMessageExpanded = !this.isMessageExpanded;
|
|
6962
7464
|
this.chatService.toggleMessageState = false;
|
|
7465
|
+
this.cdr.detectChanges();
|
|
6963
7466
|
}
|
|
6964
7467
|
onExpandableKeydown(event) {
|
|
6965
7468
|
const key = normalizeKeys(event);
|
|
@@ -7074,7 +7577,13 @@ class MessageComponent extends ChatItem {
|
|
|
7074
7577
|
: this.chatService.fileActions || [];
|
|
7075
7578
|
return transformActions(actions);
|
|
7076
7579
|
}
|
|
7077
|
-
|
|
7580
|
+
updateExpandCollapseIconAfterStable() {
|
|
7581
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
7582
|
+
this.showExpandCollapseIcon = this.calculateExpandCollapseIconVisibility();
|
|
7583
|
+
this.cdr.detectChanges();
|
|
7584
|
+
}));
|
|
7585
|
+
}
|
|
7586
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: MessageComponent, deps: [{ token: i0.ElementRef }, { token: i1$1.IntlService }, { token: ChatService }, { token: i1.LocalizationService }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
|
|
7078
7587
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.20", 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-failed": "this.failedClass", "class.k-message-removed": "this.removedClass", "attr.tabIndex": "this.tabIndex" } }, providers: [
|
|
7079
7588
|
{
|
|
7080
7589
|
provide: ChatItem,
|
|
@@ -7159,7 +7668,7 @@ class MessageComponent extends ChatItem {
|
|
|
7159
7668
|
</span>
|
|
7160
7669
|
} @if (!message.isDeleted && parts.length > 0) {
|
|
7161
7670
|
<span class="k-chat-bubble-text">
|
|
7162
|
-
@for (part of parts; track
|
|
7671
|
+
@for (part of parts; track $index) { @if (part.type === 'text') {{{part.content}}} @if (part.type ===
|
|
7163
7672
|
'link') {<a [href]="part.href" target="_blank">{{ part.content }}</a
|
|
7164
7673
|
>} }
|
|
7165
7674
|
</span>
|
|
@@ -7172,7 +7681,7 @@ class MessageComponent extends ChatItem {
|
|
|
7172
7681
|
'k-files-vertical': chatService.messageFilesLayout === 'vertical'
|
|
7173
7682
|
}"
|
|
7174
7683
|
>
|
|
7175
|
-
@for (file of message.files; track file) {
|
|
7684
|
+
@for (file of message.files; track file.id) {
|
|
7176
7685
|
<li
|
|
7177
7686
|
class="k-file-box"
|
|
7178
7687
|
[chatFile]="file"
|
|
@@ -7186,7 +7695,7 @@ class MessageComponent extends ChatItem {
|
|
|
7186
7695
|
} @else {
|
|
7187
7696
|
<ul class="k-file-box-wrapper">
|
|
7188
7697
|
<div class="k-files-scroll">
|
|
7189
|
-
@for (file of message.files; track file) {
|
|
7698
|
+
@for (file of message.files; track file.id) {
|
|
7190
7699
|
<li
|
|
7191
7700
|
class="k-file-box"
|
|
7192
7701
|
[chatFile]="file"
|
|
@@ -7272,7 +7781,7 @@ class MessageComponent extends ChatItem {
|
|
|
7272
7781
|
</div>
|
|
7273
7782
|
} } @if (showToolbar) {
|
|
7274
7783
|
<kendo-toolbar class="k-chat-message-toolbar" fillMode="flat">
|
|
7275
|
-
@for (action of toolbarActions; track action) {
|
|
7784
|
+
@for (action of toolbarActions; track action.id) {
|
|
7276
7785
|
<kendo-toolbar-button
|
|
7277
7786
|
fillMode="flat"
|
|
7278
7787
|
[icon]="action.icon"
|
|
@@ -7376,7 +7885,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
7376
7885
|
</span>
|
|
7377
7886
|
} @if (!message.isDeleted && parts.length > 0) {
|
|
7378
7887
|
<span class="k-chat-bubble-text">
|
|
7379
|
-
@for (part of parts; track
|
|
7888
|
+
@for (part of parts; track $index) { @if (part.type === 'text') {{{part.content}}} @if (part.type ===
|
|
7380
7889
|
'link') {<a [href]="part.href" target="_blank">{{ part.content }}</a
|
|
7381
7890
|
>} }
|
|
7382
7891
|
</span>
|
|
@@ -7389,7 +7898,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
7389
7898
|
'k-files-vertical': chatService.messageFilesLayout === 'vertical'
|
|
7390
7899
|
}"
|
|
7391
7900
|
>
|
|
7392
|
-
@for (file of message.files; track file) {
|
|
7901
|
+
@for (file of message.files; track file.id) {
|
|
7393
7902
|
<li
|
|
7394
7903
|
class="k-file-box"
|
|
7395
7904
|
[chatFile]="file"
|
|
@@ -7403,7 +7912,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
7403
7912
|
} @else {
|
|
7404
7913
|
<ul class="k-file-box-wrapper">
|
|
7405
7914
|
<div class="k-files-scroll">
|
|
7406
|
-
@for (file of message.files; track file) {
|
|
7915
|
+
@for (file of message.files; track file.id) {
|
|
7407
7916
|
<li
|
|
7408
7917
|
class="k-file-box"
|
|
7409
7918
|
[chatFile]="file"
|
|
@@ -7489,7 +7998,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
7489
7998
|
</div>
|
|
7490
7999
|
} } @if (showToolbar) {
|
|
7491
8000
|
<kendo-toolbar class="k-chat-message-toolbar" fillMode="flat">
|
|
7492
|
-
@for (action of toolbarActions; track action) {
|
|
8001
|
+
@for (action of toolbarActions; track action.id) {
|
|
7493
8002
|
<kendo-toolbar-button
|
|
7494
8003
|
fillMode="flat"
|
|
7495
8004
|
[icon]="action.icon"
|
|
@@ -7516,7 +8025,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
7516
8025
|
ButtonComponent,
|
|
7517
8026
|
],
|
|
7518
8027
|
}]
|
|
7519
|
-
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1$1.IntlService }, { type: ChatService }, { type: i1.LocalizationService }, { type: i0.ChangeDetectorRef }], propDecorators: { message: [{
|
|
8028
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1$1.IntlService }, { type: ChatService }, { type: i1.LocalizationService }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }], propDecorators: { message: [{
|
|
7520
8029
|
type: Input
|
|
7521
8030
|
}], tabbable: [{
|
|
7522
8031
|
type: Input
|
|
@@ -8074,6 +8583,7 @@ class MessageListComponent {
|
|
|
8074
8583
|
item.selected = true;
|
|
8075
8584
|
this.selectedItem = item;
|
|
8076
8585
|
}
|
|
8586
|
+
this.chatService.layoutChangeInProgress = true;
|
|
8077
8587
|
this.cdr.detectChanges();
|
|
8078
8588
|
}
|
|
8079
8589
|
this.chatService.toggleMessageState = false;
|
|
@@ -8238,7 +8748,7 @@ class MessageListComponent {
|
|
|
8238
8748
|
<div class="k-message-group-content">
|
|
8239
8749
|
@if (showGroupAuthor(group)) {
|
|
8240
8750
|
<p class="k-message-author">{{ group.author.name }}</p>
|
|
8241
|
-
} @for (msg of group.messages; track msg; let firstMessage = $first; let lastMessage = $last) { @if
|
|
8751
|
+
} @for (msg of group.messages; track msg.id; let firstMessage = $first; let lastMessage = $last) { @if
|
|
8242
8752
|
(msg.user?.avatarUrl) {
|
|
8243
8753
|
<div class="k-avatar">
|
|
8244
8754
|
<span class="k-avatar-image">
|
|
@@ -8346,7 +8856,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
8346
8856
|
<div class="k-message-group-content">
|
|
8347
8857
|
@if (showGroupAuthor(group)) {
|
|
8348
8858
|
<p class="k-message-author">{{ group.author.name }}</p>
|
|
8349
|
-
} @for (msg of group.messages; track msg; let firstMessage = $first; let lastMessage = $last) { @if
|
|
8859
|
+
} @for (msg of group.messages; track msg.id; let firstMessage = $first; let lastMessage = $last) { @if
|
|
8350
8860
|
(msg.user?.avatarUrl) {
|
|
8351
8861
|
<div class="k-avatar">
|
|
8352
8862
|
<span class="k-avatar-image">
|
|
@@ -8666,6 +9176,86 @@ class ChatComponent {
|
|
|
8666
9176
|
get sendButtonSettings() {
|
|
8667
9177
|
return this.sendButton;
|
|
8668
9178
|
}
|
|
9179
|
+
/**
|
|
9180
|
+
* Controls the scrolling behavior of the message list.
|
|
9181
|
+
*
|
|
9182
|
+
* @default 'scrollable'
|
|
9183
|
+
*/
|
|
9184
|
+
scrollMode = 'scrollable';
|
|
9185
|
+
/**
|
|
9186
|
+
* Sets the number of messages loaded per page in endless scroll mode.
|
|
9187
|
+
* Ignored in `scrollable` mode.
|
|
9188
|
+
*
|
|
9189
|
+
* @default 50
|
|
9190
|
+
*/
|
|
9191
|
+
pageSize = 50;
|
|
9192
|
+
/**
|
|
9193
|
+
* Sets the minimum distance between a new receiver message and the top of the visible
|
|
9194
|
+
* message area when auto-scrolling. Keeps older messages partially visible instead of
|
|
9195
|
+
* scrolling them out of view.
|
|
9196
|
+
*
|
|
9197
|
+
* Accepts a percentage string relative to the visible area height (for example, `'30%'`)
|
|
9198
|
+
* or a pixel value as a number. Negative values scroll the message fully to the bottom.
|
|
9199
|
+
*
|
|
9200
|
+
* Has no effect on author messages, which always scroll to the bottom.
|
|
9201
|
+
*
|
|
9202
|
+
* @default '20%'
|
|
9203
|
+
*/
|
|
9204
|
+
autoScrollThreshold = '20%';
|
|
9205
|
+
/**
|
|
9206
|
+
* Sets the total number of messages in the conversation.
|
|
9207
|
+
*
|
|
9208
|
+
* When `messages.length` equals `total` (or `total` is not set), the Chat operates in
|
|
9209
|
+
* **built-in mode** and manages rendering internally. When `messages.length < total`,
|
|
9210
|
+
* the Chat operates in **remote mode** — the consumer manages data via
|
|
9211
|
+
* [`loadMore`](slug:api_conversational-ui_chatcomponent#loadMore) events.
|
|
9212
|
+
*
|
|
9213
|
+
* Only relevant when [`scrollMode`](slug:api_conversational-ui_chatcomponent#scrollMode)
|
|
9214
|
+
* is `'endless'`.
|
|
9215
|
+
*/
|
|
9216
|
+
total;
|
|
9217
|
+
/**
|
|
9218
|
+
* Sets the index in the full conversation that `messages[0]` corresponds to.
|
|
9219
|
+
*
|
|
9220
|
+
* Only relevant in remote mode (`messages.length < total`). As the user scrolls up
|
|
9221
|
+
* and the consumer prepends messages, `startIndex` decreases. The Chat uses this to
|
|
9222
|
+
* compute the correct range for the next
|
|
9223
|
+
* [`loadMore`](slug:api_conversational-ui_chatcomponent#loadMore) call.
|
|
9224
|
+
*
|
|
9225
|
+
* Ignored in built-in mode.
|
|
9226
|
+
*/
|
|
9227
|
+
startIndex;
|
|
9228
|
+
/**
|
|
9229
|
+
* Sets the exclusive end index of the current batch in the full conversation.
|
|
9230
|
+
*
|
|
9231
|
+
* Only relevant in remote mode (`messages.length < total`). As the user scrolls down
|
|
9232
|
+
* and the consumer appends messages, `endIndex` increases. The Chat uses this to know
|
|
9233
|
+
* whether more messages exist below the current batch.
|
|
9234
|
+
*
|
|
9235
|
+
* Ignored in built-in mode.
|
|
9236
|
+
*/
|
|
9237
|
+
endIndex;
|
|
9238
|
+
/**
|
|
9239
|
+
* Sets the full set of pinned messages in the conversation.
|
|
9240
|
+
*
|
|
9241
|
+
* Only needed in remote mode (`messages.length < total`). The Chat uses this to
|
|
9242
|
+
* render the pinned message indicator when the pinned message is outside the current
|
|
9243
|
+
* batch. In built-in mode, the Chat has all messages and can look them up directly.
|
|
9244
|
+
*
|
|
9245
|
+
* @default []
|
|
9246
|
+
*/
|
|
9247
|
+
pinnedMessages = [];
|
|
9248
|
+
/**
|
|
9249
|
+
* Sets the reply targets that live outside the current batch.
|
|
9250
|
+
*
|
|
9251
|
+
* Only needed in remote mode (`messages.length < total`). The Chat uses this to
|
|
9252
|
+
* render reply previews when the replied-to message is not in the current batch.
|
|
9253
|
+
* The consumer resolves this after each data fetch by finding which `replyToId`
|
|
9254
|
+
* targets are missing from the batch and fetching only those.
|
|
9255
|
+
*
|
|
9256
|
+
* @default []
|
|
9257
|
+
*/
|
|
9258
|
+
repliedToMessages = [];
|
|
8669
9259
|
/**
|
|
8670
9260
|
* Sets the names of the model fields from which the Chat reads its data.
|
|
8671
9261
|
* Lets you map custom data types to the expected `Message` format.
|
|
@@ -8743,6 +9333,14 @@ class ChatComponent {
|
|
|
8743
9333
|
* Fires when the user types in the message input box.
|
|
8744
9334
|
*/
|
|
8745
9335
|
inputValueChange = new EventEmitter();
|
|
9336
|
+
/**
|
|
9337
|
+
* Fires when the user scrolls near the edge of the rendered message window in endless scroll mode.
|
|
9338
|
+
*/
|
|
9339
|
+
loadMore = new EventEmitter();
|
|
9340
|
+
/**
|
|
9341
|
+
* Fires when the user clicks a referenced message (pinned indicator or reply preview).
|
|
9342
|
+
*/
|
|
9343
|
+
referencedMessageClick = new EventEmitter();
|
|
8746
9344
|
get className() {
|
|
8747
9345
|
return 'k-chat';
|
|
8748
9346
|
}
|
|
@@ -8793,6 +9391,43 @@ class ChatComponent {
|
|
|
8793
9391
|
}
|
|
8794
9392
|
return this.messages;
|
|
8795
9393
|
}
|
|
9394
|
+
/**
|
|
9395
|
+
* @hidden
|
|
9396
|
+
* Returns `true` when the Chat is in remote endless scroll mode.
|
|
9397
|
+
* Remote mode is active whenever the consumer provides a `total`.
|
|
9398
|
+
*/
|
|
9399
|
+
get isRemoteMode() {
|
|
9400
|
+
return this.scrollMode === 'endless'
|
|
9401
|
+
&& isPresent(this.total);
|
|
9402
|
+
}
|
|
9403
|
+
/**
|
|
9404
|
+
* @hidden
|
|
9405
|
+
* Returns the messages to render — sliced in endless mode, full in scrollable mode.
|
|
9406
|
+
* In remote mode, returns processedMessages as-is (consumer already provides only the current batch).
|
|
9407
|
+
*/
|
|
9408
|
+
get renderedMessages() {
|
|
9409
|
+
const all = this.processedMessages;
|
|
9410
|
+
if (!all || all.length === 0) {
|
|
9411
|
+
return [];
|
|
9412
|
+
}
|
|
9413
|
+
if (this.scrollMode === 'endless') {
|
|
9414
|
+
if (this.isRemoteMode) {
|
|
9415
|
+
return all;
|
|
9416
|
+
}
|
|
9417
|
+
const start = this.endlessState.startIndex;
|
|
9418
|
+
const end = this.endlessState.endIndex;
|
|
9419
|
+
if (all !== this._renderedMessagesSource ||
|
|
9420
|
+
start !== this._renderedMessagesStart ||
|
|
9421
|
+
end !== this._renderedMessagesEnd) {
|
|
9422
|
+
this._cachedRenderedMessages = all.slice(start, end);
|
|
9423
|
+
this._renderedMessagesSource = all;
|
|
9424
|
+
this._renderedMessagesStart = start;
|
|
9425
|
+
this._renderedMessagesEnd = end;
|
|
9426
|
+
}
|
|
9427
|
+
return this._cachedRenderedMessages;
|
|
9428
|
+
}
|
|
9429
|
+
return all;
|
|
9430
|
+
}
|
|
8796
9431
|
/**
|
|
8797
9432
|
* @hidden
|
|
8798
9433
|
*/
|
|
@@ -8838,6 +9473,19 @@ class ChatComponent {
|
|
|
8838
9473
|
* @hidden
|
|
8839
9474
|
*/
|
|
8840
9475
|
scrollToBottomIcon = arrowDownOutlineIcon;
|
|
9476
|
+
/**
|
|
9477
|
+
* @hidden
|
|
9478
|
+
*/
|
|
9479
|
+
endlessState = new EndlessScrollState();
|
|
9480
|
+
/**
|
|
9481
|
+
* @hidden
|
|
9482
|
+
*/
|
|
9483
|
+
showLicenseWatermark = false;
|
|
9484
|
+
/**
|
|
9485
|
+
* @hidden
|
|
9486
|
+
*/
|
|
9487
|
+
licenseMessage;
|
|
9488
|
+
anchor;
|
|
8841
9489
|
direction;
|
|
8842
9490
|
subs = new Subscription();
|
|
8843
9491
|
_modelFields = defaultModelFields;
|
|
@@ -8848,13 +9496,25 @@ class ChatComponent {
|
|
|
8848
9496
|
_lastModelFields = null;
|
|
8849
9497
|
_cachedContextMenuActions = [];
|
|
8850
9498
|
_lastContextMenuActionsReference = null;
|
|
9499
|
+
_previousMessagesLength = 0;
|
|
9500
|
+
_pendingScrollAction = null;
|
|
9501
|
+
_scrollHandledBeforePaint = false;
|
|
9502
|
+
_lastNewMessageId = null;
|
|
9503
|
+
_cachedRenderedMessages = [];
|
|
9504
|
+
_renderedMessagesSource = null;
|
|
9505
|
+
_renderedMessagesStart = -1;
|
|
9506
|
+
_renderedMessagesEnd = -1;
|
|
9507
|
+
_pendingRemoteScrollToMessageId = null;
|
|
9508
|
+
_remoteScrollToBottom = false;
|
|
8851
9509
|
constructor(localization, zone, renderer, element, chatService) {
|
|
8852
9510
|
this.localization = localization;
|
|
8853
9511
|
this.zone = zone;
|
|
8854
9512
|
this.renderer = renderer;
|
|
8855
9513
|
this.element = element;
|
|
8856
9514
|
this.chatService = chatService;
|
|
8857
|
-
validatePackage(packageMetadata);
|
|
9515
|
+
const isValid = validatePackage(packageMetadata);
|
|
9516
|
+
this.licenseMessage = getLicenseMessage(packageMetadata);
|
|
9517
|
+
this.showLicenseWatermark = shouldShowValidationUI(isValid);
|
|
8858
9518
|
this.direction = localization.rtl ? 'rtl' : 'ltr';
|
|
8859
9519
|
this.subs.add(localization.changes.subscribe(({ rtl }) => {
|
|
8860
9520
|
this.direction = rtl ? 'rtl' : 'ltr';
|
|
@@ -8900,17 +9560,53 @@ class ChatComponent {
|
|
|
8900
9560
|
}));
|
|
8901
9561
|
this.pinnedMessage = this.findLastPinnedMessage();
|
|
8902
9562
|
this.chatService.authorId = this.authorId;
|
|
9563
|
+
this.chatService.repliedToMessages = this.repliedToMessages || [];
|
|
9564
|
+
this.initEndlessScroll();
|
|
9565
|
+
this.subs.add(this.chatService.replyReferenceClick$.subscribe((messageId) => {
|
|
9566
|
+
this.handleReferencedMessageClick(messageId, 'reply');
|
|
9567
|
+
}));
|
|
8903
9568
|
}
|
|
8904
9569
|
/**
|
|
8905
9570
|
* @hidden
|
|
8906
9571
|
*/
|
|
8907
9572
|
ngOnChanges(changes) {
|
|
8908
|
-
this.zone.runOutsideAngular(() => setTimeout(() => {
|
|
8909
|
-
this.messageList.element.nativeElement.style.flex = '1 1 auto';
|
|
8910
|
-
}));
|
|
8911
9573
|
if (isChanged('messages', changes, false)) {
|
|
8912
9574
|
this.pinnedMessage = this.findLastPinnedMessage();
|
|
8913
9575
|
this.chatService.messages = this.processedMessages;
|
|
9576
|
+
if (this.isRemoteMode) {
|
|
9577
|
+
const skipDetectAndSchedule = this.endlessState.isLoading || !!this._pendingRemoteScrollToMessageId;
|
|
9578
|
+
if (!skipDetectAndSchedule) {
|
|
9579
|
+
this.detectNewMessageScrollAction();
|
|
9580
|
+
}
|
|
9581
|
+
this.handleRemoteMessagesChange();
|
|
9582
|
+
if (!skipDetectAndSchedule) {
|
|
9583
|
+
this.scheduleNewMessageScroll();
|
|
9584
|
+
}
|
|
9585
|
+
}
|
|
9586
|
+
else {
|
|
9587
|
+
this.detectNewMessageScrollAction();
|
|
9588
|
+
this.handleMessagesChange();
|
|
9589
|
+
this.scheduleNewMessageScroll();
|
|
9590
|
+
}
|
|
9591
|
+
this.handlePendingRemoteScroll();
|
|
9592
|
+
}
|
|
9593
|
+
if (isChanged('pinnedMessages', changes, false)) {
|
|
9594
|
+
this.pinnedMessage = this.findLastPinnedMessage();
|
|
9595
|
+
}
|
|
9596
|
+
if (isChanged('repliedToMessages', changes, false)) {
|
|
9597
|
+
this.chatService.repliedToMessages = this.repliedToMessages || [];
|
|
9598
|
+
}
|
|
9599
|
+
this.validateRemoteInputs();
|
|
9600
|
+
if (isChanged('scrollMode', changes, false)) {
|
|
9601
|
+
this.initEndlessScroll();
|
|
9602
|
+
}
|
|
9603
|
+
if (isChanged('pageSize', changes, false) && this.scrollMode === 'endless') {
|
|
9604
|
+
this.endlessState.init(this.processedMessages?.length || 0, this.pageSize);
|
|
9605
|
+
}
|
|
9606
|
+
if (this.isRemoteMode && (isChanged('startIndex', changes, false) ||
|
|
9607
|
+
isChanged('endIndex', changes, false) ||
|
|
9608
|
+
isChanged('total', changes, false))) {
|
|
9609
|
+
this.endlessState.syncFromInputs(this.startIndex, this.endIndex, this.total);
|
|
8914
9610
|
}
|
|
8915
9611
|
if (isChanged('height', changes, false)) {
|
|
8916
9612
|
this.renderer.setStyle(this.element.nativeElement, 'height', `${processCssValue(this.height)}`);
|
|
@@ -8980,9 +9676,130 @@ class ChatComponent {
|
|
|
8980
9676
|
/**
|
|
8981
9677
|
* @hidden
|
|
8982
9678
|
*/
|
|
8983
|
-
scrollToPinnedMessage() {
|
|
9679
|
+
scrollToPinnedMessage(event) {
|
|
9680
|
+
if (event && event.target?.closest('button')) {
|
|
9681
|
+
return;
|
|
9682
|
+
}
|
|
8984
9683
|
if (this.pinnedMessage) {
|
|
8985
|
-
this.
|
|
9684
|
+
this.handleReferencedMessageClick(this.pinnedMessage.id, 'pinned');
|
|
9685
|
+
}
|
|
9686
|
+
}
|
|
9687
|
+
/**
|
|
9688
|
+
* @hidden
|
|
9689
|
+
*/
|
|
9690
|
+
onNearTop() {
|
|
9691
|
+
if (this.scrollMode !== 'endless' || this.endlessState.isLoading) {
|
|
9692
|
+
return;
|
|
9693
|
+
}
|
|
9694
|
+
if (this.isRemoteMode) {
|
|
9695
|
+
if (this.startIndex <= 0) {
|
|
9696
|
+
return;
|
|
9697
|
+
}
|
|
9698
|
+
this.endlessState.isLoading = true;
|
|
9699
|
+
this.anchor?.recordScrollHeight();
|
|
9700
|
+
this.anchor?.setAriaLive('off');
|
|
9701
|
+
const reqStart = Math.max(0, this.startIndex - this.pageSize);
|
|
9702
|
+
this.loadMore.emit({ startIndex: reqStart, endIndex: this.startIndex });
|
|
9703
|
+
return;
|
|
9704
|
+
}
|
|
9705
|
+
const range = this.endlessState.extendUp();
|
|
9706
|
+
if (range) {
|
|
9707
|
+
this.endlessState.isLoading = true;
|
|
9708
|
+
this.anchor?.recordScrollHeight();
|
|
9709
|
+
this.anchor?.setAriaLive('off');
|
|
9710
|
+
this.loadMore.emit({ startIndex: range.start, endIndex: range.end });
|
|
9711
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
9712
|
+
this.anchor?.preserveScrollPosition();
|
|
9713
|
+
this.anchor?.setAriaLive('polite');
|
|
9714
|
+
this.endlessState.isLoading = false;
|
|
9715
|
+
}));
|
|
9716
|
+
}
|
|
9717
|
+
}
|
|
9718
|
+
/**
|
|
9719
|
+
* @hidden
|
|
9720
|
+
*/
|
|
9721
|
+
onNearBottom() {
|
|
9722
|
+
if (this.scrollMode !== 'endless' || this.endlessState.isLoading) {
|
|
9723
|
+
return;
|
|
9724
|
+
}
|
|
9725
|
+
if (this.isRemoteMode) {
|
|
9726
|
+
if (this.endIndex >= this.total) {
|
|
9727
|
+
return;
|
|
9728
|
+
}
|
|
9729
|
+
this.endlessState.isLoading = true;
|
|
9730
|
+
this.anchor?.setAriaLive('off');
|
|
9731
|
+
const reqEnd = Math.min(this.total, this.endIndex + this.pageSize);
|
|
9732
|
+
this.loadMore.emit({ startIndex: this.endIndex, endIndex: reqEnd });
|
|
9733
|
+
return;
|
|
9734
|
+
}
|
|
9735
|
+
const range = this.endlessState.extendDown();
|
|
9736
|
+
if (range) {
|
|
9737
|
+
this.endlessState.isLoading = true;
|
|
9738
|
+
this.loadMore.emit({ startIndex: range.start, endIndex: range.end });
|
|
9739
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
9740
|
+
this.endlessState.isLoading = false;
|
|
9741
|
+
}));
|
|
9742
|
+
}
|
|
9743
|
+
}
|
|
9744
|
+
/**
|
|
9745
|
+
* @hidden
|
|
9746
|
+
*/
|
|
9747
|
+
onScrollToBottomClick() {
|
|
9748
|
+
if (this.scrollMode === 'endless' && !this.endlessState.isAtEnd) {
|
|
9749
|
+
if (this.isRemoteMode) {
|
|
9750
|
+
this.endlessState.isLoading = true;
|
|
9751
|
+
this._remoteScrollToBottom = true;
|
|
9752
|
+
this.autoScroll = true;
|
|
9753
|
+
const reqStart = Math.max(0, this.total - this.pageSize);
|
|
9754
|
+
this.loadMore.emit({ startIndex: reqStart, endIndex: this.total });
|
|
9755
|
+
return;
|
|
9756
|
+
}
|
|
9757
|
+
this.anchor?.lockForMessageScroll();
|
|
9758
|
+
const range = this.endlessState.jumpToEnd();
|
|
9759
|
+
this.loadMore.emit({ startIndex: range.start, endIndex: range.end });
|
|
9760
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
9761
|
+
this.anchor?.scrollToBottomWithSettle(() => {
|
|
9762
|
+
this.anchor?.unlockForMessageScroll();
|
|
9763
|
+
});
|
|
9764
|
+
}));
|
|
9765
|
+
}
|
|
9766
|
+
else {
|
|
9767
|
+
this.anchor?.scrollToBottom();
|
|
9768
|
+
}
|
|
9769
|
+
}
|
|
9770
|
+
/**
|
|
9771
|
+
* @hidden
|
|
9772
|
+
*/
|
|
9773
|
+
handleReferencedMessageClick(messageId, type) {
|
|
9774
|
+
this.referencedMessageClick.emit({ id: messageId, type });
|
|
9775
|
+
this.autoScroll = false;
|
|
9776
|
+
if (this.scrollMode === 'endless') {
|
|
9777
|
+
if (this.isRemoteMode) {
|
|
9778
|
+
this.handleRemoteReferencedMessageClick(messageId);
|
|
9779
|
+
return;
|
|
9780
|
+
}
|
|
9781
|
+
const allMessages = this.processedMessages;
|
|
9782
|
+
const targetIndex = allMessages.findIndex(m => m.id === messageId);
|
|
9783
|
+
if (targetIndex === -1) {
|
|
9784
|
+
return;
|
|
9785
|
+
}
|
|
9786
|
+
if (this.endlessState.contains(targetIndex)) {
|
|
9787
|
+
this.anchor?.lockForMessageScroll();
|
|
9788
|
+
this.chatService.scrollToMessage(messageId, 'auto');
|
|
9789
|
+
this.anchor?.unlockForMessageScroll();
|
|
9790
|
+
}
|
|
9791
|
+
else {
|
|
9792
|
+
this.anchor?.lockForMessageScroll();
|
|
9793
|
+
const range = this.endlessState.jumpTo(targetIndex);
|
|
9794
|
+
this.loadMore.emit({ startIndex: range.start, endIndex: range.end });
|
|
9795
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
9796
|
+
this.chatService.scrollToMessage(messageId, 'auto');
|
|
9797
|
+
this.anchor?.unlockForMessageScroll();
|
|
9798
|
+
}));
|
|
9799
|
+
}
|
|
9800
|
+
}
|
|
9801
|
+
else {
|
|
9802
|
+
this.chatService.scrollToMessage(messageId);
|
|
8986
9803
|
}
|
|
8987
9804
|
}
|
|
8988
9805
|
/**
|
|
@@ -9022,8 +9839,203 @@ class ChatComponent {
|
|
|
9022
9839
|
this.chatService.selectOnMenuClose = false;
|
|
9023
9840
|
}
|
|
9024
9841
|
}
|
|
9842
|
+
/**
|
|
9843
|
+
* @hidden
|
|
9844
|
+
*/
|
|
9845
|
+
onMessageListResize() {
|
|
9846
|
+
this.anchor?.calculateMessageBoxSeparator();
|
|
9847
|
+
const layoutChange = this.chatService.layoutChangeInProgress;
|
|
9848
|
+
const skipAutoScroll = !!this._pendingScrollAction || this._scrollHandledBeforePaint || layoutChange;
|
|
9849
|
+
if (skipAutoScroll) {
|
|
9850
|
+
this._scrollHandledBeforePaint = false;
|
|
9851
|
+
if (layoutChange) {
|
|
9852
|
+
this.chatService.layoutChangeInProgress = false;
|
|
9853
|
+
this.anchor?.onScroll();
|
|
9854
|
+
}
|
|
9855
|
+
return;
|
|
9856
|
+
}
|
|
9857
|
+
this.anchor?.autoScrollToBottom();
|
|
9858
|
+
}
|
|
9859
|
+
/**
|
|
9860
|
+
* Schedules the new-message scroll adjustment on zone.onStable so it runs
|
|
9861
|
+
* after Angular renders the new DOM but before the browser paints,
|
|
9862
|
+
* preventing a visible intermediate frame.
|
|
9863
|
+
*/
|
|
9864
|
+
scheduleNewMessageScroll() {
|
|
9865
|
+
if (!this._pendingScrollAction) {
|
|
9866
|
+
return;
|
|
9867
|
+
}
|
|
9868
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
9869
|
+
this.zone.run(() => {
|
|
9870
|
+
const action = this._pendingScrollAction;
|
|
9871
|
+
this._pendingScrollAction = null;
|
|
9872
|
+
this._scrollHandledBeforePaint = true;
|
|
9873
|
+
if (action === 'author') {
|
|
9874
|
+
this._lastNewMessageId = null;
|
|
9875
|
+
this.autoScroll = true;
|
|
9876
|
+
this.anchor?.scrollToBottomWithSettle();
|
|
9877
|
+
return;
|
|
9878
|
+
}
|
|
9879
|
+
if (action === 'receiver') {
|
|
9880
|
+
const messageEl = this.getLastNewMessageElement();
|
|
9881
|
+
this._lastNewMessageId = null;
|
|
9882
|
+
this.autoScroll = true;
|
|
9883
|
+
if (messageEl) {
|
|
9884
|
+
this.anchor?.scrollWithThreshold(messageEl);
|
|
9885
|
+
}
|
|
9886
|
+
else {
|
|
9887
|
+
this.anchor?.scrollToBottom();
|
|
9888
|
+
}
|
|
9889
|
+
}
|
|
9890
|
+
});
|
|
9891
|
+
}));
|
|
9892
|
+
}
|
|
9893
|
+
detectNewMessageScrollAction() {
|
|
9894
|
+
const messages = this.processedMessages;
|
|
9895
|
+
const prevLength = this._previousMessagesLength;
|
|
9896
|
+
const hasNoMessages = !messages;
|
|
9897
|
+
const hasNoNewMessages = messages && messages.length <= prevLength;
|
|
9898
|
+
const isInitialLoad = prevLength === 0;
|
|
9899
|
+
if (hasNoMessages || hasNoNewMessages || isInitialLoad) {
|
|
9900
|
+
this._pendingScrollAction = null;
|
|
9901
|
+
this._lastNewMessageId = null;
|
|
9902
|
+
return;
|
|
9903
|
+
}
|
|
9904
|
+
const lastMessage = messages[messages.length - 1];
|
|
9905
|
+
this._lastNewMessageId = lastMessage?.id ?? null;
|
|
9906
|
+
const isAuthor = this.isOwnMessage(lastMessage);
|
|
9907
|
+
if (isAuthor) {
|
|
9908
|
+
this._pendingScrollAction = 'author';
|
|
9909
|
+
return;
|
|
9910
|
+
}
|
|
9911
|
+
if (!this.anchor) {
|
|
9912
|
+
this._pendingScrollAction = null;
|
|
9913
|
+
return;
|
|
9914
|
+
}
|
|
9915
|
+
const distance = this.anchor.getDistanceFromBottom();
|
|
9916
|
+
const threshold = this.anchor.getAutoScrollThresholdPx();
|
|
9917
|
+
const isNearBottom = distance <= threshold || this.anchor.isFollowingThreshold;
|
|
9918
|
+
this._pendingScrollAction = isNearBottom ? 'receiver' : null;
|
|
9919
|
+
}
|
|
9920
|
+
getLastNewMessageElement() {
|
|
9921
|
+
if (this._lastNewMessageId == null) {
|
|
9922
|
+
return null;
|
|
9923
|
+
}
|
|
9924
|
+
const ref = this.chatService.messageElementsMap.get(this._lastNewMessageId);
|
|
9925
|
+
return ref?.nativeElement ?? null;
|
|
9926
|
+
}
|
|
9025
9927
|
findLastPinnedMessage() {
|
|
9026
|
-
|
|
9928
|
+
const fromMessages = [...this.processedMessages].reverse().find((message) => message.isPinned);
|
|
9929
|
+
if (fromMessages) {
|
|
9930
|
+
return fromMessages;
|
|
9931
|
+
}
|
|
9932
|
+
if (this.isRemoteMode && this.pinnedMessages?.length) {
|
|
9933
|
+
return [...this.pinnedMessages].reverse().find((message) => message.isPinned);
|
|
9934
|
+
}
|
|
9935
|
+
return undefined;
|
|
9936
|
+
}
|
|
9937
|
+
initEndlessScroll() {
|
|
9938
|
+
if (this.scrollMode === 'endless') {
|
|
9939
|
+
if (this.isRemoteMode) {
|
|
9940
|
+
this.endlessState.syncFromInputs(this.startIndex ?? 0, this.endIndex ?? 0, this.total ?? 0);
|
|
9941
|
+
}
|
|
9942
|
+
else {
|
|
9943
|
+
this.endlessState.init(this.processedMessages?.length || 0, this.pageSize);
|
|
9944
|
+
}
|
|
9945
|
+
this._previousMessagesLength = this.processedMessages?.length || 0;
|
|
9946
|
+
}
|
|
9947
|
+
else {
|
|
9948
|
+
this.endlessState.reset();
|
|
9949
|
+
this._remoteScrollToBottom = false;
|
|
9950
|
+
this._pendingRemoteScrollToMessageId = null;
|
|
9951
|
+
}
|
|
9952
|
+
}
|
|
9953
|
+
handleMessagesChange() {
|
|
9954
|
+
if (this.scrollMode !== 'endless') {
|
|
9955
|
+
this._previousMessagesLength = this.processedMessages?.length || 0;
|
|
9956
|
+
return;
|
|
9957
|
+
}
|
|
9958
|
+
const allMessages = this.processedMessages || [];
|
|
9959
|
+
const wasAtEnd = this.endlessState.isAtEnd;
|
|
9960
|
+
const isNewMessageAppended = allMessages.length > this._previousMessagesLength && this._previousMessagesLength > 0;
|
|
9961
|
+
const isSameLength = allMessages.length === this._previousMessagesLength;
|
|
9962
|
+
const shouldScroll = this._pendingScrollAction !== null;
|
|
9963
|
+
if (isNewMessageAppended) {
|
|
9964
|
+
this.endlessState.updateTotal(allMessages.length);
|
|
9965
|
+
if (shouldScroll) {
|
|
9966
|
+
if (!wasAtEnd) {
|
|
9967
|
+
const range = this.endlessState.jumpToEnd();
|
|
9968
|
+
this.loadMore.emit({ startIndex: range.start, endIndex: range.end });
|
|
9969
|
+
}
|
|
9970
|
+
else {
|
|
9971
|
+
this.endlessState.endIndex = allMessages.length;
|
|
9972
|
+
}
|
|
9973
|
+
}
|
|
9974
|
+
}
|
|
9975
|
+
else if (!isSameLength) {
|
|
9976
|
+
this.endlessState.init(allMessages.length, this.pageSize);
|
|
9977
|
+
}
|
|
9978
|
+
this._previousMessagesLength = allMessages.length;
|
|
9979
|
+
}
|
|
9980
|
+
handleRemoteMessagesChange() {
|
|
9981
|
+
const wasLoading = this.endlessState.isLoading;
|
|
9982
|
+
const prevLength = this._previousMessagesLength;
|
|
9983
|
+
const currentLength = this.processedMessages?.length || 0;
|
|
9984
|
+
const isInitialBatch = prevLength === 0 && currentLength > 0;
|
|
9985
|
+
if (wasLoading) {
|
|
9986
|
+
this.endlessState.isLoading = false;
|
|
9987
|
+
if (this._remoteScrollToBottom) {
|
|
9988
|
+
this._remoteScrollToBottom = false;
|
|
9989
|
+
this._scrollHandledBeforePaint = true;
|
|
9990
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
9991
|
+
this.anchor?.scrollToBottomWithSettle();
|
|
9992
|
+
}));
|
|
9993
|
+
}
|
|
9994
|
+
else {
|
|
9995
|
+
this._scrollHandledBeforePaint = true;
|
|
9996
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
9997
|
+
this.anchor?.preserveScrollPosition();
|
|
9998
|
+
this.anchor?.setAriaLive('polite');
|
|
9999
|
+
}));
|
|
10000
|
+
}
|
|
10001
|
+
}
|
|
10002
|
+
else if (isInitialBatch) {
|
|
10003
|
+
this.autoScroll = true;
|
|
10004
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
10005
|
+
this.anchor?.scrollToBottom();
|
|
10006
|
+
}));
|
|
10007
|
+
}
|
|
10008
|
+
else if (currentLength > 0 && !this._pendingScrollAction) {
|
|
10009
|
+
const lastMessage = this.processedMessages[currentLength - 1];
|
|
10010
|
+
const isNowAtEnd = this.endIndex >= this.total;
|
|
10011
|
+
if (isNowAtEnd && this.isOwnMessage(lastMessage)) {
|
|
10012
|
+
this._pendingScrollAction = 'author';
|
|
10013
|
+
}
|
|
10014
|
+
}
|
|
10015
|
+
this._previousMessagesLength = currentLength;
|
|
10016
|
+
}
|
|
10017
|
+
handleRemoteReferencedMessageClick(messageId) {
|
|
10018
|
+
const messageEl = this.chatService.messageElementsMap.get(messageId);
|
|
10019
|
+
if (messageEl) {
|
|
10020
|
+
this.anchor?.lockForMessageScroll();
|
|
10021
|
+
this.chatService.scrollToMessage(messageId, 'auto');
|
|
10022
|
+
this.anchor?.unlockForMessageScroll();
|
|
10023
|
+
return;
|
|
10024
|
+
}
|
|
10025
|
+
this._pendingRemoteScrollToMessageId = messageId;
|
|
10026
|
+
}
|
|
10027
|
+
handlePendingRemoteScroll() {
|
|
10028
|
+
if (!this._pendingRemoteScrollToMessageId) {
|
|
10029
|
+
return;
|
|
10030
|
+
}
|
|
10031
|
+
const messageId = this._pendingRemoteScrollToMessageId;
|
|
10032
|
+
this._pendingRemoteScrollToMessageId = null;
|
|
10033
|
+
this._scrollHandledBeforePaint = true;
|
|
10034
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
10035
|
+
this.anchor?.lockForMessageScroll();
|
|
10036
|
+
this.chatService.scrollToMessage(messageId, 'auto');
|
|
10037
|
+
this.anchor?.unlockForMessageScroll();
|
|
10038
|
+
}));
|
|
9027
10039
|
}
|
|
9028
10040
|
updateChatServiceProperties(propNames, changes) {
|
|
9029
10041
|
propNames.forEach(propName => {
|
|
@@ -9032,6 +10044,34 @@ class ChatComponent {
|
|
|
9032
10044
|
}
|
|
9033
10045
|
});
|
|
9034
10046
|
}
|
|
10047
|
+
validateRemoteInputs() {
|
|
10048
|
+
if (!isDevMode()) {
|
|
10049
|
+
return;
|
|
10050
|
+
}
|
|
10051
|
+
const hasRemoteInputs = isPresent(this.total) || isPresent(this.startIndex) || isPresent(this.endIndex);
|
|
10052
|
+
if (hasRemoteInputs && this.scrollMode !== 'endless') {
|
|
10053
|
+
console.warn('[kendo-chat] [total], [startIndex], and [endIndex] are only used when [scrollMode] is \'endless\'.');
|
|
10054
|
+
return;
|
|
10055
|
+
}
|
|
10056
|
+
if (isPresent(this.total) && (!isPresent(this.startIndex) || !isPresent(this.endIndex))) {
|
|
10057
|
+
console.warn('[kendo-chat] Remote mode requires [startIndex] and [endIndex] when [total] is set.');
|
|
10058
|
+
}
|
|
10059
|
+
if (isPresent(this.startIndex) && this.startIndex < 0) {
|
|
10060
|
+
console.warn('[kendo-chat] [startIndex] must not be negative.');
|
|
10061
|
+
}
|
|
10062
|
+
if (isPresent(this.startIndex) && isPresent(this.endIndex) && this.startIndex > this.endIndex) {
|
|
10063
|
+
console.warn(`[kendo-chat] [startIndex] (${this.startIndex}) must not exceed [endIndex] (${this.endIndex}).`);
|
|
10064
|
+
}
|
|
10065
|
+
if (isPresent(this.endIndex) && isPresent(this.total) && this.endIndex > this.total) {
|
|
10066
|
+
console.warn(`[kendo-chat] [endIndex] (${this.endIndex}) must not exceed [total] (${this.total}).`);
|
|
10067
|
+
}
|
|
10068
|
+
if (isPresent(this.startIndex) && isPresent(this.endIndex) && this.messages) {
|
|
10069
|
+
const expectedLength = this.endIndex - this.startIndex;
|
|
10070
|
+
if (this.messages.length !== expectedLength) {
|
|
10071
|
+
console.warn(`[kendo-chat] messages.length (${this.messages.length}) does not match the range [startIndex]–[endIndex] (expected ${expectedLength}).`);
|
|
10072
|
+
}
|
|
10073
|
+
}
|
|
10074
|
+
}
|
|
9035
10075
|
mergeWithDefaultActions(actions, defaultActions) {
|
|
9036
10076
|
if (!actions || actions.length === 0) {
|
|
9037
10077
|
return [];
|
|
@@ -9045,7 +10085,7 @@ class ChatComponent {
|
|
|
9045
10085
|
});
|
|
9046
10086
|
}
|
|
9047
10087
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatComponent, deps: [{ token: i1.LocalizationService }, { token: i0.NgZone }, { token: i0.Renderer2 }, { token: i0.ElementRef }, { token: ChatService }], target: i0.ɵɵFactoryTarget.Component });
|
|
9048
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.20", 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", speechToTextButton: "speechToTextButton", fileSelectButton: "fileSelectButton", messageBoxSettings: "messageBoxSettings", messageToolbarActions: "messageToolbarActions", inputValue: "inputValue", authorMessageSettings: "authorMessageSettings", receiverMessageSettings: "receiverMessageSettings", messageContextMenuActions: "messageContextMenuActions", fileActions: "fileActions", messageFilesLayout: "messageFilesLayout", suggestionsLayout: "suggestionsLayout", quickActionsLayout: "quickActionsLayout", suggestions: "suggestions", sendButton: "sendButton", sendButtonSettings: "sendButtonSettings", modelFields: "modelFields", scrollToBottomButton: "scrollToBottomButton", loading: "loading" }, outputs: { sendMessage: "sendMessage", resendMessage: "resendMessage", 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: [
|
|
10088
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.20", 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", speechToTextButton: "speechToTextButton", fileSelectButton: "fileSelectButton", messageBoxSettings: "messageBoxSettings", messageToolbarActions: "messageToolbarActions", inputValue: "inputValue", authorMessageSettings: "authorMessageSettings", receiverMessageSettings: "receiverMessageSettings", messageContextMenuActions: "messageContextMenuActions", fileActions: "fileActions", messageFilesLayout: "messageFilesLayout", suggestionsLayout: "suggestionsLayout", quickActionsLayout: "quickActionsLayout", suggestions: "suggestions", sendButton: "sendButton", sendButtonSettings: "sendButtonSettings", scrollMode: "scrollMode", pageSize: "pageSize", autoScrollThreshold: "autoScrollThreshold", total: "total", startIndex: "startIndex", endIndex: "endIndex", pinnedMessages: "pinnedMessages", repliedToMessages: "repliedToMessages", modelFields: "modelFields", scrollToBottomButton: "scrollToBottomButton", loading: "loading" }, outputs: { sendMessage: "sendMessage", resendMessage: "resendMessage", toolbarActionClick: "toolbarActionClick", contextMenuActionClick: "contextMenuActionClick", fileActionClick: "fileActionClick", download: "download", executeAction: "executeAction", suggestionExecute: "suggestionExecute", fileSelect: "fileSelect", fileRemove: "fileRemove", unpin: "unpin", inputValueChange: "inputValueChange", loadMore: "loadMore", referencedMessageClick: "referencedMessageClick" }, host: { properties: { "class": "this.className", "attr.dir": "this.dirAttr" } }, providers: [
|
|
9049
10089
|
LocalizationService,
|
|
9050
10090
|
ChatService,
|
|
9051
10091
|
SuggestionsScrollService,
|
|
@@ -9053,7 +10093,7 @@ class ChatComponent {
|
|
|
9053
10093
|
provide: L10N_PREFIX,
|
|
9054
10094
|
useValue: 'kendo.chat',
|
|
9055
10095
|
},
|
|
9056
|
-
], 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: "messageBoxStartAffixTemplate", first: true, predicate: ChatMessageBoxStartAffixTemplateDirective, descendants: true }, { propertyName: "messageBoxEndAffixTemplate", first: true, predicate: ChatMessageBoxEndAffixTemplateDirective, descendants: true }, { propertyName: "messageBoxTopAffixTemplate", first: true, predicate: ChatMessageBoxTopAffixTemplateDirective, 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: `
|
|
10096
|
+
], 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: "messageBoxStartAffixTemplate", first: true, predicate: ChatMessageBoxStartAffixTemplateDirective, descendants: true }, { propertyName: "messageBoxEndAffixTemplate", first: true, predicate: ChatMessageBoxEndAffixTemplateDirective, descendants: true }, { propertyName: "messageBoxTopAffixTemplate", first: true, predicate: ChatMessageBoxTopAffixTemplateDirective, 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 }, { propertyName: "anchor", first: true, predicate: ["anchor"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
9057
10097
|
<ng-container
|
|
9058
10098
|
kendoChatLocalizedMessages
|
|
9059
10099
|
i18n-deletedMessageSenderText="
|
|
@@ -9123,9 +10163,7 @@ class ChatComponent {
|
|
|
9123
10163
|
kendo.chat.nextSuggestionsButtonTitle|The title of the button that scrolls to the next suggestions
|
|
9124
10164
|
"
|
|
9125
10165
|
nextSuggestionsButtonTitle="Scroll right"
|
|
9126
|
-
i18n-unpinMessageTitle="
|
|
9127
|
-
kendo.chat.unpinMessageTitle|The title of the button that unpins a pinned message
|
|
9128
|
-
"
|
|
10166
|
+
i18n-unpinMessageTitle="kendo.chat.unpinMessageTitle|The title of the button that unpins a pinned message"
|
|
9129
10167
|
unpinMessageTitle="Unpin message"
|
|
9130
10168
|
>
|
|
9131
10169
|
</ng-container>
|
|
@@ -9144,18 +10182,30 @@ class ChatComponent {
|
|
|
9144
10182
|
[attr.aria-label]="textFor('messageListLabel')"
|
|
9145
10183
|
#anchor="scrollAnchor"
|
|
9146
10184
|
[(autoScroll)]="autoScroll"
|
|
10185
|
+
[autoScrollThreshold]="autoScrollThreshold"
|
|
10186
|
+
[endlessMode]="scrollMode === 'endless'"
|
|
10187
|
+
[rangeIsAtEnd]="endlessState.isAtEnd"
|
|
10188
|
+
(nearTop)="onNearTop()"
|
|
10189
|
+
(nearBottom)="onNearBottom()"
|
|
9147
10190
|
>
|
|
9148
10191
|
@if (pinnedMessage) {
|
|
9149
10192
|
<div
|
|
9150
10193
|
class="k-message-reference k-message-pinned"
|
|
9151
10194
|
[class.k-message-reference-receiver]="!isOwnMessage(pinnedMessage)"
|
|
9152
10195
|
[class.k-message-reference-sender]="isOwnMessage(pinnedMessage)"
|
|
9153
|
-
(click)="scrollToPinnedMessage()"
|
|
10196
|
+
(click)="scrollToPinnedMessage($event)"
|
|
9154
10197
|
>
|
|
9155
10198
|
<kendo-icon-wrapper name="pin" [svgIcon]="pinIcon"> </kendo-icon-wrapper>
|
|
9156
10199
|
<chat-message-reference-content [message]="pinnedMessage"></chat-message-reference-content>
|
|
9157
10200
|
<span class="k-spacer"></span>
|
|
9158
|
-
<button
|
|
10201
|
+
<button
|
|
10202
|
+
kendoButton
|
|
10203
|
+
icon="x"
|
|
10204
|
+
[svgIcon]="deleteIcon"
|
|
10205
|
+
[attr.title]="textFor('unpinMessageTitle')"
|
|
10206
|
+
(click)="unpin.emit(pinnedMessage)"
|
|
10207
|
+
fillMode="flat"
|
|
10208
|
+
></button>
|
|
9159
10209
|
</div>
|
|
9160
10210
|
} @if (processedMessages && processedMessages.length === 0) {
|
|
9161
10211
|
<div class="k-message-list-content k-message-list-content-empty">
|
|
@@ -9163,7 +10213,7 @@ class ChatComponent {
|
|
|
9163
10213
|
</div>
|
|
9164
10214
|
} @else {
|
|
9165
10215
|
<kendo-chat-message-list
|
|
9166
|
-
[messages]="
|
|
10216
|
+
[messages]="renderedMessages"
|
|
9167
10217
|
[authorMessageContentTemplate]="authorMessageContentTemplate"
|
|
9168
10218
|
[receiverMessageContentTemplate]="receiverMessageContentTemplate"
|
|
9169
10219
|
[messageContentTemplate]="messageContentTemplate"
|
|
@@ -9177,7 +10227,7 @@ class ChatComponent {
|
|
|
9177
10227
|
[attachmentTemplate]="attachmentTemplate"
|
|
9178
10228
|
[authorId]="authorId"
|
|
9179
10229
|
(executeAction)="dispatchAction($event)"
|
|
9180
|
-
(resize)="
|
|
10230
|
+
(resize)="onMessageListResize()"
|
|
9181
10231
|
(navigate)="this.autoScroll = false"
|
|
9182
10232
|
>
|
|
9183
10233
|
</kendo-chat-message-list>
|
|
@@ -9190,7 +10240,7 @@ class ChatComponent {
|
|
|
9190
10240
|
[align]="{ horizontal: 'center' }"
|
|
9191
10241
|
icon="arrow-down-outline"
|
|
9192
10242
|
[svgIcon]="scrollToBottomIcon"
|
|
9193
|
-
(click)="
|
|
10243
|
+
(click)="onScrollToBottomClick()"
|
|
9194
10244
|
></kendo-floatingactionbutton>
|
|
9195
10245
|
</div>
|
|
9196
10246
|
}
|
|
@@ -9227,7 +10277,11 @@ class ChatComponent {
|
|
|
9227
10277
|
(popupClose)="handleMenuClose($event)"
|
|
9228
10278
|
(select)="onContextMenuAction($event.item.originalAction)"
|
|
9229
10279
|
></kendo-contextmenu>
|
|
9230
|
-
|
|
10280
|
+
|
|
10281
|
+
@if (showLicenseWatermark) {
|
|
10282
|
+
<div kendoWatermarkOverlay [licenseMessage]="licenseMessage"></div>
|
|
10283
|
+
}
|
|
10284
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: LocalizedMessagesDirective$2, selector: "[kendoChatLocalizedMessages]" }, { kind: "directive", type: ScrollAnchorDirective, selector: "[kendoChatScrollAnchor]", inputs: ["autoScroll", "autoScrollThreshold", "endlessMode", "rangeIsAtEnd"], outputs: ["autoScrollChange", "nearTop", "nearBottom"], 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", "messageBoxStartAffixTemplate", "messageBoxEndAffixTemplate", "messageBoxTopAffixTemplate", "suggestionTemplate", "loading"], outputs: ["sendMessage", "executeSuggestion", "fileSelect", "fileRemove"] }, { kind: "component", type: MessageReferenceComponent, selector: "chat-message-reference-content", inputs: ["message"] }, { 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: ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "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"] }, { kind: "component", type: FloatingActionButtonComponent, selector: "kendo-floatingactionbutton", inputs: ["themeColor", "size", "rounded", "disabled", "align", "offset", "positionMode", "icon", "svgIcon", "iconClass", "buttonClass", "dialClass", "text", "dialItemAnimation", "tabIndex", "dialItems"], outputs: ["blur", "focus", "dialItemClick", "open", "close"] }, { kind: "component", type: WatermarkOverlayComponent, selector: "div[kendoWatermarkOverlay], kendo-watermark-overlay", inputs: ["licenseMessage"] }] });
|
|
9231
10285
|
}
|
|
9232
10286
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatComponent, decorators: [{
|
|
9233
10287
|
type: Component,
|
|
@@ -9312,9 +10366,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9312
10366
|
kendo.chat.nextSuggestionsButtonTitle|The title of the button that scrolls to the next suggestions
|
|
9313
10367
|
"
|
|
9314
10368
|
nextSuggestionsButtonTitle="Scroll right"
|
|
9315
|
-
i18n-unpinMessageTitle="
|
|
9316
|
-
kendo.chat.unpinMessageTitle|The title of the button that unpins a pinned message
|
|
9317
|
-
"
|
|
10369
|
+
i18n-unpinMessageTitle="kendo.chat.unpinMessageTitle|The title of the button that unpins a pinned message"
|
|
9318
10370
|
unpinMessageTitle="Unpin message"
|
|
9319
10371
|
>
|
|
9320
10372
|
</ng-container>
|
|
@@ -9333,18 +10385,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9333
10385
|
[attr.aria-label]="textFor('messageListLabel')"
|
|
9334
10386
|
#anchor="scrollAnchor"
|
|
9335
10387
|
[(autoScroll)]="autoScroll"
|
|
10388
|
+
[autoScrollThreshold]="autoScrollThreshold"
|
|
10389
|
+
[endlessMode]="scrollMode === 'endless'"
|
|
10390
|
+
[rangeIsAtEnd]="endlessState.isAtEnd"
|
|
10391
|
+
(nearTop)="onNearTop()"
|
|
10392
|
+
(nearBottom)="onNearBottom()"
|
|
9336
10393
|
>
|
|
9337
10394
|
@if (pinnedMessage) {
|
|
9338
10395
|
<div
|
|
9339
10396
|
class="k-message-reference k-message-pinned"
|
|
9340
10397
|
[class.k-message-reference-receiver]="!isOwnMessage(pinnedMessage)"
|
|
9341
10398
|
[class.k-message-reference-sender]="isOwnMessage(pinnedMessage)"
|
|
9342
|
-
(click)="scrollToPinnedMessage()"
|
|
10399
|
+
(click)="scrollToPinnedMessage($event)"
|
|
9343
10400
|
>
|
|
9344
10401
|
<kendo-icon-wrapper name="pin" [svgIcon]="pinIcon"> </kendo-icon-wrapper>
|
|
9345
10402
|
<chat-message-reference-content [message]="pinnedMessage"></chat-message-reference-content>
|
|
9346
10403
|
<span class="k-spacer"></span>
|
|
9347
|
-
<button
|
|
10404
|
+
<button
|
|
10405
|
+
kendoButton
|
|
10406
|
+
icon="x"
|
|
10407
|
+
[svgIcon]="deleteIcon"
|
|
10408
|
+
[attr.title]="textFor('unpinMessageTitle')"
|
|
10409
|
+
(click)="unpin.emit(pinnedMessage)"
|
|
10410
|
+
fillMode="flat"
|
|
10411
|
+
></button>
|
|
9348
10412
|
</div>
|
|
9349
10413
|
} @if (processedMessages && processedMessages.length === 0) {
|
|
9350
10414
|
<div class="k-message-list-content k-message-list-content-empty">
|
|
@@ -9352,7 +10416,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9352
10416
|
</div>
|
|
9353
10417
|
} @else {
|
|
9354
10418
|
<kendo-chat-message-list
|
|
9355
|
-
[messages]="
|
|
10419
|
+
[messages]="renderedMessages"
|
|
9356
10420
|
[authorMessageContentTemplate]="authorMessageContentTemplate"
|
|
9357
10421
|
[receiverMessageContentTemplate]="receiverMessageContentTemplate"
|
|
9358
10422
|
[messageContentTemplate]="messageContentTemplate"
|
|
@@ -9366,7 +10430,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9366
10430
|
[attachmentTemplate]="attachmentTemplate"
|
|
9367
10431
|
[authorId]="authorId"
|
|
9368
10432
|
(executeAction)="dispatchAction($event)"
|
|
9369
|
-
(resize)="
|
|
10433
|
+
(resize)="onMessageListResize()"
|
|
9370
10434
|
(navigate)="this.autoScroll = false"
|
|
9371
10435
|
>
|
|
9372
10436
|
</kendo-chat-message-list>
|
|
@@ -9379,7 +10443,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9379
10443
|
[align]="{ horizontal: 'center' }"
|
|
9380
10444
|
icon="arrow-down-outline"
|
|
9381
10445
|
[svgIcon]="scrollToBottomIcon"
|
|
9382
|
-
(click)="
|
|
10446
|
+
(click)="onScrollToBottomClick()"
|
|
9383
10447
|
></kendo-floatingactionbutton>
|
|
9384
10448
|
</div>
|
|
9385
10449
|
}
|
|
@@ -9416,6 +10480,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9416
10480
|
(popupClose)="handleMenuClose($event)"
|
|
9417
10481
|
(select)="onContextMenuAction($event.item.originalAction)"
|
|
9418
10482
|
></kendo-contextmenu>
|
|
10483
|
+
|
|
10484
|
+
@if (showLicenseWatermark) {
|
|
10485
|
+
<div kendoWatermarkOverlay [licenseMessage]="licenseMessage"></div>
|
|
10486
|
+
}
|
|
9419
10487
|
`,
|
|
9420
10488
|
standalone: true,
|
|
9421
10489
|
imports: [
|
|
@@ -9429,6 +10497,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9429
10497
|
ButtonComponent,
|
|
9430
10498
|
ContextMenuComponent,
|
|
9431
10499
|
FloatingActionButtonComponent,
|
|
10500
|
+
WatermarkOverlayComponent,
|
|
9432
10501
|
],
|
|
9433
10502
|
}]
|
|
9434
10503
|
}], ctorParameters: () => [{ type: i1.LocalizationService }, { type: i0.NgZone }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: ChatService }], propDecorators: { messages: [{
|
|
@@ -9483,6 +10552,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9483
10552
|
type: Input
|
|
9484
10553
|
}], sendButtonSettings: [{
|
|
9485
10554
|
type: Input
|
|
10555
|
+
}], scrollMode: [{
|
|
10556
|
+
type: Input
|
|
10557
|
+
}], pageSize: [{
|
|
10558
|
+
type: Input
|
|
10559
|
+
}], autoScrollThreshold: [{
|
|
10560
|
+
type: Input
|
|
10561
|
+
}], total: [{
|
|
10562
|
+
type: Input
|
|
10563
|
+
}], startIndex: [{
|
|
10564
|
+
type: Input
|
|
10565
|
+
}], endIndex: [{
|
|
10566
|
+
type: Input
|
|
10567
|
+
}], pinnedMessages: [{
|
|
10568
|
+
type: Input
|
|
10569
|
+
}], repliedToMessages: [{
|
|
10570
|
+
type: Input
|
|
9486
10571
|
}], modelFields: [{
|
|
9487
10572
|
type: Input
|
|
9488
10573
|
}], scrollToBottomButton: [{
|
|
@@ -9513,6 +10598,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9513
10598
|
type: Output
|
|
9514
10599
|
}], inputValueChange: [{
|
|
9515
10600
|
type: Output
|
|
10601
|
+
}], loadMore: [{
|
|
10602
|
+
type: Output
|
|
10603
|
+
}], referencedMessageClick: [{
|
|
10604
|
+
type: Output
|
|
9516
10605
|
}], className: [{
|
|
9517
10606
|
type: HostBinding,
|
|
9518
10607
|
args: ['class']
|
|
@@ -9579,6 +10668,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9579
10668
|
}], messageList: [{
|
|
9580
10669
|
type: ViewChild,
|
|
9581
10670
|
args: ['messageList', { static: true, read: ViewContainerRef }]
|
|
10671
|
+
}], anchor: [{
|
|
10672
|
+
type: ViewChild,
|
|
10673
|
+
args: ['anchor']
|
|
9582
10674
|
}] } });
|
|
9583
10675
|
|
|
9584
10676
|
// eslint-disable no-forward-ref
|