@limetech/lime-crm-building-blocks 1.118.1 → 1.119.0
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/CHANGELOG.md +7 -0
- package/dist/cjs/lime-crm-building-blocks.cjs.js +1 -1
- package/dist/cjs/limebb-alert-dialog.cjs.entry.js +1 -1
- package/dist/cjs/limebb-browser.cjs.entry.js +2 -2
- package/dist/cjs/limebb-chat-icon-list_3.cjs.entry.js +4 -4
- package/dist/cjs/limebb-chat-list.cjs.entry.js +336 -68
- package/dist/cjs/limebb-composer-toolbar.cjs.entry.js +2 -2
- package/dist/cjs/limebb-currency-picker.cjs.entry.js +1 -1
- package/dist/cjs/limebb-dashboard-widget.cjs.entry.js +1 -1
- package/dist/cjs/limebb-date-picker.cjs.entry.js +1 -1
- package/dist/cjs/limebb-date-range.cjs.entry.js +1 -1
- package/dist/cjs/limebb-document-chips.cjs.entry.js +1 -1
- package/dist/cjs/limebb-document-item.cjs.entry.js +2 -2
- package/dist/cjs/limebb-document-picker.cjs.entry.js +1 -1
- package/dist/cjs/limebb-feed-item-thumbnail-file-info.cjs.entry.js +1 -1
- package/dist/cjs/limebb-feed-timeline-item.cjs.entry.js +1 -1
- package/dist/cjs/limebb-feed.cjs.entry.js +1 -1
- package/dist/cjs/limebb-icon-picker.cjs.entry.js +1 -1
- package/dist/cjs/limebb-info-tile-format.cjs.entry.js +1 -1
- package/dist/cjs/limebb-info-tile.cjs.entry.js +1 -1
- package/dist/cjs/limebb-kanban-group.cjs.entry.js +1 -1
- package/dist/cjs/limebb-kanban-item.cjs.entry.js +1 -1
- package/dist/cjs/limebb-lime-query-builder.cjs.entry.js +1 -1
- package/dist/cjs/limebb-lime-query-filter-builder_3.cjs.entry.js +2 -2
- package/dist/cjs/limebb-lime-query-filter-comparison_2.cjs.entry.js +1 -1
- package/dist/cjs/limebb-lime-query-filter-group_3.cjs.entry.js +3 -3
- package/dist/cjs/limebb-lime-query-order-by-item.cjs.entry.js +2 -2
- package/dist/cjs/limebb-lime-query-response-format-builder.cjs.entry.js +1 -1
- package/dist/cjs/limebb-lime-query-response-format-editor_2.cjs.entry.js +1 -1
- package/dist/cjs/limebb-live-docs-info.cjs.entry.js +2 -2
- package/dist/cjs/limebb-locale-picker.cjs.entry.js +1 -1
- package/dist/cjs/limebb-mention-group-counter.cjs.entry.js +2 -2
- package/dist/cjs/limebb-navigation-button_2.cjs.entry.js +2 -2
- package/dist/cjs/limebb-notification-item.cjs.entry.js +1 -1
- package/dist/cjs/limebb-percentage-visualizer.cjs.entry.js +2 -2
- package/dist/cjs/limebb-text-editor.cjs.entry.js +1 -1
- package/dist/cjs/limebb-trend-indicator.cjs.entry.js +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/alert-dialog/alert-dialog.js +1 -1
- package/dist/collection/components/browser/browser.js +2 -2
- package/dist/collection/components/chat-list/chat-icon-list/chat-icon-list.js +1 -1
- package/dist/collection/components/chat-list/chat-item/chat-item.js +2 -2
- package/dist/collection/components/chat-list/chat-list.css +10 -10
- package/dist/collection/components/chat-list/chat-list.js +93 -25
- package/dist/collection/components/chat-list/process-chat-items.js +111 -45
- package/dist/collection/components/chat-list/scroll-anchor.js +93 -0
- package/dist/collection/components/chat-list/typing-indicator/typing-indicator.js +1 -1
- package/dist/collection/components/chat-list/typing-indicator-scroll.js +41 -0
- package/dist/collection/components/composer-toolbar/composer-toolbar.js +2 -2
- package/dist/collection/components/currency-picker/currency-picker.js +1 -1
- package/dist/collection/components/dashboard-widget/dashboard-widget.js +1 -1
- package/dist/collection/components/date-picker/date-picker.js +1 -1
- package/dist/collection/components/date-range/date-range.js +1 -1
- package/dist/collection/components/document-chips/document-chips.js +1 -1
- package/dist/collection/components/document-picker/document-item/document-item.js +2 -2
- package/dist/collection/components/document-picker/document-picker.js +1 -1
- package/dist/collection/components/feed/feed-item/feed-timeline-item.js +1 -1
- package/dist/collection/components/feed/feed-item-thumbnail-file-info/feed-item-thumbnail-file-info.js +1 -1
- package/dist/collection/components/feed/feed.js +1 -1
- package/dist/collection/components/icon-picker/icon-picker.js +1 -1
- package/dist/collection/components/info-tile/format/config/info-tile-format.js +1 -1
- package/dist/collection/components/info-tile/info-tile.js +1 -1
- package/dist/collection/components/kanban/kanban-group/kanban-group.js +1 -1
- package/dist/collection/components/kanban/kanban-item/kanban-item.js +1 -1
- package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-comparison.js +1 -1
- package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-group.js +2 -2
- package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-not.js +1 -1
- package/dist/collection/components/lime-query-builder/lime-query-builder.js +1 -1
- package/dist/collection/components/lime-query-builder/lime-query-response-format-builder.js +1 -1
- package/dist/collection/components/lime-query-builder/limetype-field/limetype-field.js +1 -1
- package/dist/collection/components/lime-query-builder/order-by/order-by-editor.js +1 -1
- package/dist/collection/components/lime-query-builder/order-by/order-by-item.js +2 -2
- package/dist/collection/components/lime-query-builder/response-format/response-format-item.js +1 -1
- package/dist/collection/components/limeobject/file-viewer/live-docs-info.js +2 -2
- package/dist/collection/components/locale-picker/locale-picker.js +1 -1
- package/dist/collection/components/notification-list/notification-item/notification-item.js +1 -1
- package/dist/collection/components/percentage-visualizer/percentage-visualizer.js +2 -2
- package/dist/collection/components/summary-popover/summary-popover.js +2 -2
- package/dist/collection/components/text-editor/mention-group-counter.js +2 -2
- package/dist/collection/components/text-editor/text-editor.js +1 -1
- package/dist/collection/components/trend-indicator/trend-indicator.js +1 -1
- package/dist/components/chat-icon-list.js +1 -1
- package/dist/components/chat-item.js +1 -1
- package/dist/components/currency-picker.js +1 -1
- package/dist/components/date-picker.js +1 -1
- package/dist/components/document-item.js +1 -1
- package/dist/components/feed-item-thumbnail-file-info.js +1 -1
- package/dist/components/feed-timeline-item.js +1 -1
- package/dist/components/kanban-group.js +1 -1
- package/dist/components/kanban-item.js +1 -1
- package/dist/components/lime-query-filter-comparison.js +1 -1
- package/dist/components/lime-query-filter-expression.js +1 -1
- package/dist/components/limebb-alert-dialog.js +1 -1
- package/dist/components/limebb-browser.js +1 -1
- package/dist/components/limebb-chat-list.js +1 -1
- package/dist/components/limebb-composer-toolbar.js +1 -1
- package/dist/components/limebb-dashboard-widget.js +1 -1
- package/dist/components/limebb-date-range.js +1 -1
- package/dist/components/limebb-document-chips.js +1 -1
- package/dist/components/limebb-document-picker.js +1 -1
- package/dist/components/limebb-feed.js +1 -1
- package/dist/components/limebb-icon-picker.js +1 -1
- package/dist/components/limebb-info-tile-format.js +1 -1
- package/dist/components/limebb-info-tile.js +1 -1
- package/dist/components/limebb-lime-query-builder.js +1 -1
- package/dist/components/limebb-lime-query-response-format-builder.js +1 -1
- package/dist/components/limebb-locale-picker.js +1 -1
- package/dist/components/limebb-mention-group-counter.js +1 -1
- package/dist/components/limebb-text-editor.js +1 -1
- package/dist/components/limebb-trend-indicator.js +1 -1
- package/dist/components/limetype-field.js +1 -1
- package/dist/components/live-docs-info.js +1 -1
- package/dist/components/notification-item.js +1 -1
- package/dist/components/order-by-editor.js +1 -1
- package/dist/components/order-by-item.js +1 -1
- package/dist/components/percentage-visualizer.js +1 -1
- package/dist/components/response-format-item.js +1 -1
- package/dist/components/summary-popover.js +1 -1
- package/dist/components/typing-indicator.js +1 -1
- package/dist/esm/lime-crm-building-blocks.js +1 -1
- package/dist/esm/limebb-alert-dialog.entry.js +1 -1
- package/dist/esm/limebb-browser.entry.js +2 -2
- package/dist/esm/limebb-chat-icon-list_3.entry.js +4 -4
- package/dist/esm/limebb-chat-list.entry.js +337 -69
- package/dist/esm/limebb-composer-toolbar.entry.js +2 -2
- package/dist/esm/limebb-currency-picker.entry.js +1 -1
- package/dist/esm/limebb-dashboard-widget.entry.js +1 -1
- package/dist/esm/limebb-date-picker.entry.js +1 -1
- package/dist/esm/limebb-date-range.entry.js +1 -1
- package/dist/esm/limebb-document-chips.entry.js +1 -1
- package/dist/esm/limebb-document-item.entry.js +2 -2
- package/dist/esm/limebb-document-picker.entry.js +1 -1
- package/dist/esm/limebb-feed-item-thumbnail-file-info.entry.js +1 -1
- package/dist/esm/limebb-feed-timeline-item.entry.js +1 -1
- package/dist/esm/limebb-feed.entry.js +1 -1
- package/dist/esm/limebb-icon-picker.entry.js +1 -1
- package/dist/esm/limebb-info-tile-format.entry.js +1 -1
- package/dist/esm/limebb-info-tile.entry.js +1 -1
- package/dist/esm/limebb-kanban-group.entry.js +1 -1
- package/dist/esm/limebb-kanban-item.entry.js +1 -1
- package/dist/esm/limebb-lime-query-builder.entry.js +1 -1
- package/dist/esm/limebb-lime-query-filter-builder_3.entry.js +2 -2
- package/dist/esm/limebb-lime-query-filter-comparison_2.entry.js +1 -1
- package/dist/esm/limebb-lime-query-filter-group_3.entry.js +3 -3
- package/dist/esm/limebb-lime-query-order-by-item.entry.js +2 -2
- package/dist/esm/limebb-lime-query-response-format-builder.entry.js +1 -1
- package/dist/esm/limebb-lime-query-response-format-editor_2.entry.js +1 -1
- package/dist/esm/limebb-live-docs-info.entry.js +2 -2
- package/dist/esm/limebb-locale-picker.entry.js +1 -1
- package/dist/esm/limebb-mention-group-counter.entry.js +2 -2
- package/dist/esm/limebb-navigation-button_2.entry.js +2 -2
- package/dist/esm/limebb-notification-item.entry.js +1 -1
- package/dist/esm/limebb-percentage-visualizer.entry.js +2 -2
- package/dist/esm/limebb-text-editor.entry.js +1 -1
- package/dist/esm/limebb-trend-indicator.entry.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/lime-crm-building-blocks/lime-crm-building-blocks.esm.js +1 -1
- package/dist/lime-crm-building-blocks/{p-685dd8a1.entry.js → p-02a3530f.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-52fb2a66.entry.js → p-16ad65dc.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-6cb2d9dd.entry.js → p-2079ae59.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-c3b13800.entry.js → p-2aae332a.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-b3ee6683.entry.js → p-4edfa72a.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-b36ad4e8.entry.js → p-5058bc60.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-8477cb5d.entry.js → p-52fd0169.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-21ec697f.entry.js → p-54ae7814.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-35a51259.entry.js → p-54bb577b.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/p-59b992b3.entry.js +1 -0
- package/dist/lime-crm-building-blocks/{p-1a61674f.entry.js → p-5ef41ad0.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-f375d69a.entry.js → p-640c41a3.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-528f3635.entry.js → p-65485470.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-4ddd75bc.entry.js → p-655968ce.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-8a05a0e5.entry.js → p-67551f9d.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-152edb10.entry.js → p-750dbb18.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-bac3d599.entry.js → p-83e93ebd.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-fa41a9e6.entry.js → p-8a820ede.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/p-8da95b3a.entry.js +1 -0
- package/dist/lime-crm-building-blocks/{p-943c8589.entry.js → p-999c1628.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-62437a1d.entry.js → p-aea85d91.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-9ae55a96.entry.js → p-b17327da.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-71191041.entry.js → p-b3db6a41.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-6fdee70a.entry.js → p-b6895996.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-cff9bccd.entry.js → p-b92eedb5.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-aaeecca6.entry.js → p-c019f7f2.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-fdf269f7.entry.js → p-c3648986.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-ebe6040f.entry.js → p-cc07285b.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-ffb847d0.entry.js → p-cee4e6a6.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-18fc1a06.entry.js → p-d153e06d.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-be8dc8ae.entry.js → p-e33feb37.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-fcedbc77.entry.js → p-e4005988.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-a48a6ff8.entry.js → p-e8a0bbb8.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-eec0a0c8.entry.js → p-ebeddc29.entry.js} +1 -1
- package/dist/lime-crm-building-blocks/{p-5a1f0a2b.entry.js → p-fa808d3a.entry.js} +1 -1
- package/dist/types/components/chat-list/chat-list.d.ts +13 -3
- package/dist/types/components/chat-list/process-chat-items.d.ts +20 -4
- package/dist/types/components/chat-list/scroll-anchor.d.ts +46 -0
- package/dist/types/components/chat-list/typing-indicator-scroll.d.ts +18 -0
- package/dist/types/components.d.ts +4 -4
- package/package.json +1 -1
- package/dist/lime-crm-building-blocks/p-b10faec5.entry.js +0 -1
- package/dist/lime-crm-building-blocks/p-f23ea0e7.entry.js +0 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { h, Host } from "@stencil/core";
|
|
2
2
|
import { PlatformServiceName, } from "@limetech/lime-web-components";
|
|
3
|
-
import { processChatItems, } from "./process-chat-items";
|
|
3
|
+
import { processChatItems, reverseProcessedItems, } from "./process-chat-items";
|
|
4
|
+
import { ScrollAnchor } from "./scroll-anchor";
|
|
5
|
+
import { createHeightTransitionEndHandler, verifyHeightAnimationProperty, } from "./typing-indicator-scroll";
|
|
4
6
|
/**
|
|
5
7
|
* Chat list
|
|
6
8
|
* This component displays a list of chat items, and groups them by date.
|
|
@@ -22,7 +24,10 @@ export class ChatList {
|
|
|
22
24
|
constructor() {
|
|
23
25
|
/**
|
|
24
26
|
* List of items to display in the feed.
|
|
25
|
-
* These items must be sorted by their `timestamp` in
|
|
27
|
+
* These items must be sorted by their `timestamp` in ascending order
|
|
28
|
+
* (oldest first, newest last). This matches the natural visual order
|
|
29
|
+
* for the default `oldest-on-top` layout, keeping DOM order aligned
|
|
30
|
+
* with visual order for copy/paste, screen readers, and tab order.
|
|
26
31
|
*
|
|
27
32
|
* See the [ChatListItem](#/type/ChatListItem/) type for the kinds of
|
|
28
33
|
* items that can appear in the list.
|
|
@@ -36,29 +41,56 @@ export class ChatList {
|
|
|
36
41
|
*
|
|
37
42
|
* :::important
|
|
38
43
|
* Using this prop will _not_ automatically sort the chat items for you!
|
|
39
|
-
* You still need to pass the items in the correct order (
|
|
40
|
-
* This prop
|
|
44
|
+
* You still need to pass the items in the correct order (ascending order by timestamp — oldest first).
|
|
45
|
+
* This prop only affects the direction of the elements in the user interface,
|
|
41
46
|
* such as the location of the typing indicator or the scrollbars.
|
|
42
47
|
* :::
|
|
43
48
|
*/
|
|
44
49
|
this.order = 'oldest-on-top';
|
|
45
50
|
this.processedItems = [];
|
|
51
|
+
this.handleHeightTransitionEnd = createHeightTransitionEndHandler(() => { var _a; return (_a = this.scrollAnchor) === null || _a === void 0 ? void 0 : _a.repinIfSticky(); });
|
|
46
52
|
}
|
|
47
53
|
handleItemsChange() {
|
|
48
54
|
let lastVisited = null;
|
|
49
55
|
if (this.lastVisitedTimestamp) {
|
|
50
56
|
lastVisited = parseToUTCDate(this.lastVisitedTimestamp);
|
|
51
57
|
}
|
|
52
|
-
|
|
58
|
+
const processed = processChatItems(this.items, this.dateTimeFormatter, lastVisited);
|
|
59
|
+
this.processedItems =
|
|
60
|
+
this.order === 'newest-on-top'
|
|
61
|
+
? reverseProcessedItems(processed)
|
|
62
|
+
: processed;
|
|
53
63
|
}
|
|
54
64
|
componentWillLoad() {
|
|
55
65
|
this.handleItemsChange();
|
|
66
|
+
this.scrollAnchor = new ScrollAnchor(this.host, () => this.order, this.order);
|
|
67
|
+
}
|
|
68
|
+
componentDidLoad() {
|
|
69
|
+
var _a;
|
|
70
|
+
verifyHeightAnimationProperty((_a = this.host.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.typing-indicator-height-animation'));
|
|
71
|
+
}
|
|
72
|
+
componentWillRender() {
|
|
73
|
+
if (this.items.length === 0) {
|
|
74
|
+
// Nothing to measure or scroll against. `isSticky` retains
|
|
75
|
+
// its last value (default `true`), so the first non-empty
|
|
76
|
+
// render still pins correctly.
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.scrollAnchor.beforeRender();
|
|
80
|
+
}
|
|
81
|
+
componentDidRender() {
|
|
82
|
+
if (this.items.length === 0) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
this.scrollAnchor.afterRender();
|
|
56
86
|
}
|
|
57
87
|
render() {
|
|
58
|
-
return (h(Host, { key: '
|
|
88
|
+
return (h(Host, { key: '002369dc1f47978696b4eb8ac7189f794568b0b4', class: {
|
|
59
89
|
'is-empty': this.items.length === 0 &&
|
|
60
90
|
!this.isTypingIndicatorVisible,
|
|
61
|
-
}, "aria-busy": this.isBusy(), role: "log" }, this.renderSpinner(),
|
|
91
|
+
}, "aria-busy": this.isBusy(), role: "log" }, this.renderSpinner(), this.order === 'newest-on-top' &&
|
|
92
|
+
this.renderTypingIndicatorBlock(), this.renderChatItems(), this.order === 'oldest-on-top' &&
|
|
93
|
+
this.renderTypingIndicatorBlock()));
|
|
62
94
|
}
|
|
63
95
|
renderSpinner() {
|
|
64
96
|
if (!this.loading) {
|
|
@@ -66,6 +98,14 @@ export class ChatList {
|
|
|
66
98
|
}
|
|
67
99
|
return h("limel-spinner", null);
|
|
68
100
|
}
|
|
101
|
+
renderTypingIndicatorBlock() {
|
|
102
|
+
// The indicator's grid-template-rows transition grows the
|
|
103
|
+
// container over ~0.46s. `componentDidRender` fires before the
|
|
104
|
+
// transition starts, so `scrollHeight` at that moment doesn't
|
|
105
|
+
// include the final height. `handleHeightTransitionEnd` re-pins
|
|
106
|
+
// once the transition lands.
|
|
107
|
+
return (h("div", { class: "typing-indicator-height-animation", onTransitionEnd: this.handleHeightTransitionEnd }, h("div", null, this.renderTypingIndicator())));
|
|
108
|
+
}
|
|
69
109
|
renderTypingIndicator() {
|
|
70
110
|
if (!this.isTypingIndicatorVisible) {
|
|
71
111
|
return;
|
|
@@ -81,33 +121,54 @@ export class ChatList {
|
|
|
81
121
|
case 'newItemIndicator': {
|
|
82
122
|
return this.renderIndicator();
|
|
83
123
|
}
|
|
84
|
-
|
|
124
|
+
case 'chat':
|
|
125
|
+
case 'iconList': {
|
|
126
|
+
// `insertDateGroups` wraps every `chat` / `iconList`
|
|
127
|
+
// in a `group`, so these variants shouldn't appear
|
|
128
|
+
// at top level. If the processor pipeline is ever
|
|
129
|
+
// reordered, render nothing rather than double-
|
|
130
|
+
// render once the wrapping group includes them.
|
|
85
131
|
return null;
|
|
86
132
|
}
|
|
133
|
+
default: {
|
|
134
|
+
return assertNever(item);
|
|
135
|
+
}
|
|
87
136
|
}
|
|
88
137
|
});
|
|
89
138
|
}
|
|
90
139
|
renderGroup(group) {
|
|
91
140
|
const chatItems = group.items.map((item) => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
141
|
+
switch (item.type) {
|
|
142
|
+
case 'chat': {
|
|
143
|
+
return this.renderChatItem(item.item);
|
|
144
|
+
}
|
|
145
|
+
case 'iconList': {
|
|
146
|
+
return this.renderIconList(item.item);
|
|
147
|
+
}
|
|
148
|
+
case 'newItemIndicator': {
|
|
149
|
+
return this.renderIndicator();
|
|
150
|
+
}
|
|
151
|
+
case 'group': {
|
|
152
|
+
// Date groups don't nest. A compile-time `never`
|
|
153
|
+
// here would require a separate nested-item type;
|
|
154
|
+
// this runtime null return is the pragmatic
|
|
155
|
+
// middle ground.
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
default: {
|
|
159
|
+
return assertNever(item);
|
|
160
|
+
}
|
|
100
161
|
}
|
|
101
162
|
});
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
163
|
+
// Key the section by the group's heading (unique per date, and
|
|
164
|
+
// stable across item additions). Must not vary with group
|
|
165
|
+
// content: if it did, appending a new message would change the
|
|
166
|
+
// key and cause Stencil to remount the whole section, briefly
|
|
167
|
+
// collapsing scrollHeight and scrolling the list to the top
|
|
168
|
+
// before `componentDidRender` scrolls it back.
|
|
169
|
+
const sectionKey = 'date-group-' + group.heading;
|
|
106
170
|
const headingId = `heading-${sectionKey}`.replaceAll(/[^a-zA-Z0-9_-]/g, '_');
|
|
107
171
|
const dateHeading = (h("a", { id: headingId, class: "date-heading", role: "heading", "aria-level": 2 }, group.heading));
|
|
108
|
-
if (this.order === 'oldest-on-top') {
|
|
109
|
-
return (h("section", { class: "date-group", key: sectionKey, "aria-labelledby": headingId }, chatItems, dateHeading));
|
|
110
|
-
}
|
|
111
172
|
return (h("section", { class: "date-group", key: sectionKey, "aria-labelledby": headingId }, dateHeading, chatItems));
|
|
112
173
|
}
|
|
113
174
|
renderChatItem(item) {
|
|
@@ -215,7 +276,7 @@ export class ChatList {
|
|
|
215
276
|
"optional": false,
|
|
216
277
|
"docs": {
|
|
217
278
|
"tags": [],
|
|
218
|
-
"text": "List of items to display in the feed.\nThese items must be sorted by their `timestamp` in
|
|
279
|
+
"text": "List of items to display in the feed.\nThese items must be sorted by their `timestamp` in ascending order\n(oldest first, newest last). This matches the natural visual order\nfor the default `oldest-on-top` layout, keeping DOM order aligned\nwith visual order for copy/paste, screen readers, and tab order.\n\nSee the [ChatListItem](#/type/ChatListItem/) type for the kinds of\nitems that can appear in the list."
|
|
219
280
|
},
|
|
220
281
|
"getter": false,
|
|
221
282
|
"setter": false,
|
|
@@ -295,7 +356,7 @@ export class ChatList {
|
|
|
295
356
|
"optional": false,
|
|
296
357
|
"docs": {
|
|
297
358
|
"tags": [],
|
|
298
|
-
"text": "- `newest-on-top` displays new messages at the top of the list;\nused if the chat input is located on top of the chat interface.\n- `oldest-on-top` displays new messages at the bottom of the list (default);\nused if the chat input is located at the bottom of the chat interface.\n\n:::important\nUsing this prop will _not_ automatically sort the chat items for you!\nYou still need to pass the items in the correct order (
|
|
359
|
+
"text": "- `newest-on-top` displays new messages at the top of the list;\nused if the chat input is located on top of the chat interface.\n- `oldest-on-top` displays new messages at the bottom of the list (default);\nused if the chat input is located at the bottom of the chat interface.\n\n:::important\nUsing this prop will _not_ automatically sort the chat items for you!\nYou still need to pass the items in the correct order (ascending order by timestamp \u2014 oldest first).\nThis prop only affects the direction of the elements in the user interface,\nsuch as the location of the typing indicator or the scrollbars.\n:::"
|
|
299
360
|
},
|
|
300
361
|
"getter": false,
|
|
301
362
|
"setter": false,
|
|
@@ -305,10 +366,14 @@ export class ChatList {
|
|
|
305
366
|
}
|
|
306
367
|
};
|
|
307
368
|
}
|
|
369
|
+
static get elementRef() { return "host"; }
|
|
308
370
|
static get watchers() {
|
|
309
371
|
return [{
|
|
310
372
|
"propName": "items",
|
|
311
373
|
"methodName": "handleItemsChange"
|
|
374
|
+
}, {
|
|
375
|
+
"propName": "order",
|
|
376
|
+
"methodName": "handleItemsChange"
|
|
312
377
|
}];
|
|
313
378
|
}
|
|
314
379
|
}
|
|
@@ -318,3 +383,6 @@ function parseToUTCDate(date) {
|
|
|
318
383
|
}
|
|
319
384
|
return new Date(date); // This will automatically be UTC
|
|
320
385
|
}
|
|
386
|
+
function assertNever(value) {
|
|
387
|
+
throw new Error(`Unexpected value in exhaustive switch: ${JSON.stringify(value)}`);
|
|
388
|
+
}
|
|
@@ -10,7 +10,7 @@ function getProcessedItemTimestamp(item) {
|
|
|
10
10
|
/**
|
|
11
11
|
* Processes a list of chat list inputs by adding date headers and a "new items" indicator.
|
|
12
12
|
*
|
|
13
|
-
* @param items - The list of items to process. Pre-sorted by `timestamp` in
|
|
13
|
+
* @param items - The list of items to process. Pre-sorted by `timestamp` in ascending order.
|
|
14
14
|
* @param dateTimeFormatter - A service used to format dates for the date headers.
|
|
15
15
|
* @param lastVisitedTimestamp - The timestamp of the user's last visit. Used to position the
|
|
16
16
|
* "new items" indicator.
|
|
@@ -24,16 +24,18 @@ export function processChatItems(items, dateTimeFormatter, lastVisitedTimestamp)
|
|
|
24
24
|
processedItems: [],
|
|
25
25
|
lastVisitedTimestamp: lastVisitedTimestamp,
|
|
26
26
|
dateTimeFormatter: dateTimeFormatter,
|
|
27
|
-
newItemIndicatorInserted: false,
|
|
28
|
-
lastProcessedDate: null,
|
|
29
|
-
hasUnseenNotifications: items.some((item) => lastVisitedTimestamp &&
|
|
30
|
-
new Date(item.timestamp) > lastVisitedTimestamp),
|
|
31
27
|
};
|
|
32
|
-
// Add processors here, in the order they should be executed
|
|
28
|
+
// Add processors here, in the order they should be executed.
|
|
29
|
+
// `insertNewItemIndicator` runs after `insertDateGroups` so the
|
|
30
|
+
// indicator can be placed at top level when the seen/unseen
|
|
31
|
+
// boundary falls between two groups, and only inside a group when
|
|
32
|
+
// the boundary falls within a single day. That placement makes
|
|
33
|
+
// `reverseProcessedItems` a pure two-level reverse — no group-
|
|
34
|
+
// boundary hoisting needed.
|
|
33
35
|
const processors = [
|
|
34
36
|
insertNotifications,
|
|
35
|
-
insertNewItemIndicator,
|
|
36
37
|
insertDateGroups,
|
|
38
|
+
insertNewItemIndicator,
|
|
37
39
|
];
|
|
38
40
|
const finalContext = createPipeline(initialContext, processors);
|
|
39
41
|
return finalContext.processedItems;
|
|
@@ -42,6 +44,32 @@ function createPipeline(initialContext, processors) {
|
|
|
42
44
|
// eslint-disable-next-line unicorn/no-array-reduce
|
|
43
45
|
return processors.reduce((context, processor) => processor(context), initialContext);
|
|
44
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Reverses the chronological order of a processed-items array.
|
|
49
|
+
*
|
|
50
|
+
* Pure two-level reverse: the top-level array is reversed, and each
|
|
51
|
+
* group's items are reversed. `insertNewItemIndicator` places the
|
|
52
|
+
* indicator at top level when the seen/unseen boundary falls
|
|
53
|
+
* between groups, and only nests it inside a group when the
|
|
54
|
+
* boundary falls within a single day — so reversal never needs to
|
|
55
|
+
* move an indicator across levels to preserve its separator role.
|
|
56
|
+
*
|
|
57
|
+
* Used by `limebb-chat-list` when rendering in `newest-on-top`
|
|
58
|
+
* layout, so that DOM order continues to match visual order
|
|
59
|
+
* (newest-first in that layout), keeping copy/paste, screen-reader,
|
|
60
|
+
* and tab order aligned with what the user sees.
|
|
61
|
+
*
|
|
62
|
+
* @param items - processed items in ascending-by-timestamp order
|
|
63
|
+
* @returns processed items in descending-by-timestamp order
|
|
64
|
+
*/
|
|
65
|
+
export function reverseProcessedItems(items) {
|
|
66
|
+
return items.toReversed().map((item) => {
|
|
67
|
+
if (item.type !== 'group') {
|
|
68
|
+
return item;
|
|
69
|
+
}
|
|
70
|
+
return Object.assign(Object.assign({}, item), { items: item.items.toReversed() });
|
|
71
|
+
});
|
|
72
|
+
}
|
|
45
73
|
/**
|
|
46
74
|
*
|
|
47
75
|
* @param context
|
|
@@ -62,61 +90,99 @@ export function insertNotifications(context) {
|
|
|
62
90
|
* @param context
|
|
63
91
|
*/
|
|
64
92
|
export function insertNewItemIndicator(context) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
93
|
+
if (!context.lastVisitedTimestamp) {
|
|
94
|
+
return context;
|
|
95
|
+
}
|
|
96
|
+
const groups = context.processedItems;
|
|
97
|
+
const lastVisited = context.lastVisitedTimestamp;
|
|
98
|
+
// Walk groups newest → oldest. Items and groups are sorted
|
|
99
|
+
// ascending, so at most one group straddles the seen/unseen
|
|
100
|
+
// boundary; the rest are entirely seen or entirely unseen. In an
|
|
101
|
+
// active chat where the newest item is already seen, the loop
|
|
102
|
+
// exits in O(1).
|
|
103
|
+
for (let g = groups.length - 1; g >= 0; g--) {
|
|
104
|
+
const group = groups[g];
|
|
105
|
+
if (group.type !== 'group') {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const seenIndex = findNewestSeenIndex(group.items, lastVisited);
|
|
109
|
+
if (seenIndex === -1) {
|
|
110
|
+
continue;
|
|
76
111
|
}
|
|
77
|
-
|
|
112
|
+
return placeIndicatorAtBoundary(context, groups, g, group, seenIndex);
|
|
78
113
|
}
|
|
79
|
-
|
|
80
|
-
context.newItemIndicatorInserted = newItemIndicatorInserted;
|
|
114
|
+
// No seen item found — everything is unseen, no boundary to mark.
|
|
81
115
|
return context;
|
|
82
116
|
}
|
|
117
|
+
function findNewestSeenIndex(items, lastVisited) {
|
|
118
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
119
|
+
const timestamp = getProcessedItemTimestamp(items[i]);
|
|
120
|
+
if (timestamp === undefined) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (new Date(timestamp) <= lastVisited) {
|
|
124
|
+
return i;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return -1;
|
|
128
|
+
}
|
|
129
|
+
function placeIndicatorAtBoundary(context, groups, groupIndex, group, seenIndex) {
|
|
130
|
+
const isLastItemInGroup = seenIndex === group.items.length - 1;
|
|
131
|
+
const isLastGroup = groupIndex === groups.length - 1;
|
|
132
|
+
// Newest seen item is the very last item overall → everything is
|
|
133
|
+
// seen, nothing to mark.
|
|
134
|
+
if (isLastItemInGroup && isLastGroup) {
|
|
135
|
+
return context;
|
|
136
|
+
}
|
|
137
|
+
// Seen item is last in its group → boundary is between groups.
|
|
138
|
+
// Place the indicator at top level so reversal stays a pure
|
|
139
|
+
// two-level reverse.
|
|
140
|
+
if (isLastItemInGroup) {
|
|
141
|
+
return Object.assign(Object.assign({}, context), { processedItems: [
|
|
142
|
+
...groups.slice(0, groupIndex + 1),
|
|
143
|
+
{ type: 'newItemIndicator' },
|
|
144
|
+
...groups.slice(groupIndex + 1),
|
|
145
|
+
] });
|
|
146
|
+
}
|
|
147
|
+
// Boundary falls inside a single day — indicator stays nested.
|
|
148
|
+
const newGroupItems = [
|
|
149
|
+
...group.items.slice(0, seenIndex + 1),
|
|
150
|
+
{ type: 'newItemIndicator' },
|
|
151
|
+
...group.items.slice(seenIndex + 1),
|
|
152
|
+
];
|
|
153
|
+
return Object.assign(Object.assign({}, context), { processedItems: [
|
|
154
|
+
...groups.slice(0, groupIndex),
|
|
155
|
+
Object.assign(Object.assign({}, group), { items: newGroupItems }),
|
|
156
|
+
...groups.slice(groupIndex + 1),
|
|
157
|
+
] });
|
|
158
|
+
}
|
|
83
159
|
/**
|
|
84
160
|
*
|
|
85
161
|
* @param context
|
|
86
162
|
*/
|
|
87
|
-
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
88
163
|
export function insertDateGroups(context) {
|
|
89
164
|
const newProcessedItems = [];
|
|
90
165
|
let currentGroup = null;
|
|
91
166
|
let lastProcessedDate = null;
|
|
92
167
|
for (const item of context.processedItems) {
|
|
93
|
-
if (item.type
|
|
94
|
-
|
|
95
|
-
const currentDate = context.dateTimeFormatter.format(currentTimestamp, 'date');
|
|
96
|
-
if (currentDate !== lastProcessedDate) {
|
|
97
|
-
if (currentGroup) {
|
|
98
|
-
newProcessedItems.push(currentGroup);
|
|
99
|
-
}
|
|
100
|
-
currentGroup = {
|
|
101
|
-
type: 'group',
|
|
102
|
-
groupType: 'dateGroup',
|
|
103
|
-
heading: currentDate,
|
|
104
|
-
items: [],
|
|
105
|
-
};
|
|
106
|
-
lastProcessedDate = currentDate;
|
|
107
|
-
}
|
|
108
|
-
if (currentGroup) {
|
|
109
|
-
currentGroup.items.push(item);
|
|
110
|
-
}
|
|
168
|
+
if (item.type !== 'chat' && item.type !== 'iconList') {
|
|
169
|
+
continue;
|
|
111
170
|
}
|
|
112
|
-
|
|
171
|
+
const currentDate = context.dateTimeFormatter.format(new Date(item.item.timestamp), 'date');
|
|
172
|
+
if (currentDate !== lastProcessedDate) {
|
|
113
173
|
if (currentGroup) {
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
newProcessedItems.push(item);
|
|
174
|
+
newProcessedItems.push(currentGroup);
|
|
118
175
|
}
|
|
176
|
+
currentGroup = {
|
|
177
|
+
type: 'group',
|
|
178
|
+
groupType: 'dateGroup',
|
|
179
|
+
heading: currentDate,
|
|
180
|
+
items: [item],
|
|
181
|
+
};
|
|
182
|
+
lastProcessedDate = currentDate;
|
|
183
|
+
continue;
|
|
119
184
|
}
|
|
185
|
+
currentGroup === null || currentGroup === void 0 ? void 0 : currentGroup.items.push(item);
|
|
120
186
|
}
|
|
121
187
|
if (currentGroup) {
|
|
122
188
|
newProcessedItems.push(currentGroup);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// "Near enough the newest edge to count as sticky." 50px is roughly
|
|
2
|
+
// two message-bubble margins; a user who is within that distance
|
|
3
|
+
// clearly wasn't scrolling back through history, so we treat their
|
|
4
|
+
// position as "stuck to newest" and re-pin on each items update.
|
|
5
|
+
const NEAR_NEWEST_THRESHOLD_PX = 50;
|
|
6
|
+
/**
|
|
7
|
+
* Keeps a scroll container stuck to its "newest" edge as items are
|
|
8
|
+
* appended, so long as the user hasn't scrolled away.
|
|
9
|
+
*
|
|
10
|
+
* We use imperative scrolling rather than `flex-direction: column-reverse`
|
|
11
|
+
* so DOM order matches visual order (copy/paste, screen readers, tab
|
|
12
|
+
* order all align with what the user sees). CSS `overflow-anchor: auto`
|
|
13
|
+
* — the default and left enabled here — solves a different problem:
|
|
14
|
+
* preserving scroll position when content is inserted *above* the
|
|
15
|
+
* viewport. It doesn't stick the scroll to the newest edge when items
|
|
16
|
+
* arrive below the viewport, which is what chat typically needs; that
|
|
17
|
+
* requires the imperative approach in this class.
|
|
18
|
+
*
|
|
19
|
+
* Lifecycle:
|
|
20
|
+
* - `beforeRender()` in `componentWillRender` — snapshot whether the
|
|
21
|
+
* user is near the newest edge, using the order the scroll position
|
|
22
|
+
* was last measured against. Capturing here (synchronously, before
|
|
23
|
+
* the new DOM lands) avoids the race a scroll-event listener would
|
|
24
|
+
* have with `componentDidRender`.
|
|
25
|
+
* - `afterRender()` in `componentDidRender` — if the snapshot said
|
|
26
|
+
* "near newest", re-pin to the newest edge (new `scrollHeight`).
|
|
27
|
+
* - `repinIfSticky()` for out-of-band re-pins (e.g. after the typing
|
|
28
|
+
* indicator's height transition finishes — its `scrollHeight`
|
|
29
|
+
* change lands after `componentDidRender`).
|
|
30
|
+
*
|
|
31
|
+
* Scroll-container contract: the passed element must itself be the
|
|
32
|
+
* scrolling ancestor (the element whose `scrollTop` / `scrollHeight`
|
|
33
|
+
* / `clientHeight` reflect the chat list's scroll state). In practice
|
|
34
|
+
* that is the component's host, which sets `overflow: hidden auto` on
|
|
35
|
+
* itself. If future refactors introduce an inner scrolling wrapper,
|
|
36
|
+
* pass that wrapper instead.
|
|
37
|
+
*/
|
|
38
|
+
export class ScrollAnchor {
|
|
39
|
+
constructor(scrollContainer, getOrder, initialOrder) {
|
|
40
|
+
this.scrollContainer = scrollContainer;
|
|
41
|
+
this.getOrder = getOrder;
|
|
42
|
+
this.isSticky = true;
|
|
43
|
+
this.paintedOrder = initialOrder;
|
|
44
|
+
}
|
|
45
|
+
beforeRender() {
|
|
46
|
+
if (!this.scrollContainer.isConnected) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// Measure against the order the scroll position was last painted
|
|
50
|
+
// with. If `order` just changed, the scroll position still refers
|
|
51
|
+
// to the previous layout until this render commits.
|
|
52
|
+
this.isSticky =
|
|
53
|
+
this.distanceFromNewestEdge(this.paintedOrder) <
|
|
54
|
+
NEAR_NEWEST_THRESHOLD_PX;
|
|
55
|
+
}
|
|
56
|
+
afterRender() {
|
|
57
|
+
if (!this.scrollContainer.isConnected) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
this.paintedOrder = this.getOrder();
|
|
61
|
+
if (this.isSticky) {
|
|
62
|
+
this.scrollToNewest();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
repinIfSticky() {
|
|
66
|
+
if (!this.scrollContainer.isConnected) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (this.isSticky) {
|
|
70
|
+
this.scrollToNewest();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
scrollToNewest() {
|
|
74
|
+
const target = this.getOrder() === 'oldest-on-top'
|
|
75
|
+
? this.scrollContainer.scrollHeight
|
|
76
|
+
: 0;
|
|
77
|
+
// Reading `scrollTop` is a cheap (already-measured) lookup, and
|
|
78
|
+
// short-circuiting avoids forcing a layout when we'd write the
|
|
79
|
+
// same value — important under high-frequency re-renders
|
|
80
|
+
// (streaming AI output).
|
|
81
|
+
if (this.scrollContainer.scrollTop === target) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
this.scrollContainer.scrollTop = target;
|
|
85
|
+
}
|
|
86
|
+
distanceFromNewestEdge(order) {
|
|
87
|
+
return order === 'oldest-on-top'
|
|
88
|
+
? this.scrollContainer.scrollHeight -
|
|
89
|
+
this.scrollContainer.scrollTop -
|
|
90
|
+
this.scrollContainer.clientHeight
|
|
91
|
+
: this.scrollContainer.scrollTop;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -7,7 +7,7 @@ import { h } from "@stencil/core";
|
|
|
7
7
|
*/
|
|
8
8
|
export class TypingIndicator {
|
|
9
9
|
render() {
|
|
10
|
-
return (h("div", { key: '
|
|
10
|
+
return (h("div", { key: 'da6a23e31e0d54dd99f45de9f6ef27ed5562a96b', class: "typing-ellipsis" }, h("div", { key: 'd4f1af82536b37640041525ef6f448a87819d220', class: "circle" }), h("div", { key: '3523c0242ee91eeec62b95ca932cff67ee82e8e6', class: "circle" }), h("div", { key: 'ac2c1b8e9180776961a8ab0bd96f1ed219bb4d5f', class: "circle" })));
|
|
11
11
|
}
|
|
12
12
|
static get is() { return "limebb-typing-indicator"; }
|
|
13
13
|
static get encapsulation() { return "shadow"; }
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// The CSS property the typing-indicator container transitions.
|
|
2
|
+
// Mirrored from chat-list.scss `.typing-indicator-height-animation
|
|
3
|
+
// { transition: grid-template-rows ... }`. The runtime verifier below
|
|
4
|
+
// guards against the SCSS and the handler-side filter silently
|
|
5
|
+
// disagreeing.
|
|
6
|
+
export const HEIGHT_ANIMATION_PROPERTY = 'grid-template-rows';
|
|
7
|
+
/**
|
|
8
|
+
* Returns a `transitionend` listener for the typing indicator's
|
|
9
|
+
* height transition. Filters by property name so unrelated
|
|
10
|
+
* transitions on the same element (e.g. opacity) don't trigger the
|
|
11
|
+
* callback.
|
|
12
|
+
* @param onHeightTransitionEnd
|
|
13
|
+
*/
|
|
14
|
+
export function createHeightTransitionEndHandler(onHeightTransitionEnd) {
|
|
15
|
+
return (event) => {
|
|
16
|
+
if (event.propertyName !== HEIGHT_ANIMATION_PROPERTY) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
onHeightTransitionEnd();
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Verifies that the passed element actually transitions on the
|
|
24
|
+
* property the height-transition handler filters on. Surfaces SCSS
|
|
25
|
+
* drift loudly instead of silently breaking auto-scroll-on-indicator-
|
|
26
|
+
* appearance.
|
|
27
|
+
* @param element
|
|
28
|
+
*/
|
|
29
|
+
export function verifyHeightAnimationProperty(element) {
|
|
30
|
+
if (!element) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const transitionProperty = globalThis.getComputedStyle(element).transitionProperty;
|
|
34
|
+
if (!transitionProperty) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const properties = new Set(transitionProperty.split(',').map((p) => p.trim()));
|
|
38
|
+
if (!properties.has('all') && !properties.has(HEIGHT_ANIMATION_PROPERTY)) {
|
|
39
|
+
console.warn(`[limebb-chat-list] Typing indicator is transitioning \`${transitionProperty}\` instead of \`${HEIGHT_ANIMATION_PROPERTY}\`. The auto-scroll on indicator appearance will not fire. Update the CSS or the handler so they match.`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -83,8 +83,8 @@ export class ComposerToolbar {
|
|
|
83
83
|
}
|
|
84
84
|
render() {
|
|
85
85
|
return [
|
|
86
|
-
h("div", { key: '
|
|
87
|
-
h("slot", { key: '
|
|
86
|
+
h("div", { key: '6034b2945edfd9b613f606f8bb4fb0db81c1390f', class: "actions" }, h("slot", { key: '82b9bc14d80aedd3320af70bc63d3a908db6a7b2', name: "leading" }), this.renderFileInput(), this.renderTriggerActions(), h("slot", { key: '338fc1b7c7ec3f7dfedb123b7e09dd05f75fc45c', name: "trailing" })),
|
|
87
|
+
h("slot", { key: '57b277b772cbaf702e619ecbd6ff9032718b87b7', name: "primary-action" }),
|
|
88
88
|
];
|
|
89
89
|
}
|
|
90
90
|
get translator() {
|
|
@@ -84,7 +84,7 @@ export class CurrencyPicker {
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
render() {
|
|
87
|
-
return (h("limel-picker", { key: '
|
|
87
|
+
return (h("limel-picker", { key: '12d166b930a2864eefbb16c4d66706dd02b54112', label: this.label || this.defaultLabel, value: this.createListItem(this.value), helperText: this.helperText, onChange: this.handleChange, badgeIcons: false, disabled: this.disabled, required: this.required, readonly: this.readonly, invalid: this.readonly, searcher: this.search }));
|
|
88
88
|
}
|
|
89
89
|
getCurrencyName(code) {
|
|
90
90
|
var _a;
|
|
@@ -17,7 +17,7 @@ export class DashboardComponent {
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
render() {
|
|
20
|
-
return [this.renderHeader(), h("slot", { key: '
|
|
20
|
+
return [this.renderHeader(), h("slot", { key: 'f5202c8c5d6ceb3e01420c6045ecb5589fed915a', name: "content" })];
|
|
21
21
|
}
|
|
22
22
|
static get is() { return "limebb-dashboard-widget"; }
|
|
23
23
|
static get encapsulation() { return "shadow"; }
|
|
@@ -66,7 +66,7 @@ export class DatePicker {
|
|
|
66
66
|
if (this.value) {
|
|
67
67
|
this.shouldEmitValueAsString = typeof this.value === 'string';
|
|
68
68
|
}
|
|
69
|
-
return (h("limel-date-picker", { key: '
|
|
69
|
+
return (h("limel-date-picker", { key: 'f3276cfc57128c566248a97e5fea9a3dcc107888', disabled: this.disabled, readonly: this.readonly, invalid: this.invalid, label: this.label, placeholder: this.placeholder, helperText: this.helperText, required: this.required, value: this.parseDateValue(), type: this.type, language: this.language, formatter: this.formatter, onChange: this.handleChange }));
|
|
70
70
|
}
|
|
71
71
|
parseDateValue() {
|
|
72
72
|
const value = this.value;
|
|
@@ -33,7 +33,7 @@ export class DateRange {
|
|
|
33
33
|
this.originalEndTime = this.endTime;
|
|
34
34
|
}
|
|
35
35
|
render() {
|
|
36
|
-
return (h("div", { key: '
|
|
36
|
+
return (h("div", { key: 'e00abfd31fcae2a4279ae2194696b5cffb76dd79', class: "date-pickers" }, this.renderStartDatePicker(), this.renderEndDatePicker()));
|
|
37
37
|
}
|
|
38
38
|
renderStartDatePicker() {
|
|
39
39
|
if (!this.originalStartTime) {
|
|
@@ -44,7 +44,7 @@ export class DocumentChips {
|
|
|
44
44
|
this.revokeAllBlobUrls();
|
|
45
45
|
}
|
|
46
46
|
render() {
|
|
47
|
-
return (h(Host, { key: '
|
|
47
|
+
return (h(Host, { key: '93d00770917014b1de6e380db6b102859210fded', role: "list", "aria-label": this.accessibleLabel }, this.files.map((file) => this.renderFile(file))));
|
|
48
48
|
}
|
|
49
49
|
renderFile(file) {
|
|
50
50
|
const isReady = !file.uploadState || file.uploadState === 'done';
|
|
@@ -78,10 +78,10 @@ export class DocumentItemComponent {
|
|
|
78
78
|
render() {
|
|
79
79
|
const isUnavailable = this.isUnavailable();
|
|
80
80
|
const image = this.getImage();
|
|
81
|
-
return (h(Host, { key: '
|
|
81
|
+
return (h(Host, { key: '97fe779b6706627e3abb9729b6ea2933d5ccafb4', id: String(this.item.file.id), class: {
|
|
82
82
|
'has-image': !!image,
|
|
83
83
|
'has-error': !!this.item.hasError,
|
|
84
|
-
}, role: this.type === 'radio' ? 'radio' : 'checkbox', "aria-checked": this.item.selected ? 'true' : 'false', "aria-disabled": isUnavailable ? 'true' : 'false', tabIndex: isUnavailable ? -1 : 0 }, h("limel-card", { key: '
|
|
84
|
+
}, role: this.type === 'radio' ? 'radio' : 'checkbox', "aria-checked": this.item.selected ? 'true' : 'false', "aria-disabled": isUnavailable ? 'true' : 'false', tabIndex: isUnavailable ? -1 : 0 }, h("limel-card", { key: 'c897ca8a474b6b2e2f9e722507dd4b038ece058b', image: image, clickable: isUnavailable ? false : true, onClick: this.handleDocumentItemClick, onKeyDown: this.handleKeyDown }, h("div", { key: 'cc75606770a2e0edeab4111e6e04608f6d841b6e', slot: "component" }, this.renderFileSize(), this.renderBooleanInput())), this.renderHelp()));
|
|
85
85
|
}
|
|
86
86
|
renderBooleanInput() {
|
|
87
87
|
const id = this.getControlId();
|
|
@@ -76,7 +76,7 @@ export class DocumentPicker {
|
|
|
76
76
|
this.labelId = createRandomString();
|
|
77
77
|
}
|
|
78
78
|
render() {
|
|
79
|
-
return (h(Host, { key: '
|
|
79
|
+
return (h(Host, { key: 'd3d8d080de7dd9ceec2cbfa7382e9ac669c2d35b' }, h("limel-notched-outline", { key: '978b42774a036eccb8f347231a98b495e7b520a0', labelId: this.labelId, label: this.label, required: this.required, invalid: this.invalid, hasFloatingLabel: true }, h("limel-masonry-layout", { key: '5aa49ca5764d4b25dae50754ab77498beeaf016c', slot: "content", role: this.type === 'radio' ? 'radiogroup' : 'group', "aria-labelledby": this.label ? this.labelId : undefined, "aria-describedby": this.helperText ? this.helperTextId : undefined }, this.renderItems())), this.renderHelperLine()));
|
|
80
80
|
}
|
|
81
81
|
renderItems() {
|
|
82
82
|
return this.items.map((item) => (h("limebb-document-item", { platform: this.platform, context: this.context, item: item, key: item.file.id, type: this.type, onInteract: this.handleItemInteract })));
|