@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.
Files changed (200) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cjs/lime-crm-building-blocks.cjs.js +1 -1
  3. package/dist/cjs/limebb-alert-dialog.cjs.entry.js +1 -1
  4. package/dist/cjs/limebb-browser.cjs.entry.js +2 -2
  5. package/dist/cjs/limebb-chat-icon-list_3.cjs.entry.js +4 -4
  6. package/dist/cjs/limebb-chat-list.cjs.entry.js +336 -68
  7. package/dist/cjs/limebb-composer-toolbar.cjs.entry.js +2 -2
  8. package/dist/cjs/limebb-currency-picker.cjs.entry.js +1 -1
  9. package/dist/cjs/limebb-dashboard-widget.cjs.entry.js +1 -1
  10. package/dist/cjs/limebb-date-picker.cjs.entry.js +1 -1
  11. package/dist/cjs/limebb-date-range.cjs.entry.js +1 -1
  12. package/dist/cjs/limebb-document-chips.cjs.entry.js +1 -1
  13. package/dist/cjs/limebb-document-item.cjs.entry.js +2 -2
  14. package/dist/cjs/limebb-document-picker.cjs.entry.js +1 -1
  15. package/dist/cjs/limebb-feed-item-thumbnail-file-info.cjs.entry.js +1 -1
  16. package/dist/cjs/limebb-feed-timeline-item.cjs.entry.js +1 -1
  17. package/dist/cjs/limebb-feed.cjs.entry.js +1 -1
  18. package/dist/cjs/limebb-icon-picker.cjs.entry.js +1 -1
  19. package/dist/cjs/limebb-info-tile-format.cjs.entry.js +1 -1
  20. package/dist/cjs/limebb-info-tile.cjs.entry.js +1 -1
  21. package/dist/cjs/limebb-kanban-group.cjs.entry.js +1 -1
  22. package/dist/cjs/limebb-kanban-item.cjs.entry.js +1 -1
  23. package/dist/cjs/limebb-lime-query-builder.cjs.entry.js +1 -1
  24. package/dist/cjs/limebb-lime-query-filter-builder_3.cjs.entry.js +2 -2
  25. package/dist/cjs/limebb-lime-query-filter-comparison_2.cjs.entry.js +1 -1
  26. package/dist/cjs/limebb-lime-query-filter-group_3.cjs.entry.js +3 -3
  27. package/dist/cjs/limebb-lime-query-order-by-item.cjs.entry.js +2 -2
  28. package/dist/cjs/limebb-lime-query-response-format-builder.cjs.entry.js +1 -1
  29. package/dist/cjs/limebb-lime-query-response-format-editor_2.cjs.entry.js +1 -1
  30. package/dist/cjs/limebb-live-docs-info.cjs.entry.js +2 -2
  31. package/dist/cjs/limebb-locale-picker.cjs.entry.js +1 -1
  32. package/dist/cjs/limebb-mention-group-counter.cjs.entry.js +2 -2
  33. package/dist/cjs/limebb-navigation-button_2.cjs.entry.js +2 -2
  34. package/dist/cjs/limebb-notification-item.cjs.entry.js +1 -1
  35. package/dist/cjs/limebb-percentage-visualizer.cjs.entry.js +2 -2
  36. package/dist/cjs/limebb-text-editor.cjs.entry.js +1 -1
  37. package/dist/cjs/limebb-trend-indicator.cjs.entry.js +1 -1
  38. package/dist/cjs/loader.cjs.js +1 -1
  39. package/dist/collection/components/alert-dialog/alert-dialog.js +1 -1
  40. package/dist/collection/components/browser/browser.js +2 -2
  41. package/dist/collection/components/chat-list/chat-icon-list/chat-icon-list.js +1 -1
  42. package/dist/collection/components/chat-list/chat-item/chat-item.js +2 -2
  43. package/dist/collection/components/chat-list/chat-list.css +10 -10
  44. package/dist/collection/components/chat-list/chat-list.js +93 -25
  45. package/dist/collection/components/chat-list/process-chat-items.js +111 -45
  46. package/dist/collection/components/chat-list/scroll-anchor.js +93 -0
  47. package/dist/collection/components/chat-list/typing-indicator/typing-indicator.js +1 -1
  48. package/dist/collection/components/chat-list/typing-indicator-scroll.js +41 -0
  49. package/dist/collection/components/composer-toolbar/composer-toolbar.js +2 -2
  50. package/dist/collection/components/currency-picker/currency-picker.js +1 -1
  51. package/dist/collection/components/dashboard-widget/dashboard-widget.js +1 -1
  52. package/dist/collection/components/date-picker/date-picker.js +1 -1
  53. package/dist/collection/components/date-range/date-range.js +1 -1
  54. package/dist/collection/components/document-chips/document-chips.js +1 -1
  55. package/dist/collection/components/document-picker/document-item/document-item.js +2 -2
  56. package/dist/collection/components/document-picker/document-picker.js +1 -1
  57. package/dist/collection/components/feed/feed-item/feed-timeline-item.js +1 -1
  58. package/dist/collection/components/feed/feed-item-thumbnail-file-info/feed-item-thumbnail-file-info.js +1 -1
  59. package/dist/collection/components/feed/feed.js +1 -1
  60. package/dist/collection/components/icon-picker/icon-picker.js +1 -1
  61. package/dist/collection/components/info-tile/format/config/info-tile-format.js +1 -1
  62. package/dist/collection/components/info-tile/info-tile.js +1 -1
  63. package/dist/collection/components/kanban/kanban-group/kanban-group.js +1 -1
  64. package/dist/collection/components/kanban/kanban-item/kanban-item.js +1 -1
  65. package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-comparison.js +1 -1
  66. package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-group.js +2 -2
  67. package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-not.js +1 -1
  68. package/dist/collection/components/lime-query-builder/lime-query-builder.js +1 -1
  69. package/dist/collection/components/lime-query-builder/lime-query-response-format-builder.js +1 -1
  70. package/dist/collection/components/lime-query-builder/limetype-field/limetype-field.js +1 -1
  71. package/dist/collection/components/lime-query-builder/order-by/order-by-editor.js +1 -1
  72. package/dist/collection/components/lime-query-builder/order-by/order-by-item.js +2 -2
  73. package/dist/collection/components/lime-query-builder/response-format/response-format-item.js +1 -1
  74. package/dist/collection/components/limeobject/file-viewer/live-docs-info.js +2 -2
  75. package/dist/collection/components/locale-picker/locale-picker.js +1 -1
  76. package/dist/collection/components/notification-list/notification-item/notification-item.js +1 -1
  77. package/dist/collection/components/percentage-visualizer/percentage-visualizer.js +2 -2
  78. package/dist/collection/components/summary-popover/summary-popover.js +2 -2
  79. package/dist/collection/components/text-editor/mention-group-counter.js +2 -2
  80. package/dist/collection/components/text-editor/text-editor.js +1 -1
  81. package/dist/collection/components/trend-indicator/trend-indicator.js +1 -1
  82. package/dist/components/chat-icon-list.js +1 -1
  83. package/dist/components/chat-item.js +1 -1
  84. package/dist/components/currency-picker.js +1 -1
  85. package/dist/components/date-picker.js +1 -1
  86. package/dist/components/document-item.js +1 -1
  87. package/dist/components/feed-item-thumbnail-file-info.js +1 -1
  88. package/dist/components/feed-timeline-item.js +1 -1
  89. package/dist/components/kanban-group.js +1 -1
  90. package/dist/components/kanban-item.js +1 -1
  91. package/dist/components/lime-query-filter-comparison.js +1 -1
  92. package/dist/components/lime-query-filter-expression.js +1 -1
  93. package/dist/components/limebb-alert-dialog.js +1 -1
  94. package/dist/components/limebb-browser.js +1 -1
  95. package/dist/components/limebb-chat-list.js +1 -1
  96. package/dist/components/limebb-composer-toolbar.js +1 -1
  97. package/dist/components/limebb-dashboard-widget.js +1 -1
  98. package/dist/components/limebb-date-range.js +1 -1
  99. package/dist/components/limebb-document-chips.js +1 -1
  100. package/dist/components/limebb-document-picker.js +1 -1
  101. package/dist/components/limebb-feed.js +1 -1
  102. package/dist/components/limebb-icon-picker.js +1 -1
  103. package/dist/components/limebb-info-tile-format.js +1 -1
  104. package/dist/components/limebb-info-tile.js +1 -1
  105. package/dist/components/limebb-lime-query-builder.js +1 -1
  106. package/dist/components/limebb-lime-query-response-format-builder.js +1 -1
  107. package/dist/components/limebb-locale-picker.js +1 -1
  108. package/dist/components/limebb-mention-group-counter.js +1 -1
  109. package/dist/components/limebb-text-editor.js +1 -1
  110. package/dist/components/limebb-trend-indicator.js +1 -1
  111. package/dist/components/limetype-field.js +1 -1
  112. package/dist/components/live-docs-info.js +1 -1
  113. package/dist/components/notification-item.js +1 -1
  114. package/dist/components/order-by-editor.js +1 -1
  115. package/dist/components/order-by-item.js +1 -1
  116. package/dist/components/percentage-visualizer.js +1 -1
  117. package/dist/components/response-format-item.js +1 -1
  118. package/dist/components/summary-popover.js +1 -1
  119. package/dist/components/typing-indicator.js +1 -1
  120. package/dist/esm/lime-crm-building-blocks.js +1 -1
  121. package/dist/esm/limebb-alert-dialog.entry.js +1 -1
  122. package/dist/esm/limebb-browser.entry.js +2 -2
  123. package/dist/esm/limebb-chat-icon-list_3.entry.js +4 -4
  124. package/dist/esm/limebb-chat-list.entry.js +337 -69
  125. package/dist/esm/limebb-composer-toolbar.entry.js +2 -2
  126. package/dist/esm/limebb-currency-picker.entry.js +1 -1
  127. package/dist/esm/limebb-dashboard-widget.entry.js +1 -1
  128. package/dist/esm/limebb-date-picker.entry.js +1 -1
  129. package/dist/esm/limebb-date-range.entry.js +1 -1
  130. package/dist/esm/limebb-document-chips.entry.js +1 -1
  131. package/dist/esm/limebb-document-item.entry.js +2 -2
  132. package/dist/esm/limebb-document-picker.entry.js +1 -1
  133. package/dist/esm/limebb-feed-item-thumbnail-file-info.entry.js +1 -1
  134. package/dist/esm/limebb-feed-timeline-item.entry.js +1 -1
  135. package/dist/esm/limebb-feed.entry.js +1 -1
  136. package/dist/esm/limebb-icon-picker.entry.js +1 -1
  137. package/dist/esm/limebb-info-tile-format.entry.js +1 -1
  138. package/dist/esm/limebb-info-tile.entry.js +1 -1
  139. package/dist/esm/limebb-kanban-group.entry.js +1 -1
  140. package/dist/esm/limebb-kanban-item.entry.js +1 -1
  141. package/dist/esm/limebb-lime-query-builder.entry.js +1 -1
  142. package/dist/esm/limebb-lime-query-filter-builder_3.entry.js +2 -2
  143. package/dist/esm/limebb-lime-query-filter-comparison_2.entry.js +1 -1
  144. package/dist/esm/limebb-lime-query-filter-group_3.entry.js +3 -3
  145. package/dist/esm/limebb-lime-query-order-by-item.entry.js +2 -2
  146. package/dist/esm/limebb-lime-query-response-format-builder.entry.js +1 -1
  147. package/dist/esm/limebb-lime-query-response-format-editor_2.entry.js +1 -1
  148. package/dist/esm/limebb-live-docs-info.entry.js +2 -2
  149. package/dist/esm/limebb-locale-picker.entry.js +1 -1
  150. package/dist/esm/limebb-mention-group-counter.entry.js +2 -2
  151. package/dist/esm/limebb-navigation-button_2.entry.js +2 -2
  152. package/dist/esm/limebb-notification-item.entry.js +1 -1
  153. package/dist/esm/limebb-percentage-visualizer.entry.js +2 -2
  154. package/dist/esm/limebb-text-editor.entry.js +1 -1
  155. package/dist/esm/limebb-trend-indicator.entry.js +1 -1
  156. package/dist/esm/loader.js +1 -1
  157. package/dist/lime-crm-building-blocks/lime-crm-building-blocks.esm.js +1 -1
  158. package/dist/lime-crm-building-blocks/{p-685dd8a1.entry.js → p-02a3530f.entry.js} +1 -1
  159. package/dist/lime-crm-building-blocks/{p-52fb2a66.entry.js → p-16ad65dc.entry.js} +1 -1
  160. package/dist/lime-crm-building-blocks/{p-6cb2d9dd.entry.js → p-2079ae59.entry.js} +1 -1
  161. package/dist/lime-crm-building-blocks/{p-c3b13800.entry.js → p-2aae332a.entry.js} +1 -1
  162. package/dist/lime-crm-building-blocks/{p-b3ee6683.entry.js → p-4edfa72a.entry.js} +1 -1
  163. package/dist/lime-crm-building-blocks/{p-b36ad4e8.entry.js → p-5058bc60.entry.js} +1 -1
  164. package/dist/lime-crm-building-blocks/{p-8477cb5d.entry.js → p-52fd0169.entry.js} +1 -1
  165. package/dist/lime-crm-building-blocks/{p-21ec697f.entry.js → p-54ae7814.entry.js} +1 -1
  166. package/dist/lime-crm-building-blocks/{p-35a51259.entry.js → p-54bb577b.entry.js} +1 -1
  167. package/dist/lime-crm-building-blocks/p-59b992b3.entry.js +1 -0
  168. package/dist/lime-crm-building-blocks/{p-1a61674f.entry.js → p-5ef41ad0.entry.js} +1 -1
  169. package/dist/lime-crm-building-blocks/{p-f375d69a.entry.js → p-640c41a3.entry.js} +1 -1
  170. package/dist/lime-crm-building-blocks/{p-528f3635.entry.js → p-65485470.entry.js} +1 -1
  171. package/dist/lime-crm-building-blocks/{p-4ddd75bc.entry.js → p-655968ce.entry.js} +1 -1
  172. package/dist/lime-crm-building-blocks/{p-8a05a0e5.entry.js → p-67551f9d.entry.js} +1 -1
  173. package/dist/lime-crm-building-blocks/{p-152edb10.entry.js → p-750dbb18.entry.js} +1 -1
  174. package/dist/lime-crm-building-blocks/{p-bac3d599.entry.js → p-83e93ebd.entry.js} +1 -1
  175. package/dist/lime-crm-building-blocks/{p-fa41a9e6.entry.js → p-8a820ede.entry.js} +1 -1
  176. package/dist/lime-crm-building-blocks/p-8da95b3a.entry.js +1 -0
  177. package/dist/lime-crm-building-blocks/{p-943c8589.entry.js → p-999c1628.entry.js} +1 -1
  178. package/dist/lime-crm-building-blocks/{p-62437a1d.entry.js → p-aea85d91.entry.js} +1 -1
  179. package/dist/lime-crm-building-blocks/{p-9ae55a96.entry.js → p-b17327da.entry.js} +1 -1
  180. package/dist/lime-crm-building-blocks/{p-71191041.entry.js → p-b3db6a41.entry.js} +1 -1
  181. package/dist/lime-crm-building-blocks/{p-6fdee70a.entry.js → p-b6895996.entry.js} +1 -1
  182. package/dist/lime-crm-building-blocks/{p-cff9bccd.entry.js → p-b92eedb5.entry.js} +1 -1
  183. package/dist/lime-crm-building-blocks/{p-aaeecca6.entry.js → p-c019f7f2.entry.js} +1 -1
  184. package/dist/lime-crm-building-blocks/{p-fdf269f7.entry.js → p-c3648986.entry.js} +1 -1
  185. package/dist/lime-crm-building-blocks/{p-ebe6040f.entry.js → p-cc07285b.entry.js} +1 -1
  186. package/dist/lime-crm-building-blocks/{p-ffb847d0.entry.js → p-cee4e6a6.entry.js} +1 -1
  187. package/dist/lime-crm-building-blocks/{p-18fc1a06.entry.js → p-d153e06d.entry.js} +1 -1
  188. package/dist/lime-crm-building-blocks/{p-be8dc8ae.entry.js → p-e33feb37.entry.js} +1 -1
  189. package/dist/lime-crm-building-blocks/{p-fcedbc77.entry.js → p-e4005988.entry.js} +1 -1
  190. package/dist/lime-crm-building-blocks/{p-a48a6ff8.entry.js → p-e8a0bbb8.entry.js} +1 -1
  191. package/dist/lime-crm-building-blocks/{p-eec0a0c8.entry.js → p-ebeddc29.entry.js} +1 -1
  192. package/dist/lime-crm-building-blocks/{p-5a1f0a2b.entry.js → p-fa808d3a.entry.js} +1 -1
  193. package/dist/types/components/chat-list/chat-list.d.ts +13 -3
  194. package/dist/types/components/chat-list/process-chat-items.d.ts +20 -4
  195. package/dist/types/components/chat-list/scroll-anchor.d.ts +46 -0
  196. package/dist/types/components/chat-list/typing-indicator-scroll.d.ts +18 -0
  197. package/dist/types/components.d.ts +4 -4
  198. package/package.json +1 -1
  199. package/dist/lime-crm-building-blocks/p-b10faec5.entry.js +0 -1
  200. package/dist/lime-crm-building-blocks/p-f23ea0e7.entry.js +0 -1
@@ -1,4 +1,4 @@
1
- import { r as registerInstance, h, H as Host } from './index-BIwHMk6j.js';
1
+ import { r as registerInstance, h, H as Host, g as getElement } from './index-BIwHMk6j.js';
2
2
  import { n } from './index.esm-BjlE4usG.js';
3
3
 
4
4
  function isIconList(item) {
@@ -13,7 +13,7 @@ function getProcessedItemTimestamp(item) {
13
13
  /**
14
14
  * Processes a list of chat list inputs by adding date headers and a "new items" indicator.
15
15
  *
16
- * @param items - The list of items to process. Pre-sorted by `timestamp` in descending order.
16
+ * @param items - The list of items to process. Pre-sorted by `timestamp` in ascending order.
17
17
  * @param dateTimeFormatter - A service used to format dates for the date headers.
18
18
  * @param lastVisitedTimestamp - The timestamp of the user's last visit. Used to position the
19
19
  * "new items" indicator.
@@ -27,16 +27,18 @@ function processChatItems(items, dateTimeFormatter, lastVisitedTimestamp) {
27
27
  processedItems: [],
28
28
  lastVisitedTimestamp: lastVisitedTimestamp,
29
29
  dateTimeFormatter: dateTimeFormatter,
30
- newItemIndicatorInserted: false,
31
- lastProcessedDate: null,
32
- hasUnseenNotifications: items.some((item) => lastVisitedTimestamp &&
33
- new Date(item.timestamp) > lastVisitedTimestamp),
34
30
  };
35
- // Add processors here, in the order they should be executed
31
+ // Add processors here, in the order they should be executed.
32
+ // `insertNewItemIndicator` runs after `insertDateGroups` so the
33
+ // indicator can be placed at top level when the seen/unseen
34
+ // boundary falls between two groups, and only inside a group when
35
+ // the boundary falls within a single day. That placement makes
36
+ // `reverseProcessedItems` a pure two-level reverse — no group-
37
+ // boundary hoisting needed.
36
38
  const processors = [
37
39
  insertNotifications,
38
- insertNewItemIndicator,
39
40
  insertDateGroups,
41
+ insertNewItemIndicator,
40
42
  ];
41
43
  const finalContext = createPipeline(initialContext, processors);
42
44
  return finalContext.processedItems;
@@ -45,6 +47,32 @@ function createPipeline(initialContext, processors) {
45
47
  // eslint-disable-next-line unicorn/no-array-reduce
46
48
  return processors.reduce((context, processor) => processor(context), initialContext);
47
49
  }
50
+ /**
51
+ * Reverses the chronological order of a processed-items array.
52
+ *
53
+ * Pure two-level reverse: the top-level array is reversed, and each
54
+ * group's items are reversed. `insertNewItemIndicator` places the
55
+ * indicator at top level when the seen/unseen boundary falls
56
+ * between groups, and only nests it inside a group when the
57
+ * boundary falls within a single day — so reversal never needs to
58
+ * move an indicator across levels to preserve its separator role.
59
+ *
60
+ * Used by `limebb-chat-list` when rendering in `newest-on-top`
61
+ * layout, so that DOM order continues to match visual order
62
+ * (newest-first in that layout), keeping copy/paste, screen-reader,
63
+ * and tab order aligned with what the user sees.
64
+ *
65
+ * @param items - processed items in ascending-by-timestamp order
66
+ * @returns processed items in descending-by-timestamp order
67
+ */
68
+ function reverseProcessedItems(items) {
69
+ return items.toReversed().map((item) => {
70
+ if (item.type !== 'group') {
71
+ return item;
72
+ }
73
+ return Object.assign(Object.assign({}, item), { items: item.items.toReversed() });
74
+ });
75
+ }
48
76
  /**
49
77
  *
50
78
  * @param context
@@ -65,61 +93,99 @@ function insertNotifications(context) {
65
93
  * @param context
66
94
  */
67
95
  function insertNewItemIndicator(context) {
68
- const processedWithIndicator = [];
69
- let newItemIndicatorInserted = context.newItemIndicatorInserted;
70
- for (const item of context.processedItems) {
71
- const timestamp = getProcessedItemTimestamp(item);
72
- if (timestamp !== undefined &&
73
- context.lastVisitedTimestamp &&
74
- !newItemIndicatorInserted &&
75
- new Date(timestamp) <= context.lastVisitedTimestamp &&
76
- context.hasUnseenNotifications) {
77
- processedWithIndicator.push({ type: 'newItemIndicator' });
78
- newItemIndicatorInserted = true;
96
+ if (!context.lastVisitedTimestamp) {
97
+ return context;
98
+ }
99
+ const groups = context.processedItems;
100
+ const lastVisited = context.lastVisitedTimestamp;
101
+ // Walk groups newest → oldest. Items and groups are sorted
102
+ // ascending, so at most one group straddles the seen/unseen
103
+ // boundary; the rest are entirely seen or entirely unseen. In an
104
+ // active chat where the newest item is already seen, the loop
105
+ // exits in O(1).
106
+ for (let g = groups.length - 1; g >= 0; g--) {
107
+ const group = groups[g];
108
+ if (group.type !== 'group') {
109
+ continue;
79
110
  }
80
- processedWithIndicator.push(item);
111
+ const seenIndex = findNewestSeenIndex(group.items, lastVisited);
112
+ if (seenIndex === -1) {
113
+ continue;
114
+ }
115
+ return placeIndicatorAtBoundary(context, groups, g, group, seenIndex);
81
116
  }
82
- context.processedItems = processedWithIndicator;
83
- context.newItemIndicatorInserted = newItemIndicatorInserted;
117
+ // No seen item found — everything is unseen, no boundary to mark.
84
118
  return context;
85
119
  }
120
+ function findNewestSeenIndex(items, lastVisited) {
121
+ for (let i = items.length - 1; i >= 0; i--) {
122
+ const timestamp = getProcessedItemTimestamp(items[i]);
123
+ if (timestamp === undefined) {
124
+ continue;
125
+ }
126
+ if (new Date(timestamp) <= lastVisited) {
127
+ return i;
128
+ }
129
+ }
130
+ return -1;
131
+ }
132
+ function placeIndicatorAtBoundary(context, groups, groupIndex, group, seenIndex) {
133
+ const isLastItemInGroup = seenIndex === group.items.length - 1;
134
+ const isLastGroup = groupIndex === groups.length - 1;
135
+ // Newest seen item is the very last item overall → everything is
136
+ // seen, nothing to mark.
137
+ if (isLastItemInGroup && isLastGroup) {
138
+ return context;
139
+ }
140
+ // Seen item is last in its group → boundary is between groups.
141
+ // Place the indicator at top level so reversal stays a pure
142
+ // two-level reverse.
143
+ if (isLastItemInGroup) {
144
+ return Object.assign(Object.assign({}, context), { processedItems: [
145
+ ...groups.slice(0, groupIndex + 1),
146
+ { type: 'newItemIndicator' },
147
+ ...groups.slice(groupIndex + 1),
148
+ ] });
149
+ }
150
+ // Boundary falls inside a single day — indicator stays nested.
151
+ const newGroupItems = [
152
+ ...group.items.slice(0, seenIndex + 1),
153
+ { type: 'newItemIndicator' },
154
+ ...group.items.slice(seenIndex + 1),
155
+ ];
156
+ return Object.assign(Object.assign({}, context), { processedItems: [
157
+ ...groups.slice(0, groupIndex),
158
+ Object.assign(Object.assign({}, group), { items: newGroupItems }),
159
+ ...groups.slice(groupIndex + 1),
160
+ ] });
161
+ }
86
162
  /**
87
163
  *
88
164
  * @param context
89
165
  */
90
- // eslint-disable-next-line sonarjs/cognitive-complexity
91
166
  function insertDateGroups(context) {
92
167
  const newProcessedItems = [];
93
168
  let currentGroup = null;
94
169
  let lastProcessedDate = null;
95
170
  for (const item of context.processedItems) {
96
- if (item.type === 'chat' || item.type === 'iconList') {
97
- const currentTimestamp = new Date(item.item.timestamp);
98
- const currentDate = context.dateTimeFormatter.format(currentTimestamp, 'date');
99
- if (currentDate !== lastProcessedDate) {
100
- if (currentGroup) {
101
- newProcessedItems.push(currentGroup);
102
- }
103
- currentGroup = {
104
- type: 'group',
105
- groupType: 'dateGroup',
106
- heading: currentDate,
107
- items: [],
108
- };
109
- lastProcessedDate = currentDate;
110
- }
111
- if (currentGroup) {
112
- currentGroup.items.push(item);
113
- }
171
+ if (item.type !== 'chat' && item.type !== 'iconList') {
172
+ continue;
114
173
  }
115
- else if (item.type === 'newItemIndicator') {
174
+ const currentDate = context.dateTimeFormatter.format(new Date(item.item.timestamp), 'date');
175
+ if (currentDate !== lastProcessedDate) {
116
176
  if (currentGroup) {
117
- currentGroup.items.push(item);
118
- }
119
- else {
120
- newProcessedItems.push(item);
177
+ newProcessedItems.push(currentGroup);
121
178
  }
179
+ currentGroup = {
180
+ type: 'group',
181
+ groupType: 'dateGroup',
182
+ heading: currentDate,
183
+ items: [item],
184
+ };
185
+ lastProcessedDate = currentDate;
186
+ continue;
122
187
  }
188
+ currentGroup === null || currentGroup === void 0 ? void 0 : currentGroup.items.push(item);
123
189
  }
124
190
  if (currentGroup) {
125
191
  newProcessedItems.push(currentGroup);
@@ -127,14 +193,153 @@ function insertDateGroups(context) {
127
193
  return Object.assign(Object.assign({}, context), { processedItems: newProcessedItems });
128
194
  }
129
195
 
130
- const chatListCss = () => `@charset "UTF-8";:host(limebb-chat-list){--limel-top-edge-fade-height:0.75rem;isolation:isolate;--limel-overflow-mask-vertical:linear-gradient( to bottom, transparent 0%, black calc(0% + var(--limel-top-edge-fade-height, 1rem)), black calc(100% - var(--limel-bottom-edge-fade-height, 1rem)), transparent 100% );-webkit-mask-image:var(--limel-overflow-mask-vertical);mask-image:var(--limel-overflow-mask-vertical);padding-top:var(--limel-top-edge-fade-height, 1rem);padding-bottom:var(--limel-bottom-edge-fade-height, 1rem);box-sizing:border-box;display:flex;gap:0.5rem;height:100%;width:100%;min-width:0;min-height:0;padding:var(--limel-top-edge-fade-height) 0.5rem;overflow:hidden auto}:host(limebb-chat-list.is-empty){padding:0}:host(limebb-chat-list[order=oldest-on-top]){flex-direction:column-reverse}:host(limebb-chat-list[order=newest-on-top]){flex-direction:column}*{box-sizing:border-box}limel-spinner{align-self:center;justify-self:center;margin:auto}.date-group{isolation:isolate;display:flex;flex-direction:inherit;gap:0.5rem}.date-heading{position:sticky;z-index:1;top:0.25rem;display:flex;border-radius:9rem;padding:0.25rem 0.5rem;margin:0 auto;width:fit-content;font-size:0.75rem;line-height:1;color:rgb(var(--contrast-900));backdrop-filter:blur(0.5rem);-webkit-backdrop-filter:blur(0.5rem);transition:color 0.2s ease, border-color 0.4s ease}.date-heading:hover{color:rgb(var(--contrast-1000));border-color:rgb(var(--contrast-500))}.date-heading:hover:before{opacity:1}.date-heading:before{transition:opacity 0.2s ease;content:"";position:absolute;z-index:-1;inset:0;opacity:0.6;border-radius:inherit;background-color:rgb(var(--contrast-100))}.new-items-indicator{position:relative;isolation:isolate;display:flex;align-items:center;justify-content:flex-end;margin:0.25rem 0}.new-items-indicator hr{border:none;position:absolute;width:100%;height:1px;background-color:rgb(var(--color-red-lighter))}.new-items-indicator h3{z-index:1;padding:0 0.5rem;border-radius:1rem;margin:0 1rem;font-size:small;line-height:0.75rem;text-transform:lowercase;color:rgb(var(--color-red-default));background-color:rgb(var(--contrast-100))}.typing-indicator-height-animation{--limebb-chat-typing-opacity-transition-speed:0.1s;--limebb-chat-typing-opacity-transition-delay:0s;--limebb-chat-typing-grid-template-rows-transition-speed:0.3s;position:sticky;top:0;z-index:1;transition:grid-template-rows var(--limebb-chat-typing-grid-template-rows-transition-speed) ease;display:grid;grid-template-rows:0}.typing-indicator-height-animation div{transition:opacity var(--limebb-chat-typing-opacity-transition-speed) ease var(--limebb-chat-typing-opacity-transition-delay);overflow:hidden;opacity:0}:host(limebb-chat-list[is-typing-indicator-visible]) .typing-indicator-height-animation{--limebb-chat-typing-opacity-transition-speed:0.4s;--limebb-chat-typing-opacity-transition-delay:0.3s;--limebb-chat-typing-grid-template-rows-transition-speed:0.46s;grid-template-rows:1.5rem}:host(limebb-chat-list[is-typing-indicator-visible]) .typing-indicator-height-animation div{opacity:1}.date-group limebb-chat-item{--limebb-promoted-action-bar-grid-template-rows:0fr}:host(limebb-chat-list[order=oldest-on-top]) .date-group:first-of-type limebb-chat-item:first-of-type{--limebb-promoted-action-bar-grid-template-rows:1fr}:host(limebb-chat-list[order=newest-on-top]) .date-group:first-of-type limebb-chat-item:first-of-type{--limebb-promoted-action-bar-grid-template-rows:1fr}`;
196
+ // "Near enough the newest edge to count as sticky." 50px is roughly
197
+ // two message-bubble margins; a user who is within that distance
198
+ // clearly wasn't scrolling back through history, so we treat their
199
+ // position as "stuck to newest" and re-pin on each items update.
200
+ const NEAR_NEWEST_THRESHOLD_PX = 50;
201
+ /**
202
+ * Keeps a scroll container stuck to its "newest" edge as items are
203
+ * appended, so long as the user hasn't scrolled away.
204
+ *
205
+ * We use imperative scrolling rather than `flex-direction: column-reverse`
206
+ * so DOM order matches visual order (copy/paste, screen readers, tab
207
+ * order all align with what the user sees). CSS `overflow-anchor: auto`
208
+ * — the default and left enabled here — solves a different problem:
209
+ * preserving scroll position when content is inserted *above* the
210
+ * viewport. It doesn't stick the scroll to the newest edge when items
211
+ * arrive below the viewport, which is what chat typically needs; that
212
+ * requires the imperative approach in this class.
213
+ *
214
+ * Lifecycle:
215
+ * - `beforeRender()` in `componentWillRender` — snapshot whether the
216
+ * user is near the newest edge, using the order the scroll position
217
+ * was last measured against. Capturing here (synchronously, before
218
+ * the new DOM lands) avoids the race a scroll-event listener would
219
+ * have with `componentDidRender`.
220
+ * - `afterRender()` in `componentDidRender` — if the snapshot said
221
+ * "near newest", re-pin to the newest edge (new `scrollHeight`).
222
+ * - `repinIfSticky()` for out-of-band re-pins (e.g. after the typing
223
+ * indicator's height transition finishes — its `scrollHeight`
224
+ * change lands after `componentDidRender`).
225
+ *
226
+ * Scroll-container contract: the passed element must itself be the
227
+ * scrolling ancestor (the element whose `scrollTop` / `scrollHeight`
228
+ * / `clientHeight` reflect the chat list's scroll state). In practice
229
+ * that is the component's host, which sets `overflow: hidden auto` on
230
+ * itself. If future refactors introduce an inner scrolling wrapper,
231
+ * pass that wrapper instead.
232
+ */
233
+ class ScrollAnchor {
234
+ constructor(scrollContainer, getOrder, initialOrder) {
235
+ this.scrollContainer = scrollContainer;
236
+ this.getOrder = getOrder;
237
+ this.isSticky = true;
238
+ this.paintedOrder = initialOrder;
239
+ }
240
+ beforeRender() {
241
+ if (!this.scrollContainer.isConnected) {
242
+ return;
243
+ }
244
+ // Measure against the order the scroll position was last painted
245
+ // with. If `order` just changed, the scroll position still refers
246
+ // to the previous layout until this render commits.
247
+ this.isSticky =
248
+ this.distanceFromNewestEdge(this.paintedOrder) <
249
+ NEAR_NEWEST_THRESHOLD_PX;
250
+ }
251
+ afterRender() {
252
+ if (!this.scrollContainer.isConnected) {
253
+ return;
254
+ }
255
+ this.paintedOrder = this.getOrder();
256
+ if (this.isSticky) {
257
+ this.scrollToNewest();
258
+ }
259
+ }
260
+ repinIfSticky() {
261
+ if (!this.scrollContainer.isConnected) {
262
+ return;
263
+ }
264
+ if (this.isSticky) {
265
+ this.scrollToNewest();
266
+ }
267
+ }
268
+ scrollToNewest() {
269
+ const target = this.getOrder() === 'oldest-on-top'
270
+ ? this.scrollContainer.scrollHeight
271
+ : 0;
272
+ // Reading `scrollTop` is a cheap (already-measured) lookup, and
273
+ // short-circuiting avoids forcing a layout when we'd write the
274
+ // same value — important under high-frequency re-renders
275
+ // (streaming AI output).
276
+ if (this.scrollContainer.scrollTop === target) {
277
+ return;
278
+ }
279
+ this.scrollContainer.scrollTop = target;
280
+ }
281
+ distanceFromNewestEdge(order) {
282
+ return order === 'oldest-on-top'
283
+ ? this.scrollContainer.scrollHeight -
284
+ this.scrollContainer.scrollTop -
285
+ this.scrollContainer.clientHeight
286
+ : this.scrollContainer.scrollTop;
287
+ }
288
+ }
289
+
290
+ // The CSS property the typing-indicator container transitions.
291
+ // Mirrored from chat-list.scss `.typing-indicator-height-animation
292
+ // { transition: grid-template-rows ... }`. The runtime verifier below
293
+ // guards against the SCSS and the handler-side filter silently
294
+ // disagreeing.
295
+ const HEIGHT_ANIMATION_PROPERTY = 'grid-template-rows';
296
+ /**
297
+ * Returns a `transitionend` listener for the typing indicator's
298
+ * height transition. Filters by property name so unrelated
299
+ * transitions on the same element (e.g. opacity) don't trigger the
300
+ * callback.
301
+ * @param onHeightTransitionEnd
302
+ */
303
+ function createHeightTransitionEndHandler(onHeightTransitionEnd) {
304
+ return (event) => {
305
+ if (event.propertyName !== HEIGHT_ANIMATION_PROPERTY) {
306
+ return;
307
+ }
308
+ onHeightTransitionEnd();
309
+ };
310
+ }
311
+ /**
312
+ * Verifies that the passed element actually transitions on the
313
+ * property the height-transition handler filters on. Surfaces SCSS
314
+ * drift loudly instead of silently breaking auto-scroll-on-indicator-
315
+ * appearance.
316
+ * @param element
317
+ */
318
+ function verifyHeightAnimationProperty(element) {
319
+ if (!element) {
320
+ return;
321
+ }
322
+ const transitionProperty = globalThis.getComputedStyle(element).transitionProperty;
323
+ if (!transitionProperty) {
324
+ return;
325
+ }
326
+ const properties = new Set(transitionProperty.split(',').map((p) => p.trim()));
327
+ if (!properties.has('all') && !properties.has(HEIGHT_ANIMATION_PROPERTY)) {
328
+ 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.`);
329
+ }
330
+ }
331
+
332
+ const chatListCss = () => `@charset "UTF-8";:host(limebb-chat-list){--limel-top-edge-fade-height:0.75rem;isolation:isolate;--limel-overflow-mask-vertical:linear-gradient( to bottom, transparent 0%, black calc(0% + var(--limel-top-edge-fade-height, 1rem)), black calc(100% - var(--limel-bottom-edge-fade-height, 1rem)), transparent 100% );-webkit-mask-image:var(--limel-overflow-mask-vertical);mask-image:var(--limel-overflow-mask-vertical);padding-top:var(--limel-top-edge-fade-height, 1rem);padding-bottom:var(--limel-bottom-edge-fade-height, 1rem);box-sizing:border-box;display:flex;flex-direction:column;gap:0.5rem;height:100%;width:100%;min-width:0;min-height:0;padding:var(--limel-top-edge-fade-height) 0.5rem;overflow:hidden auto}:host(limebb-chat-list.is-empty){padding:0}*{box-sizing:border-box}limel-spinner{align-self:center;justify-self:center;margin:auto}.date-group{isolation:isolate;display:flex;flex-direction:inherit;gap:0.5rem}.date-heading{position:sticky;z-index:1;top:0.25rem;display:flex;border-radius:9rem;padding:0.25rem 0.5rem;margin:0 auto;width:fit-content;font-size:0.75rem;line-height:1;color:rgb(var(--contrast-900));backdrop-filter:blur(0.5rem);-webkit-backdrop-filter:blur(0.5rem);transition:color 0.2s ease, border-color 0.4s ease}.date-heading:hover{color:rgb(var(--contrast-1000));border-color:rgb(var(--contrast-500))}.date-heading:hover:before{opacity:1}.date-heading:before{transition:opacity 0.2s ease;content:"";position:absolute;z-index:-1;inset:0;opacity:0.6;border-radius:inherit;background-color:rgb(var(--contrast-100))}.new-items-indicator{position:relative;isolation:isolate;display:flex;align-items:center;justify-content:flex-end;margin:0.25rem 0}.new-items-indicator hr{border:none;position:absolute;width:100%;height:1px;background-color:rgb(var(--color-red-lighter))}.new-items-indicator h3{z-index:1;padding:0 0.5rem;border-radius:1rem;margin:0 1rem;font-size:small;line-height:0.75rem;text-transform:lowercase;color:rgb(var(--color-red-default));background-color:rgb(var(--contrast-100))}.typing-indicator-height-animation{--limebb-chat-typing-opacity-transition-speed:0.1s;--limebb-chat-typing-opacity-transition-delay:0s;--limebb-chat-typing-grid-template-rows-transition-speed:0.3s;position:sticky;z-index:1;transition:grid-template-rows var(--limebb-chat-typing-grid-template-rows-transition-speed) ease;display:grid;grid-template-rows:0}.typing-indicator-height-animation div{transition:opacity var(--limebb-chat-typing-opacity-transition-speed) ease var(--limebb-chat-typing-opacity-transition-delay);overflow:hidden;opacity:0}:host(limebb-chat-list[order=oldest-on-top]) .typing-indicator-height-animation{bottom:0}:host(limebb-chat-list[order=newest-on-top]) .typing-indicator-height-animation{top:0}:host(limebb-chat-list[is-typing-indicator-visible]) .typing-indicator-height-animation{--limebb-chat-typing-opacity-transition-speed:0.4s;--limebb-chat-typing-opacity-transition-delay:0.3s;--limebb-chat-typing-grid-template-rows-transition-speed:0.46s;grid-template-rows:1.5rem}:host(limebb-chat-list[is-typing-indicator-visible]) .typing-indicator-height-animation div{opacity:1}.date-group limebb-chat-item{--limebb-promoted-action-bar-grid-template-rows:0fr}:host(limebb-chat-list[order=oldest-on-top]) .date-group:last-of-type limebb-chat-item:last-of-type{--limebb-promoted-action-bar-grid-template-rows:1fr}:host(limebb-chat-list[order=newest-on-top]) .date-group:first-of-type limebb-chat-item:first-of-type{--limebb-promoted-action-bar-grid-template-rows:1fr}`;
131
333
 
132
334
  const ChatList = class {
133
335
  constructor(hostRef) {
134
336
  registerInstance(this, hostRef);
135
337
  /**
136
338
  * List of items to display in the feed.
137
- * These items must be sorted by their `timestamp` in descending order.
339
+ * These items must be sorted by their `timestamp` in ascending order
340
+ * (oldest first, newest last). This matches the natural visual order
341
+ * for the default `oldest-on-top` layout, keeping DOM order aligned
342
+ * with visual order for copy/paste, screen readers, and tab order.
138
343
  *
139
344
  * See the [ChatListItem](#/type/ChatListItem/) type for the kinds of
140
345
  * items that can appear in the list.
@@ -148,29 +353,56 @@ const ChatList = class {
148
353
  *
149
354
  * :::important
150
355
  * Using this prop will _not_ automatically sort the chat items for you!
151
- * You still need to pass the items in the correct order (descending order by timestamp).
152
- * This prop will only affect the direction of the elements in the user interface,
356
+ * You still need to pass the items in the correct order (ascending order by timestamp — oldest first).
357
+ * This prop only affects the direction of the elements in the user interface,
153
358
  * such as the location of the typing indicator or the scrollbars.
154
359
  * :::
155
360
  */
156
361
  this.order = 'oldest-on-top';
157
362
  this.processedItems = [];
363
+ this.handleHeightTransitionEnd = createHeightTransitionEndHandler(() => { var _a; return (_a = this.scrollAnchor) === null || _a === void 0 ? void 0 : _a.repinIfSticky(); });
158
364
  }
159
365
  handleItemsChange() {
160
366
  let lastVisited = null;
161
367
  if (this.lastVisitedTimestamp) {
162
368
  lastVisited = parseToUTCDate(this.lastVisitedTimestamp);
163
369
  }
164
- this.processedItems = processChatItems(this.items, this.dateTimeFormatter, lastVisited);
370
+ const processed = processChatItems(this.items, this.dateTimeFormatter, lastVisited);
371
+ this.processedItems =
372
+ this.order === 'newest-on-top'
373
+ ? reverseProcessedItems(processed)
374
+ : processed;
165
375
  }
166
376
  componentWillLoad() {
167
377
  this.handleItemsChange();
378
+ this.scrollAnchor = new ScrollAnchor(this.host, () => this.order, this.order);
379
+ }
380
+ componentDidLoad() {
381
+ var _a;
382
+ verifyHeightAnimationProperty((_a = this.host.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.typing-indicator-height-animation'));
383
+ }
384
+ componentWillRender() {
385
+ if (this.items.length === 0) {
386
+ // Nothing to measure or scroll against. `isSticky` retains
387
+ // its last value (default `true`), so the first non-empty
388
+ // render still pins correctly.
389
+ return;
390
+ }
391
+ this.scrollAnchor.beforeRender();
392
+ }
393
+ componentDidRender() {
394
+ if (this.items.length === 0) {
395
+ return;
396
+ }
397
+ this.scrollAnchor.afterRender();
168
398
  }
169
399
  render() {
170
- return (h(Host, { key: 'b042ac219b98d38deb63cd862fc6aeb565bf3064', class: {
400
+ return (h(Host, { key: '002369dc1f47978696b4eb8ac7189f794568b0b4', class: {
171
401
  'is-empty': this.items.length === 0 &&
172
402
  !this.isTypingIndicatorVisible,
173
- }, "aria-busy": this.isBusy(), role: "log" }, this.renderSpinner(), h("div", { key: 'dd245b7fced3824abbc97671204310f51f0ba603', class: "typing-indicator-height-animation" }, h("div", { key: 'da9f1fdb805cda959b707598694070ce8b765965' }, this.renderTypingIndicator())), this.renderChatItems()));
403
+ }, "aria-busy": this.isBusy(), role: "log" }, this.renderSpinner(), this.order === 'newest-on-top' &&
404
+ this.renderTypingIndicatorBlock(), this.renderChatItems(), this.order === 'oldest-on-top' &&
405
+ this.renderTypingIndicatorBlock()));
174
406
  }
175
407
  renderSpinner() {
176
408
  if (!this.loading) {
@@ -178,6 +410,14 @@ const ChatList = class {
178
410
  }
179
411
  return h("limel-spinner", null);
180
412
  }
413
+ renderTypingIndicatorBlock() {
414
+ // The indicator's grid-template-rows transition grows the
415
+ // container over ~0.46s. `componentDidRender` fires before the
416
+ // transition starts, so `scrollHeight` at that moment doesn't
417
+ // include the final height. `handleHeightTransitionEnd` re-pins
418
+ // once the transition lands.
419
+ return (h("div", { class: "typing-indicator-height-animation", onTransitionEnd: this.handleHeightTransitionEnd }, h("div", null, this.renderTypingIndicator())));
420
+ }
181
421
  renderTypingIndicator() {
182
422
  if (!this.isTypingIndicatorVisible) {
183
423
  return;
@@ -193,33 +433,54 @@ const ChatList = class {
193
433
  case 'newItemIndicator': {
194
434
  return this.renderIndicator();
195
435
  }
196
- default: {
436
+ case 'chat':
437
+ case 'iconList': {
438
+ // `insertDateGroups` wraps every `chat` / `iconList`
439
+ // in a `group`, so these variants shouldn't appear
440
+ // at top level. If the processor pipeline is ever
441
+ // reordered, render nothing rather than double-
442
+ // render once the wrapping group includes them.
197
443
  return null;
198
444
  }
445
+ default: {
446
+ return assertNever(item);
447
+ }
199
448
  }
200
449
  });
201
450
  }
202
451
  renderGroup(group) {
203
452
  const chatItems = group.items.map((item) => {
204
- if (item.type === 'chat') {
205
- return this.renderChatItem(item.item);
206
- }
207
- else if (item.type === 'iconList') {
208
- return this.renderIconList(item.item);
209
- }
210
- else if (item.type === 'newItemIndicator') {
211
- return this.renderIndicator();
453
+ switch (item.type) {
454
+ case 'chat': {
455
+ return this.renderChatItem(item.item);
456
+ }
457
+ case 'iconList': {
458
+ return this.renderIconList(item.item);
459
+ }
460
+ case 'newItemIndicator': {
461
+ return this.renderIndicator();
462
+ }
463
+ case 'group': {
464
+ // Date groups don't nest. A compile-time `never`
465
+ // here would require a separate nested-item type;
466
+ // this runtime null return is the pragmatic
467
+ // middle ground.
468
+ return null;
469
+ }
470
+ default: {
471
+ return assertNever(item);
472
+ }
212
473
  }
213
474
  });
214
- const groupChatItems = group.items.filter((i) => i.type === 'chat');
215
- const lastChatItem = groupChatItems.at(-1);
216
- const stableId = (lastChatItem === null || lastChatItem === void 0 ? void 0 : lastChatItem.type) === 'chat' ? lastChatItem.item.id : '';
217
- const sectionKey = 'date-group-' + group.heading + '-' + stableId;
475
+ // Key the section by the group's heading (unique per date, and
476
+ // stable across item additions). Must not vary with group
477
+ // content: if it did, appending a new message would change the
478
+ // key and cause Stencil to remount the whole section, briefly
479
+ // collapsing scrollHeight and scrolling the list to the top
480
+ // before `componentDidRender` scrolls it back.
481
+ const sectionKey = 'date-group-' + group.heading;
218
482
  const headingId = `heading-${sectionKey}`.replaceAll(/[^a-zA-Z0-9_-]/g, '_');
219
483
  const dateHeading = (h("a", { id: headingId, class: "date-heading", role: "heading", "aria-level": 2 }, group.heading));
220
- if (this.order === 'oldest-on-top') {
221
- return (h("section", { class: "date-group", key: sectionKey, "aria-labelledby": headingId }, chatItems, dateHeading));
222
- }
223
484
  return (h("section", { class: "date-group", key: sectionKey, "aria-labelledby": headingId }, dateHeading, chatItems));
224
485
  }
225
486
  renderChatItem(item) {
@@ -240,9 +501,13 @@ const ChatList = class {
240
501
  get translator() {
241
502
  return this.platform.get(n.Translate);
242
503
  }
504
+ get host() { return getElement(this); }
243
505
  static get watchers() { return {
244
506
  "items": [{
245
507
  "handleItemsChange": 0
508
+ }],
509
+ "order": [{
510
+ "handleItemsChange": 0
246
511
  }]
247
512
  }; }
248
513
  };
@@ -252,6 +517,9 @@ function parseToUTCDate(date) {
252
517
  }
253
518
  return new Date(date); // This will automatically be UTC
254
519
  }
520
+ function assertNever(value) {
521
+ throw new Error(`Unexpected value in exhaustive switch: ${JSON.stringify(value)}`);
522
+ }
255
523
  ChatList.style = chatListCss();
256
524
 
257
525
  export { ChatList as limebb_chat_list };
@@ -64,8 +64,8 @@ const ComposerToolbar = class {
64
64
  }
65
65
  render() {
66
66
  return [
67
- h("div", { key: '62fa9154e6593b46509a4b4185c425c416fbf1de', class: "actions" }, h("slot", { key: '97476b607eb583b8c26e0164836174b9aeb8faa9', name: "leading" }), this.renderFileInput(), this.renderTriggerActions(), h("slot", { key: '14c2388e0cb0d1ec2a1cd548d11994475da5e949', name: "trailing" })),
68
- h("slot", { key: 'bc290147db27a9a6454cb32031bfe0990fe59e77', name: "primary-action" }),
67
+ h("div", { key: '6034b2945edfd9b613f606f8bb4fb0db81c1390f', class: "actions" }, h("slot", { key: '82b9bc14d80aedd3320af70bc63d3a908db6a7b2', name: "leading" }), this.renderFileInput(), this.renderTriggerActions(), h("slot", { key: '338fc1b7c7ec3f7dfedb123b7e09dd05f75fc45c', name: "trailing" })),
68
+ h("slot", { key: '57b277b772cbaf702e619ecbd6ff9032718b87b7', name: "primary-action" }),
69
69
  ];
70
70
  }
71
71
  get translator() {
@@ -1968,7 +1968,7 @@ const CurrencyPicker = class {
1968
1968
  };
1969
1969
  }
1970
1970
  render() {
1971
- return (h("limel-picker", { key: '75278107cede6773fb098f52183b8c3d291d12fe', 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 }));
1971
+ 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 }));
1972
1972
  }
1973
1973
  getCurrencyName(code) {
1974
1974
  var _a;
@@ -16,7 +16,7 @@ const DashboardComponent = class {
16
16
  };
17
17
  }
18
18
  render() {
19
- return [this.renderHeader(), h("slot", { key: '3c5a1725b0cf3887074d0be4b1b7086126447d3b', name: "content" })];
19
+ return [this.renderHeader(), h("slot", { key: 'f5202c8c5d6ceb3e01420c6045ecb5589fed915a', name: "content" })];
20
20
  }
21
21
  };
22
22
  DashboardComponent.style = dashboardWidgetCss();
@@ -73,7 +73,7 @@ const DatePicker = class {
73
73
  if (this.value) {
74
74
  this.shouldEmitValueAsString = typeof this.value === 'string';
75
75
  }
76
- return (h("limel-date-picker", { key: 'd3f3229f134e51ae94abbdb63da0e88988eaa474', 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 }));
76
+ 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 }));
77
77
  }
78
78
  parseDateValue() {
79
79
  const value = this.value;
@@ -21,7 +21,7 @@ const DateRange = class {
21
21
  this.originalEndTime = this.endTime;
22
22
  }
23
23
  render() {
24
- return (h("div", { key: '0eab8abe9007384af9e7fcd56f8ac37c4aeb6b39', class: "date-pickers" }, this.renderStartDatePicker(), this.renderEndDatePicker()));
24
+ return (h("div", { key: 'e00abfd31fcae2a4279ae2194696b5cffb76dd79', class: "date-pickers" }, this.renderStartDatePicker(), this.renderEndDatePicker()));
25
25
  }
26
26
  renderStartDatePicker() {
27
27
  if (!this.originalStartTime) {
@@ -345,7 +345,7 @@ const DocumentChips = class {
345
345
  this.revokeAllBlobUrls();
346
346
  }
347
347
  render() {
348
- return (h(Host, { key: 'd5bdfcc1c2895820c4613bf907799063e1118899', role: "list", "aria-label": this.accessibleLabel }, this.files.map((file) => this.renderFile(file))));
348
+ return (h(Host, { key: '93d00770917014b1de6e380db6b102859210fded', role: "list", "aria-label": this.accessibleLabel }, this.files.map((file) => this.renderFile(file))));
349
349
  }
350
350
  renderFile(file) {
351
351
  const isReady = !file.uploadState || file.uploadState === 'done';
@@ -114,10 +114,10 @@ const DocumentItemComponent = class {
114
114
  render() {
115
115
  const isUnavailable = this.isUnavailable();
116
116
  const image = this.getImage();
117
- return (h(Host, { key: '339a290abba67ded1229568ca8b3bb90238abb43', id: String(this.item.file.id), class: {
117
+ return (h(Host, { key: '97fe779b6706627e3abb9729b6ea2933d5ccafb4', id: String(this.item.file.id), class: {
118
118
  'has-image': !!image,
119
119
  'has-error': !!this.item.hasError,
120
- }, 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: '518a23576f225d4a7b67cc7c1adee6d64ed27589', image: image, clickable: isUnavailable ? false : true, onClick: this.handleDocumentItemClick, onKeyDown: this.handleKeyDown }, h("div", { key: 'beb0197e876f351a0e432c58a6061c0937a78b70', slot: "component" }, this.renderFileSize(), this.renderBooleanInput())), this.renderHelp()));
120
+ }, 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()));
121
121
  }
122
122
  renderBooleanInput() {
123
123
  const id = this.getControlId();
@@ -66,7 +66,7 @@ const DocumentPicker = class {
66
66
  this.labelId = createRandomString();
67
67
  }
68
68
  render() {
69
- return (h(Host, { key: 'ff9e59b375c66d9b5c6818e4b81776d4dbfd63a8' }, h("limel-notched-outline", { key: '8414b5e0363c10b90c0dc5fda549d0268c177a8d', labelId: this.labelId, label: this.label, required: this.required, invalid: this.invalid, hasFloatingLabel: true }, h("limel-masonry-layout", { key: '06037f5780b8755b628309e26fa908167df49347', 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()));
69
+ 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()));
70
70
  }
71
71
  renderItems() {
72
72
  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 })));
@@ -7,7 +7,7 @@ const FeedItemThumbnailFileInfo = class {
7
7
  registerInstance(this, hostRef);
8
8
  }
9
9
  render() {
10
- return h("span", { key: '9fda2e7e6963d53030bec12e65b5b7e9d25d14b9' }, this.description);
10
+ return h("span", { key: 'ed2f52dbd7899d308078d5cee6e9e7bc3e6c0f50' }, this.description);
11
11
  }
12
12
  };
13
13
  FeedItemThumbnailFileInfo.style = feedItemThumbnailFileInfoCss();