@progress/kendo-angular-conversational-ui 24.0.0-develop.2 → 24.0.0-develop.20
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/NOTICE.txt +2599 -172
- 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 +142 -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 +1168 -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: 1778149452,
|
|
219
|
+
version: '24.0.0-develop.20',
|
|
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: this._endlessMode ? 'auto' : '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,62 @@ 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 from the top of the visible message area that prevents auto-scrolling when a new receiver message arrives.
|
|
9194
|
+
* Accepts a percentage string (for example, `'30%'`) or a pixel value as a number. Set to `0` to always auto-scroll to new messages.
|
|
9195
|
+
*
|
|
9196
|
+
* Has no effect on author messages, which always scroll to the bottom.
|
|
9197
|
+
*
|
|
9198
|
+
* For more details, refer to the [Auto-Scroll Threshold](slug:scroll_modes_chat#auto-scroll-threshold) article.
|
|
9199
|
+
*
|
|
9200
|
+
* @default '20%'
|
|
9201
|
+
*/
|
|
9202
|
+
autoScrollThreshold = '20%';
|
|
9203
|
+
/**
|
|
9204
|
+
* Sets the total number of messages in the conversation when using endless scrolling with remote data source. For more details, refer to the [Endless Scrolling with Remote Data](slug:scroll_modes_chat#remote-data) section in the documentation.
|
|
9205
|
+
*/
|
|
9206
|
+
total;
|
|
9207
|
+
/**
|
|
9208
|
+
* Sets the index of the first message in the currently loaded batch within the full conversation.
|
|
9209
|
+
* Used with remote data sources in endless scroll mode to compute the range for the next [`loadMore`](slug:api_conversational-ui_chatcomponent#loadMore) call.
|
|
9210
|
+
* For more details, refer to the [Endless Scrolling with Remote Data](slug:scroll_modes_chat#remote-data) section in the documentation.
|
|
9211
|
+
*/
|
|
9212
|
+
startIndex;
|
|
9213
|
+
/**
|
|
9214
|
+
* Sets the exclusive end index of the currently loaded batch within the full conversation.
|
|
9215
|
+
* Used with remote data sources in endless scroll mode to determine whether more messages exist beyond the current batch.
|
|
9216
|
+
* For more details, refer to the [Endless Scrolling with Remote Data](slug:scroll_modes_chat#remote-data) section in the documentation.
|
|
9217
|
+
*/
|
|
9218
|
+
endIndex;
|
|
9219
|
+
/**
|
|
9220
|
+
* Sets the full set of pinned messages in the conversation.
|
|
9221
|
+
* Used with remote data sources in endless scroll mode to render the pinned message indicator when the pinned message is outside the currently loaded batch.
|
|
9222
|
+
* For more details, refer to the [Endless Scrolling with Remote Data](slug:scroll_modes_chat#remote-data) section in the documentation.
|
|
9223
|
+
*
|
|
9224
|
+
* @default []
|
|
9225
|
+
*/
|
|
9226
|
+
pinnedMessages = [];
|
|
9227
|
+
/**
|
|
9228
|
+
* Sets the messages that serve as reply targets for messages in the currently loaded batch but exist outside it.
|
|
9229
|
+
* Used with remote data sources in endless scroll mode to render reply previews when the replied-to message is not in the current batch.
|
|
9230
|
+
* For more details, refer to the [Endless Scrolling with Remote Data](slug:scroll_modes_chat#remote-data) section in the documentation.
|
|
9231
|
+
*
|
|
9232
|
+
* @default []
|
|
9233
|
+
*/
|
|
9234
|
+
repliedToMessages = [];
|
|
8669
9235
|
/**
|
|
8670
9236
|
* Sets the names of the model fields from which the Chat reads its data.
|
|
8671
9237
|
* Lets you map custom data types to the expected `Message` format.
|
|
@@ -8743,6 +9309,14 @@ class ChatComponent {
|
|
|
8743
9309
|
* Fires when the user types in the message input box.
|
|
8744
9310
|
*/
|
|
8745
9311
|
inputValueChange = new EventEmitter();
|
|
9312
|
+
/**
|
|
9313
|
+
* Fires when the user scrolls near the edge of the rendered message window in endless scroll mode.
|
|
9314
|
+
*/
|
|
9315
|
+
loadMore = new EventEmitter();
|
|
9316
|
+
/**
|
|
9317
|
+
* Fires when the user clicks a referenced message (pinned indicator or reply preview).
|
|
9318
|
+
*/
|
|
9319
|
+
referencedMessageClick = new EventEmitter();
|
|
8746
9320
|
get className() {
|
|
8747
9321
|
return 'k-chat';
|
|
8748
9322
|
}
|
|
@@ -8793,6 +9367,43 @@ class ChatComponent {
|
|
|
8793
9367
|
}
|
|
8794
9368
|
return this.messages;
|
|
8795
9369
|
}
|
|
9370
|
+
/**
|
|
9371
|
+
* @hidden
|
|
9372
|
+
* Returns `true` when the Chat is in remote endless scroll mode.
|
|
9373
|
+
* Remote mode is active whenever the consumer provides a `total`.
|
|
9374
|
+
*/
|
|
9375
|
+
get isRemoteMode() {
|
|
9376
|
+
return this.scrollMode === 'endless'
|
|
9377
|
+
&& isPresent(this.total);
|
|
9378
|
+
}
|
|
9379
|
+
/**
|
|
9380
|
+
* @hidden
|
|
9381
|
+
* Returns the messages to render — sliced in endless mode, full in scrollable mode.
|
|
9382
|
+
* In remote mode, returns processedMessages as-is (consumer already provides only the current batch).
|
|
9383
|
+
*/
|
|
9384
|
+
get renderedMessages() {
|
|
9385
|
+
const all = this.processedMessages;
|
|
9386
|
+
if (!all || all.length === 0) {
|
|
9387
|
+
return [];
|
|
9388
|
+
}
|
|
9389
|
+
if (this.scrollMode === 'endless') {
|
|
9390
|
+
if (this.isRemoteMode) {
|
|
9391
|
+
return all;
|
|
9392
|
+
}
|
|
9393
|
+
const start = this.endlessState.startIndex;
|
|
9394
|
+
const end = this.endlessState.endIndex;
|
|
9395
|
+
if (all !== this._renderedMessagesSource ||
|
|
9396
|
+
start !== this._renderedMessagesStart ||
|
|
9397
|
+
end !== this._renderedMessagesEnd) {
|
|
9398
|
+
this._cachedRenderedMessages = all.slice(start, end);
|
|
9399
|
+
this._renderedMessagesSource = all;
|
|
9400
|
+
this._renderedMessagesStart = start;
|
|
9401
|
+
this._renderedMessagesEnd = end;
|
|
9402
|
+
}
|
|
9403
|
+
return this._cachedRenderedMessages;
|
|
9404
|
+
}
|
|
9405
|
+
return all;
|
|
9406
|
+
}
|
|
8796
9407
|
/**
|
|
8797
9408
|
* @hidden
|
|
8798
9409
|
*/
|
|
@@ -8838,6 +9449,19 @@ class ChatComponent {
|
|
|
8838
9449
|
* @hidden
|
|
8839
9450
|
*/
|
|
8840
9451
|
scrollToBottomIcon = arrowDownOutlineIcon;
|
|
9452
|
+
/**
|
|
9453
|
+
* @hidden
|
|
9454
|
+
*/
|
|
9455
|
+
endlessState = new EndlessScrollState();
|
|
9456
|
+
/**
|
|
9457
|
+
* @hidden
|
|
9458
|
+
*/
|
|
9459
|
+
showLicenseWatermark = false;
|
|
9460
|
+
/**
|
|
9461
|
+
* @hidden
|
|
9462
|
+
*/
|
|
9463
|
+
licenseMessage;
|
|
9464
|
+
anchor;
|
|
8841
9465
|
direction;
|
|
8842
9466
|
subs = new Subscription();
|
|
8843
9467
|
_modelFields = defaultModelFields;
|
|
@@ -8848,13 +9472,27 @@ class ChatComponent {
|
|
|
8848
9472
|
_lastModelFields = null;
|
|
8849
9473
|
_cachedContextMenuActions = [];
|
|
8850
9474
|
_lastContextMenuActionsReference = null;
|
|
9475
|
+
_previousMessagesLength = 0;
|
|
9476
|
+
_previousLastMessageId = null;
|
|
9477
|
+
_hasProcessedMessages = false;
|
|
9478
|
+
_pendingScrollAction = null;
|
|
9479
|
+
_scrollHandledBeforePaint = false;
|
|
9480
|
+
_lastNewMessageId = null;
|
|
9481
|
+
_cachedRenderedMessages = [];
|
|
9482
|
+
_renderedMessagesSource = null;
|
|
9483
|
+
_renderedMessagesStart = -1;
|
|
9484
|
+
_renderedMessagesEnd = -1;
|
|
9485
|
+
_pendingRemoteScrollToMessageId = null;
|
|
9486
|
+
_remoteScrollToBottom = false;
|
|
8851
9487
|
constructor(localization, zone, renderer, element, chatService) {
|
|
8852
9488
|
this.localization = localization;
|
|
8853
9489
|
this.zone = zone;
|
|
8854
9490
|
this.renderer = renderer;
|
|
8855
9491
|
this.element = element;
|
|
8856
9492
|
this.chatService = chatService;
|
|
8857
|
-
validatePackage(packageMetadata);
|
|
9493
|
+
const isValid = validatePackage(packageMetadata);
|
|
9494
|
+
this.licenseMessage = getLicenseMessage(packageMetadata);
|
|
9495
|
+
this.showLicenseWatermark = shouldShowValidationUI(isValid);
|
|
8858
9496
|
this.direction = localization.rtl ? 'rtl' : 'ltr';
|
|
8859
9497
|
this.subs.add(localization.changes.subscribe(({ rtl }) => {
|
|
8860
9498
|
this.direction = rtl ? 'rtl' : 'ltr';
|
|
@@ -8900,17 +9538,53 @@ class ChatComponent {
|
|
|
8900
9538
|
}));
|
|
8901
9539
|
this.pinnedMessage = this.findLastPinnedMessage();
|
|
8902
9540
|
this.chatService.authorId = this.authorId;
|
|
9541
|
+
this.chatService.repliedToMessages = this.repliedToMessages || [];
|
|
9542
|
+
this.initEndlessScroll();
|
|
9543
|
+
this.subs.add(this.chatService.replyReferenceClick$.subscribe((messageId) => {
|
|
9544
|
+
this.handleReferencedMessageClick(messageId, 'reply');
|
|
9545
|
+
}));
|
|
8903
9546
|
}
|
|
8904
9547
|
/**
|
|
8905
9548
|
* @hidden
|
|
8906
9549
|
*/
|
|
8907
9550
|
ngOnChanges(changes) {
|
|
8908
|
-
this.zone.runOutsideAngular(() => setTimeout(() => {
|
|
8909
|
-
this.messageList.element.nativeElement.style.flex = '1 1 auto';
|
|
8910
|
-
}));
|
|
8911
9551
|
if (isChanged('messages', changes, false)) {
|
|
8912
9552
|
this.pinnedMessage = this.findLastPinnedMessage();
|
|
8913
9553
|
this.chatService.messages = this.processedMessages;
|
|
9554
|
+
if (this.isRemoteMode) {
|
|
9555
|
+
const skipDetectAndSchedule = this.endlessState.isLoading || !!this._pendingRemoteScrollToMessageId;
|
|
9556
|
+
if (!skipDetectAndSchedule) {
|
|
9557
|
+
this.detectNewMessageScrollAction();
|
|
9558
|
+
}
|
|
9559
|
+
this.handleRemoteMessagesChange();
|
|
9560
|
+
if (!skipDetectAndSchedule) {
|
|
9561
|
+
this.scheduleNewMessageScroll();
|
|
9562
|
+
}
|
|
9563
|
+
}
|
|
9564
|
+
else {
|
|
9565
|
+
this.detectNewMessageScrollAction();
|
|
9566
|
+
this.handleMessagesChange();
|
|
9567
|
+
this.scheduleNewMessageScroll();
|
|
9568
|
+
}
|
|
9569
|
+
this.handlePendingRemoteScroll();
|
|
9570
|
+
}
|
|
9571
|
+
if (isChanged('pinnedMessages', changes, false)) {
|
|
9572
|
+
this.pinnedMessage = this.findLastPinnedMessage();
|
|
9573
|
+
}
|
|
9574
|
+
if (isChanged('repliedToMessages', changes, false)) {
|
|
9575
|
+
this.chatService.repliedToMessages = this.repliedToMessages || [];
|
|
9576
|
+
}
|
|
9577
|
+
this.validateRemoteInputs();
|
|
9578
|
+
if (isChanged('scrollMode', changes, false)) {
|
|
9579
|
+
this.initEndlessScroll();
|
|
9580
|
+
}
|
|
9581
|
+
if (isChanged('pageSize', changes, false) && this.scrollMode === 'endless') {
|
|
9582
|
+
this.endlessState.init(this.processedMessages?.length || 0, this.pageSize);
|
|
9583
|
+
}
|
|
9584
|
+
if (this.isRemoteMode && (isChanged('startIndex', changes, false) ||
|
|
9585
|
+
isChanged('endIndex', changes, false) ||
|
|
9586
|
+
isChanged('total', changes, false))) {
|
|
9587
|
+
this.endlessState.syncFromInputs(this.startIndex, this.endIndex, this.total);
|
|
8914
9588
|
}
|
|
8915
9589
|
if (isChanged('height', changes, false)) {
|
|
8916
9590
|
this.renderer.setStyle(this.element.nativeElement, 'height', `${processCssValue(this.height)}`);
|
|
@@ -8980,9 +9654,130 @@ class ChatComponent {
|
|
|
8980
9654
|
/**
|
|
8981
9655
|
* @hidden
|
|
8982
9656
|
*/
|
|
8983
|
-
scrollToPinnedMessage() {
|
|
9657
|
+
scrollToPinnedMessage(event) {
|
|
9658
|
+
if (event && event.target?.closest('button')) {
|
|
9659
|
+
return;
|
|
9660
|
+
}
|
|
8984
9661
|
if (this.pinnedMessage) {
|
|
8985
|
-
this.
|
|
9662
|
+
this.handleReferencedMessageClick(this.pinnedMessage.id, 'pinned');
|
|
9663
|
+
}
|
|
9664
|
+
}
|
|
9665
|
+
/**
|
|
9666
|
+
* @hidden
|
|
9667
|
+
*/
|
|
9668
|
+
onNearTop() {
|
|
9669
|
+
if (this.scrollMode !== 'endless' || this.endlessState.isLoading) {
|
|
9670
|
+
return;
|
|
9671
|
+
}
|
|
9672
|
+
if (this.isRemoteMode) {
|
|
9673
|
+
if (this.startIndex <= 0) {
|
|
9674
|
+
return;
|
|
9675
|
+
}
|
|
9676
|
+
this.endlessState.isLoading = true;
|
|
9677
|
+
this.anchor?.recordScrollHeight();
|
|
9678
|
+
this.anchor?.setAriaLive('off');
|
|
9679
|
+
const reqStart = Math.max(0, this.startIndex - this.pageSize);
|
|
9680
|
+
this.loadMore.emit({ startIndex: reqStart, endIndex: this.endIndex });
|
|
9681
|
+
return;
|
|
9682
|
+
}
|
|
9683
|
+
const range = this.endlessState.extendUp();
|
|
9684
|
+
if (range) {
|
|
9685
|
+
this.endlessState.isLoading = true;
|
|
9686
|
+
this.anchor?.recordScrollHeight();
|
|
9687
|
+
this.anchor?.setAriaLive('off');
|
|
9688
|
+
this.loadMore.emit({ startIndex: range.start, endIndex: range.end });
|
|
9689
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
9690
|
+
this.anchor?.preserveScrollPosition();
|
|
9691
|
+
this.anchor?.setAriaLive('polite');
|
|
9692
|
+
this.endlessState.isLoading = false;
|
|
9693
|
+
}));
|
|
9694
|
+
}
|
|
9695
|
+
}
|
|
9696
|
+
/**
|
|
9697
|
+
* @hidden
|
|
9698
|
+
*/
|
|
9699
|
+
onNearBottom() {
|
|
9700
|
+
if (this.scrollMode !== 'endless' || this.endlessState.isLoading) {
|
|
9701
|
+
return;
|
|
9702
|
+
}
|
|
9703
|
+
if (this.isRemoteMode) {
|
|
9704
|
+
if (this.endIndex >= this.total) {
|
|
9705
|
+
return;
|
|
9706
|
+
}
|
|
9707
|
+
this.endlessState.isLoading = true;
|
|
9708
|
+
this.anchor?.setAriaLive('off');
|
|
9709
|
+
const reqEnd = Math.min(this.total, this.endIndex + this.pageSize);
|
|
9710
|
+
this.loadMore.emit({ startIndex: this.startIndex, endIndex: reqEnd });
|
|
9711
|
+
return;
|
|
9712
|
+
}
|
|
9713
|
+
const range = this.endlessState.extendDown();
|
|
9714
|
+
if (range) {
|
|
9715
|
+
this.endlessState.isLoading = true;
|
|
9716
|
+
this.loadMore.emit({ startIndex: range.start, endIndex: range.end });
|
|
9717
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
9718
|
+
this.endlessState.isLoading = false;
|
|
9719
|
+
}));
|
|
9720
|
+
}
|
|
9721
|
+
}
|
|
9722
|
+
/**
|
|
9723
|
+
* @hidden
|
|
9724
|
+
*/
|
|
9725
|
+
onScrollToBottomClick() {
|
|
9726
|
+
if (this.scrollMode === 'endless' && !this.endlessState.isAtEnd) {
|
|
9727
|
+
if (this.isRemoteMode) {
|
|
9728
|
+
this.endlessState.isLoading = true;
|
|
9729
|
+
this._remoteScrollToBottom = true;
|
|
9730
|
+
this.autoScroll = true;
|
|
9731
|
+
const reqStart = Math.max(0, this.total - this.pageSize);
|
|
9732
|
+
this.loadMore.emit({ startIndex: reqStart, endIndex: this.total });
|
|
9733
|
+
return;
|
|
9734
|
+
}
|
|
9735
|
+
this.anchor?.lockForMessageScroll();
|
|
9736
|
+
const range = this.endlessState.jumpToEnd();
|
|
9737
|
+
this.loadMore.emit({ startIndex: range.start, endIndex: range.end });
|
|
9738
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
9739
|
+
this.anchor?.scrollToBottomWithSettle(() => {
|
|
9740
|
+
this.anchor?.unlockForMessageScroll();
|
|
9741
|
+
});
|
|
9742
|
+
}));
|
|
9743
|
+
}
|
|
9744
|
+
else {
|
|
9745
|
+
this.anchor?.scrollToBottom();
|
|
9746
|
+
}
|
|
9747
|
+
}
|
|
9748
|
+
/**
|
|
9749
|
+
* @hidden
|
|
9750
|
+
*/
|
|
9751
|
+
handleReferencedMessageClick(messageId, type) {
|
|
9752
|
+
this.referencedMessageClick.emit({ id: messageId, type });
|
|
9753
|
+
this.autoScroll = false;
|
|
9754
|
+
if (this.scrollMode === 'endless') {
|
|
9755
|
+
if (this.isRemoteMode) {
|
|
9756
|
+
this.handleRemoteReferencedMessageClick(messageId);
|
|
9757
|
+
return;
|
|
9758
|
+
}
|
|
9759
|
+
const allMessages = this.processedMessages;
|
|
9760
|
+
const targetIndex = allMessages.findIndex(m => m.id === messageId);
|
|
9761
|
+
if (targetIndex === -1) {
|
|
9762
|
+
return;
|
|
9763
|
+
}
|
|
9764
|
+
if (this.endlessState.contains(targetIndex)) {
|
|
9765
|
+
this.anchor?.lockForMessageScroll();
|
|
9766
|
+
this.chatService.scrollToMessage(messageId, 'auto');
|
|
9767
|
+
this.anchor?.unlockForMessageScroll();
|
|
9768
|
+
}
|
|
9769
|
+
else {
|
|
9770
|
+
this.anchor?.lockForMessageScroll();
|
|
9771
|
+
const range = this.endlessState.jumpTo(targetIndex);
|
|
9772
|
+
this.loadMore.emit({ startIndex: range.start, endIndex: range.end });
|
|
9773
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
9774
|
+
this.chatService.scrollToMessage(messageId, 'auto');
|
|
9775
|
+
this.anchor?.unlockForMessageScroll();
|
|
9776
|
+
}));
|
|
9777
|
+
}
|
|
9778
|
+
}
|
|
9779
|
+
else {
|
|
9780
|
+
this.chatService.scrollToMessage(messageId);
|
|
8986
9781
|
}
|
|
8987
9782
|
}
|
|
8988
9783
|
/**
|
|
@@ -9022,8 +9817,218 @@ class ChatComponent {
|
|
|
9022
9817
|
this.chatService.selectOnMenuClose = false;
|
|
9023
9818
|
}
|
|
9024
9819
|
}
|
|
9820
|
+
/**
|
|
9821
|
+
* @hidden
|
|
9822
|
+
*/
|
|
9823
|
+
onMessageListResize() {
|
|
9824
|
+
this.anchor?.calculateMessageBoxSeparator();
|
|
9825
|
+
const layoutChange = this.chatService.layoutChangeInProgress;
|
|
9826
|
+
const skipAutoScroll = !!this._pendingScrollAction || this._scrollHandledBeforePaint || layoutChange;
|
|
9827
|
+
if (skipAutoScroll) {
|
|
9828
|
+
this._scrollHandledBeforePaint = false;
|
|
9829
|
+
if (layoutChange) {
|
|
9830
|
+
this.chatService.layoutChangeInProgress = false;
|
|
9831
|
+
this.anchor?.onScroll();
|
|
9832
|
+
}
|
|
9833
|
+
return;
|
|
9834
|
+
}
|
|
9835
|
+
this.anchor?.autoScrollToBottom();
|
|
9836
|
+
}
|
|
9837
|
+
/**
|
|
9838
|
+
* Schedules the new-message scroll adjustment on zone.onStable so it runs
|
|
9839
|
+
* after Angular renders the new DOM but before the browser paints,
|
|
9840
|
+
* preventing a visible intermediate frame.
|
|
9841
|
+
*/
|
|
9842
|
+
scheduleNewMessageScroll() {
|
|
9843
|
+
if (!this._pendingScrollAction) {
|
|
9844
|
+
return;
|
|
9845
|
+
}
|
|
9846
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
9847
|
+
this.zone.run(() => {
|
|
9848
|
+
const action = this._pendingScrollAction;
|
|
9849
|
+
this._pendingScrollAction = null;
|
|
9850
|
+
this._scrollHandledBeforePaint = true;
|
|
9851
|
+
if (action === 'author') {
|
|
9852
|
+
this._lastNewMessageId = null;
|
|
9853
|
+
this.autoScroll = true;
|
|
9854
|
+
this.anchor?.scrollToBottomWithSettle();
|
|
9855
|
+
return;
|
|
9856
|
+
}
|
|
9857
|
+
if (action === 'receiver') {
|
|
9858
|
+
const messageEl = this.getLastNewMessageElement();
|
|
9859
|
+
this._lastNewMessageId = null;
|
|
9860
|
+
this.autoScroll = true;
|
|
9861
|
+
if (messageEl) {
|
|
9862
|
+
this.anchor?.scrollWithThreshold(messageEl);
|
|
9863
|
+
}
|
|
9864
|
+
else {
|
|
9865
|
+
this.anchor?.scrollToBottom();
|
|
9866
|
+
}
|
|
9867
|
+
}
|
|
9868
|
+
});
|
|
9869
|
+
}));
|
|
9870
|
+
}
|
|
9871
|
+
detectNewMessageScrollAction() {
|
|
9872
|
+
const messages = this.processedMessages;
|
|
9873
|
+
const prevLength = this._previousMessagesLength;
|
|
9874
|
+
const hasNoMessages = !messages;
|
|
9875
|
+
const hasNoNewMessages = messages && messages.length <= prevLength;
|
|
9876
|
+
const isInitialLoad = prevLength === 0 && !this._hasProcessedMessages;
|
|
9877
|
+
const lastMessage = messages?.length > 0 ? messages[messages.length - 1] : null;
|
|
9878
|
+
const currentLastId = lastMessage?.id ?? null;
|
|
9879
|
+
const isLastMessageReplaced = messages?.length === prevLength
|
|
9880
|
+
&& currentLastId !== this._previousLastMessageId
|
|
9881
|
+
&& this._previousLastMessageId !== null && currentLastId !== null;
|
|
9882
|
+
if (hasNoMessages || (hasNoNewMessages && !isLastMessageReplaced) || isInitialLoad) {
|
|
9883
|
+
this._pendingScrollAction = null;
|
|
9884
|
+
this._lastNewMessageId = null;
|
|
9885
|
+
return;
|
|
9886
|
+
}
|
|
9887
|
+
this._lastNewMessageId = currentLastId;
|
|
9888
|
+
const isAuthor = this.isOwnMessage(lastMessage);
|
|
9889
|
+
if (isAuthor) {
|
|
9890
|
+
this._pendingScrollAction = 'author';
|
|
9891
|
+
return;
|
|
9892
|
+
}
|
|
9893
|
+
if (!this.anchor) {
|
|
9894
|
+
this._pendingScrollAction = null;
|
|
9895
|
+
return;
|
|
9896
|
+
}
|
|
9897
|
+
const distance = this.anchor.getDistanceFromBottom();
|
|
9898
|
+
const threshold = this.anchor.getAutoScrollThresholdPx();
|
|
9899
|
+
const isNearBottom = distance <= Math.max(threshold, scrollButtonThreshold);
|
|
9900
|
+
this._pendingScrollAction = isNearBottom ? 'receiver' : null;
|
|
9901
|
+
}
|
|
9902
|
+
getLastNewMessageElement() {
|
|
9903
|
+
if (this._lastNewMessageId == null) {
|
|
9904
|
+
return null;
|
|
9905
|
+
}
|
|
9906
|
+
const ref = this.chatService.messageElementsMap.get(this._lastNewMessageId);
|
|
9907
|
+
return ref?.nativeElement ?? null;
|
|
9908
|
+
}
|
|
9025
9909
|
findLastPinnedMessage() {
|
|
9026
|
-
|
|
9910
|
+
const fromMessages = [...this.processedMessages].reverse().find((message) => message.isPinned);
|
|
9911
|
+
if (fromMessages) {
|
|
9912
|
+
return fromMessages;
|
|
9913
|
+
}
|
|
9914
|
+
if (this.isRemoteMode && this.pinnedMessages?.length) {
|
|
9915
|
+
return [...this.pinnedMessages].reverse().find((message) => message.isPinned);
|
|
9916
|
+
}
|
|
9917
|
+
return undefined;
|
|
9918
|
+
}
|
|
9919
|
+
initEndlessScroll() {
|
|
9920
|
+
if (this.scrollMode === 'endless') {
|
|
9921
|
+
if (this.isRemoteMode) {
|
|
9922
|
+
this.endlessState.syncFromInputs(this.startIndex ?? 0, this.endIndex ?? 0, this.total ?? 0);
|
|
9923
|
+
}
|
|
9924
|
+
else {
|
|
9925
|
+
this.endlessState.init(this.processedMessages?.length || 0, this.pageSize);
|
|
9926
|
+
}
|
|
9927
|
+
this._previousMessagesLength = this.processedMessages?.length || 0;
|
|
9928
|
+
}
|
|
9929
|
+
else {
|
|
9930
|
+
this.endlessState.reset();
|
|
9931
|
+
this._remoteScrollToBottom = false;
|
|
9932
|
+
this._pendingRemoteScrollToMessageId = null;
|
|
9933
|
+
}
|
|
9934
|
+
}
|
|
9935
|
+
handleMessagesChange() {
|
|
9936
|
+
if (this.scrollMode !== 'endless') {
|
|
9937
|
+
const msgs = this.processedMessages;
|
|
9938
|
+
this._previousMessagesLength = msgs?.length || 0;
|
|
9939
|
+
this._previousLastMessageId = msgs?.length > 0 ? msgs[msgs.length - 1]?.id ?? null : null;
|
|
9940
|
+
this._hasProcessedMessages = true;
|
|
9941
|
+
return;
|
|
9942
|
+
}
|
|
9943
|
+
const allMessages = this.processedMessages || [];
|
|
9944
|
+
const wasAtEnd = this.endlessState.isAtEnd;
|
|
9945
|
+
const isNewMessageAppended = allMessages.length > this._previousMessagesLength && this._previousMessagesLength > 0;
|
|
9946
|
+
const isSameLength = allMessages.length === this._previousMessagesLength;
|
|
9947
|
+
const shouldScroll = this._pendingScrollAction !== null;
|
|
9948
|
+
if (isNewMessageAppended) {
|
|
9949
|
+
this.endlessState.updateTotal(allMessages.length);
|
|
9950
|
+
if (shouldScroll) {
|
|
9951
|
+
if (!wasAtEnd) {
|
|
9952
|
+
const range = this.endlessState.jumpToEnd();
|
|
9953
|
+
this.loadMore.emit({ startIndex: range.start, endIndex: range.end });
|
|
9954
|
+
}
|
|
9955
|
+
else {
|
|
9956
|
+
this.endlessState.endIndex = allMessages.length;
|
|
9957
|
+
}
|
|
9958
|
+
}
|
|
9959
|
+
}
|
|
9960
|
+
else if (!isSameLength) {
|
|
9961
|
+
this.endlessState.init(allMessages.length, this.pageSize);
|
|
9962
|
+
}
|
|
9963
|
+
this._previousMessagesLength = allMessages.length;
|
|
9964
|
+
this._previousLastMessageId = allMessages.length > 0 ? allMessages[allMessages.length - 1]?.id ?? null : null;
|
|
9965
|
+
this._hasProcessedMessages = true;
|
|
9966
|
+
}
|
|
9967
|
+
handleRemoteMessagesChange() {
|
|
9968
|
+
const wasLoading = this.endlessState.isLoading;
|
|
9969
|
+
const prevLength = this._previousMessagesLength;
|
|
9970
|
+
const currentLength = this.processedMessages?.length || 0;
|
|
9971
|
+
const isInitialBatch = prevLength === 0 && currentLength > 0;
|
|
9972
|
+
if (wasLoading) {
|
|
9973
|
+
this.endlessState.isLoading = false;
|
|
9974
|
+
if (this._remoteScrollToBottom) {
|
|
9975
|
+
this._remoteScrollToBottom = false;
|
|
9976
|
+
this._scrollHandledBeforePaint = true;
|
|
9977
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
9978
|
+
this.anchor?.scrollToBottomWithSettle();
|
|
9979
|
+
}));
|
|
9980
|
+
}
|
|
9981
|
+
else {
|
|
9982
|
+
this._scrollHandledBeforePaint = true;
|
|
9983
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
9984
|
+
this.anchor?.preserveScrollPosition();
|
|
9985
|
+
this.anchor?.setAriaLive('polite');
|
|
9986
|
+
}));
|
|
9987
|
+
}
|
|
9988
|
+
}
|
|
9989
|
+
else if (isInitialBatch && !this._pendingScrollAction) {
|
|
9990
|
+
this.autoScroll = true;
|
|
9991
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
9992
|
+
this.anchor?.scrollToBottom();
|
|
9993
|
+
}));
|
|
9994
|
+
}
|
|
9995
|
+
else if (currentLength > 0 && !this._pendingScrollAction) {
|
|
9996
|
+
const lastMessage = this.processedMessages[currentLength - 1];
|
|
9997
|
+
const isNowAtEnd = this.endIndex >= this.total;
|
|
9998
|
+
if (isNowAtEnd && this.isOwnMessage(lastMessage)) {
|
|
9999
|
+
this._pendingScrollAction = 'author';
|
|
10000
|
+
}
|
|
10001
|
+
}
|
|
10002
|
+
this._previousMessagesLength = currentLength;
|
|
10003
|
+
this._previousLastMessageId = currentLength > 0
|
|
10004
|
+
? this.processedMessages[currentLength - 1]?.id ?? null
|
|
10005
|
+
: null;
|
|
10006
|
+
if (currentLength > 0) {
|
|
10007
|
+
this._hasProcessedMessages = true;
|
|
10008
|
+
}
|
|
10009
|
+
}
|
|
10010
|
+
handleRemoteReferencedMessageClick(messageId) {
|
|
10011
|
+
const messageEl = this.chatService.messageElementsMap.get(messageId);
|
|
10012
|
+
if (messageEl) {
|
|
10013
|
+
this.anchor?.lockForMessageScroll();
|
|
10014
|
+
this.chatService.scrollToMessage(messageId, 'auto');
|
|
10015
|
+
this.anchor?.unlockForMessageScroll();
|
|
10016
|
+
return;
|
|
10017
|
+
}
|
|
10018
|
+
this._pendingRemoteScrollToMessageId = messageId;
|
|
10019
|
+
}
|
|
10020
|
+
handlePendingRemoteScroll() {
|
|
10021
|
+
if (!this._pendingRemoteScrollToMessageId) {
|
|
10022
|
+
return;
|
|
10023
|
+
}
|
|
10024
|
+
const messageId = this._pendingRemoteScrollToMessageId;
|
|
10025
|
+
this._pendingRemoteScrollToMessageId = null;
|
|
10026
|
+
this._scrollHandledBeforePaint = true;
|
|
10027
|
+
this.subs.add(this.zone.onStable.pipe(take(1)).subscribe(() => {
|
|
10028
|
+
this.anchor?.lockForMessageScroll();
|
|
10029
|
+
this.chatService.scrollToMessage(messageId, 'auto');
|
|
10030
|
+
this.anchor?.unlockForMessageScroll();
|
|
10031
|
+
}));
|
|
9027
10032
|
}
|
|
9028
10033
|
updateChatServiceProperties(propNames, changes) {
|
|
9029
10034
|
propNames.forEach(propName => {
|
|
@@ -9032,6 +10037,34 @@ class ChatComponent {
|
|
|
9032
10037
|
}
|
|
9033
10038
|
});
|
|
9034
10039
|
}
|
|
10040
|
+
validateRemoteInputs() {
|
|
10041
|
+
if (!isDevMode()) {
|
|
10042
|
+
return;
|
|
10043
|
+
}
|
|
10044
|
+
const hasRemoteInputs = isPresent(this.total) || isPresent(this.startIndex) || isPresent(this.endIndex);
|
|
10045
|
+
if (hasRemoteInputs && this.scrollMode !== 'endless') {
|
|
10046
|
+
console.warn('[kendo-chat] [total], [startIndex], and [endIndex] are only used when [scrollMode] is \'endless\'.');
|
|
10047
|
+
return;
|
|
10048
|
+
}
|
|
10049
|
+
if (isPresent(this.total) && (!isPresent(this.startIndex) || !isPresent(this.endIndex))) {
|
|
10050
|
+
console.warn('[kendo-chat] Remote mode requires [startIndex] and [endIndex] when [total] is set.');
|
|
10051
|
+
}
|
|
10052
|
+
if (isPresent(this.startIndex) && this.startIndex < 0) {
|
|
10053
|
+
console.warn('[kendo-chat] [startIndex] must not be negative.');
|
|
10054
|
+
}
|
|
10055
|
+
if (isPresent(this.startIndex) && isPresent(this.endIndex) && this.startIndex > this.endIndex) {
|
|
10056
|
+
console.warn(`[kendo-chat] [startIndex] (${this.startIndex}) must not exceed [endIndex] (${this.endIndex}).`);
|
|
10057
|
+
}
|
|
10058
|
+
if (isPresent(this.endIndex) && isPresent(this.total) && this.endIndex > this.total) {
|
|
10059
|
+
console.warn(`[kendo-chat] [endIndex] (${this.endIndex}) must not exceed [total] (${this.total}).`);
|
|
10060
|
+
}
|
|
10061
|
+
if (isPresent(this.startIndex) && isPresent(this.endIndex) && this.messages) {
|
|
10062
|
+
const expectedLength = this.endIndex - this.startIndex;
|
|
10063
|
+
if (this.messages.length !== expectedLength) {
|
|
10064
|
+
console.warn(`[kendo-chat] messages.length (${this.messages.length}) does not match the range [startIndex]–[endIndex] (expected ${expectedLength}).`);
|
|
10065
|
+
}
|
|
10066
|
+
}
|
|
10067
|
+
}
|
|
9035
10068
|
mergeWithDefaultActions(actions, defaultActions) {
|
|
9036
10069
|
if (!actions || actions.length === 0) {
|
|
9037
10070
|
return [];
|
|
@@ -9045,7 +10078,7 @@ class ChatComponent {
|
|
|
9045
10078
|
});
|
|
9046
10079
|
}
|
|
9047
10080
|
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: [
|
|
10081
|
+
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
10082
|
LocalizationService,
|
|
9050
10083
|
ChatService,
|
|
9051
10084
|
SuggestionsScrollService,
|
|
@@ -9053,7 +10086,7 @@ class ChatComponent {
|
|
|
9053
10086
|
provide: L10N_PREFIX,
|
|
9054
10087
|
useValue: 'kendo.chat',
|
|
9055
10088
|
},
|
|
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: `
|
|
10089
|
+
], 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
10090
|
<ng-container
|
|
9058
10091
|
kendoChatLocalizedMessages
|
|
9059
10092
|
i18n-deletedMessageSenderText="
|
|
@@ -9123,9 +10156,7 @@ class ChatComponent {
|
|
|
9123
10156
|
kendo.chat.nextSuggestionsButtonTitle|The title of the button that scrolls to the next suggestions
|
|
9124
10157
|
"
|
|
9125
10158
|
nextSuggestionsButtonTitle="Scroll right"
|
|
9126
|
-
i18n-unpinMessageTitle="
|
|
9127
|
-
kendo.chat.unpinMessageTitle|The title of the button that unpins a pinned message
|
|
9128
|
-
"
|
|
10159
|
+
i18n-unpinMessageTitle="kendo.chat.unpinMessageTitle|The title of the button that unpins a pinned message"
|
|
9129
10160
|
unpinMessageTitle="Unpin message"
|
|
9130
10161
|
>
|
|
9131
10162
|
</ng-container>
|
|
@@ -9144,18 +10175,30 @@ class ChatComponent {
|
|
|
9144
10175
|
[attr.aria-label]="textFor('messageListLabel')"
|
|
9145
10176
|
#anchor="scrollAnchor"
|
|
9146
10177
|
[(autoScroll)]="autoScroll"
|
|
10178
|
+
[autoScrollThreshold]="autoScrollThreshold"
|
|
10179
|
+
[endlessMode]="scrollMode === 'endless'"
|
|
10180
|
+
[rangeIsAtEnd]="endlessState.isAtEnd"
|
|
10181
|
+
(nearTop)="onNearTop()"
|
|
10182
|
+
(nearBottom)="onNearBottom()"
|
|
9147
10183
|
>
|
|
9148
10184
|
@if (pinnedMessage) {
|
|
9149
10185
|
<div
|
|
9150
10186
|
class="k-message-reference k-message-pinned"
|
|
9151
10187
|
[class.k-message-reference-receiver]="!isOwnMessage(pinnedMessage)"
|
|
9152
10188
|
[class.k-message-reference-sender]="isOwnMessage(pinnedMessage)"
|
|
9153
|
-
(click)="scrollToPinnedMessage()"
|
|
10189
|
+
(click)="scrollToPinnedMessage($event)"
|
|
9154
10190
|
>
|
|
9155
10191
|
<kendo-icon-wrapper name="pin" [svgIcon]="pinIcon"> </kendo-icon-wrapper>
|
|
9156
10192
|
<chat-message-reference-content [message]="pinnedMessage"></chat-message-reference-content>
|
|
9157
10193
|
<span class="k-spacer"></span>
|
|
9158
|
-
<button
|
|
10194
|
+
<button
|
|
10195
|
+
kendoButton
|
|
10196
|
+
icon="x"
|
|
10197
|
+
[svgIcon]="deleteIcon"
|
|
10198
|
+
[attr.title]="textFor('unpinMessageTitle')"
|
|
10199
|
+
(click)="unpin.emit(pinnedMessage)"
|
|
10200
|
+
fillMode="flat"
|
|
10201
|
+
></button>
|
|
9159
10202
|
</div>
|
|
9160
10203
|
} @if (processedMessages && processedMessages.length === 0) {
|
|
9161
10204
|
<div class="k-message-list-content k-message-list-content-empty">
|
|
@@ -9163,7 +10206,7 @@ class ChatComponent {
|
|
|
9163
10206
|
</div>
|
|
9164
10207
|
} @else {
|
|
9165
10208
|
<kendo-chat-message-list
|
|
9166
|
-
[messages]="
|
|
10209
|
+
[messages]="renderedMessages"
|
|
9167
10210
|
[authorMessageContentTemplate]="authorMessageContentTemplate"
|
|
9168
10211
|
[receiverMessageContentTemplate]="receiverMessageContentTemplate"
|
|
9169
10212
|
[messageContentTemplate]="messageContentTemplate"
|
|
@@ -9177,7 +10220,7 @@ class ChatComponent {
|
|
|
9177
10220
|
[attachmentTemplate]="attachmentTemplate"
|
|
9178
10221
|
[authorId]="authorId"
|
|
9179
10222
|
(executeAction)="dispatchAction($event)"
|
|
9180
|
-
(resize)="
|
|
10223
|
+
(resize)="onMessageListResize()"
|
|
9181
10224
|
(navigate)="this.autoScroll = false"
|
|
9182
10225
|
>
|
|
9183
10226
|
</kendo-chat-message-list>
|
|
@@ -9190,7 +10233,7 @@ class ChatComponent {
|
|
|
9190
10233
|
[align]="{ horizontal: 'center' }"
|
|
9191
10234
|
icon="arrow-down-outline"
|
|
9192
10235
|
[svgIcon]="scrollToBottomIcon"
|
|
9193
|
-
(click)="
|
|
10236
|
+
(click)="onScrollToBottomClick()"
|
|
9194
10237
|
></kendo-floatingactionbutton>
|
|
9195
10238
|
</div>
|
|
9196
10239
|
}
|
|
@@ -9227,7 +10270,11 @@ class ChatComponent {
|
|
|
9227
10270
|
(popupClose)="handleMenuClose($event)"
|
|
9228
10271
|
(select)="onContextMenuAction($event.item.originalAction)"
|
|
9229
10272
|
></kendo-contextmenu>
|
|
9230
|
-
|
|
10273
|
+
|
|
10274
|
+
@if (showLicenseWatermark) {
|
|
10275
|
+
<div kendoWatermarkOverlay [licenseMessage]="licenseMessage"></div>
|
|
10276
|
+
}
|
|
10277
|
+
`, 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
10278
|
}
|
|
9232
10279
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatComponent, decorators: [{
|
|
9233
10280
|
type: Component,
|
|
@@ -9312,9 +10359,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9312
10359
|
kendo.chat.nextSuggestionsButtonTitle|The title of the button that scrolls to the next suggestions
|
|
9313
10360
|
"
|
|
9314
10361
|
nextSuggestionsButtonTitle="Scroll right"
|
|
9315
|
-
i18n-unpinMessageTitle="
|
|
9316
|
-
kendo.chat.unpinMessageTitle|The title of the button that unpins a pinned message
|
|
9317
|
-
"
|
|
10362
|
+
i18n-unpinMessageTitle="kendo.chat.unpinMessageTitle|The title of the button that unpins a pinned message"
|
|
9318
10363
|
unpinMessageTitle="Unpin message"
|
|
9319
10364
|
>
|
|
9320
10365
|
</ng-container>
|
|
@@ -9333,18 +10378,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9333
10378
|
[attr.aria-label]="textFor('messageListLabel')"
|
|
9334
10379
|
#anchor="scrollAnchor"
|
|
9335
10380
|
[(autoScroll)]="autoScroll"
|
|
10381
|
+
[autoScrollThreshold]="autoScrollThreshold"
|
|
10382
|
+
[endlessMode]="scrollMode === 'endless'"
|
|
10383
|
+
[rangeIsAtEnd]="endlessState.isAtEnd"
|
|
10384
|
+
(nearTop)="onNearTop()"
|
|
10385
|
+
(nearBottom)="onNearBottom()"
|
|
9336
10386
|
>
|
|
9337
10387
|
@if (pinnedMessage) {
|
|
9338
10388
|
<div
|
|
9339
10389
|
class="k-message-reference k-message-pinned"
|
|
9340
10390
|
[class.k-message-reference-receiver]="!isOwnMessage(pinnedMessage)"
|
|
9341
10391
|
[class.k-message-reference-sender]="isOwnMessage(pinnedMessage)"
|
|
9342
|
-
(click)="scrollToPinnedMessage()"
|
|
10392
|
+
(click)="scrollToPinnedMessage($event)"
|
|
9343
10393
|
>
|
|
9344
10394
|
<kendo-icon-wrapper name="pin" [svgIcon]="pinIcon"> </kendo-icon-wrapper>
|
|
9345
10395
|
<chat-message-reference-content [message]="pinnedMessage"></chat-message-reference-content>
|
|
9346
10396
|
<span class="k-spacer"></span>
|
|
9347
|
-
<button
|
|
10397
|
+
<button
|
|
10398
|
+
kendoButton
|
|
10399
|
+
icon="x"
|
|
10400
|
+
[svgIcon]="deleteIcon"
|
|
10401
|
+
[attr.title]="textFor('unpinMessageTitle')"
|
|
10402
|
+
(click)="unpin.emit(pinnedMessage)"
|
|
10403
|
+
fillMode="flat"
|
|
10404
|
+
></button>
|
|
9348
10405
|
</div>
|
|
9349
10406
|
} @if (processedMessages && processedMessages.length === 0) {
|
|
9350
10407
|
<div class="k-message-list-content k-message-list-content-empty">
|
|
@@ -9352,7 +10409,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9352
10409
|
</div>
|
|
9353
10410
|
} @else {
|
|
9354
10411
|
<kendo-chat-message-list
|
|
9355
|
-
[messages]="
|
|
10412
|
+
[messages]="renderedMessages"
|
|
9356
10413
|
[authorMessageContentTemplate]="authorMessageContentTemplate"
|
|
9357
10414
|
[receiverMessageContentTemplate]="receiverMessageContentTemplate"
|
|
9358
10415
|
[messageContentTemplate]="messageContentTemplate"
|
|
@@ -9366,7 +10423,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9366
10423
|
[attachmentTemplate]="attachmentTemplate"
|
|
9367
10424
|
[authorId]="authorId"
|
|
9368
10425
|
(executeAction)="dispatchAction($event)"
|
|
9369
|
-
(resize)="
|
|
10426
|
+
(resize)="onMessageListResize()"
|
|
9370
10427
|
(navigate)="this.autoScroll = false"
|
|
9371
10428
|
>
|
|
9372
10429
|
</kendo-chat-message-list>
|
|
@@ -9379,7 +10436,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9379
10436
|
[align]="{ horizontal: 'center' }"
|
|
9380
10437
|
icon="arrow-down-outline"
|
|
9381
10438
|
[svgIcon]="scrollToBottomIcon"
|
|
9382
|
-
(click)="
|
|
10439
|
+
(click)="onScrollToBottomClick()"
|
|
9383
10440
|
></kendo-floatingactionbutton>
|
|
9384
10441
|
</div>
|
|
9385
10442
|
}
|
|
@@ -9416,6 +10473,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9416
10473
|
(popupClose)="handleMenuClose($event)"
|
|
9417
10474
|
(select)="onContextMenuAction($event.item.originalAction)"
|
|
9418
10475
|
></kendo-contextmenu>
|
|
10476
|
+
|
|
10477
|
+
@if (showLicenseWatermark) {
|
|
10478
|
+
<div kendoWatermarkOverlay [licenseMessage]="licenseMessage"></div>
|
|
10479
|
+
}
|
|
9419
10480
|
`,
|
|
9420
10481
|
standalone: true,
|
|
9421
10482
|
imports: [
|
|
@@ -9429,6 +10490,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9429
10490
|
ButtonComponent,
|
|
9430
10491
|
ContextMenuComponent,
|
|
9431
10492
|
FloatingActionButtonComponent,
|
|
10493
|
+
WatermarkOverlayComponent,
|
|
9432
10494
|
],
|
|
9433
10495
|
}]
|
|
9434
10496
|
}], ctorParameters: () => [{ type: i1.LocalizationService }, { type: i0.NgZone }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: ChatService }], propDecorators: { messages: [{
|
|
@@ -9483,6 +10545,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9483
10545
|
type: Input
|
|
9484
10546
|
}], sendButtonSettings: [{
|
|
9485
10547
|
type: Input
|
|
10548
|
+
}], scrollMode: [{
|
|
10549
|
+
type: Input
|
|
10550
|
+
}], pageSize: [{
|
|
10551
|
+
type: Input
|
|
10552
|
+
}], autoScrollThreshold: [{
|
|
10553
|
+
type: Input
|
|
10554
|
+
}], total: [{
|
|
10555
|
+
type: Input
|
|
10556
|
+
}], startIndex: [{
|
|
10557
|
+
type: Input
|
|
10558
|
+
}], endIndex: [{
|
|
10559
|
+
type: Input
|
|
10560
|
+
}], pinnedMessages: [{
|
|
10561
|
+
type: Input
|
|
10562
|
+
}], repliedToMessages: [{
|
|
10563
|
+
type: Input
|
|
9486
10564
|
}], modelFields: [{
|
|
9487
10565
|
type: Input
|
|
9488
10566
|
}], scrollToBottomButton: [{
|
|
@@ -9513,6 +10591,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9513
10591
|
type: Output
|
|
9514
10592
|
}], inputValueChange: [{
|
|
9515
10593
|
type: Output
|
|
10594
|
+
}], loadMore: [{
|
|
10595
|
+
type: Output
|
|
10596
|
+
}], referencedMessageClick: [{
|
|
10597
|
+
type: Output
|
|
9516
10598
|
}], className: [{
|
|
9517
10599
|
type: HostBinding,
|
|
9518
10600
|
args: ['class']
|
|
@@ -9579,6 +10661,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
9579
10661
|
}], messageList: [{
|
|
9580
10662
|
type: ViewChild,
|
|
9581
10663
|
args: ['messageList', { static: true, read: ViewContainerRef }]
|
|
10664
|
+
}], anchor: [{
|
|
10665
|
+
type: ViewChild,
|
|
10666
|
+
args: ['anchor']
|
|
9582
10667
|
}] } });
|
|
9583
10668
|
|
|
9584
10669
|
// eslint-disable no-forward-ref
|