@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,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 descending order.
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 (descending order by timestamp).
40
- * This prop will only affect the direction of the elements in the user interface,
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
- this.processedItems = processChatItems(this.items, this.dateTimeFormatter, lastVisited);
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: 'b042ac219b98d38deb63cd862fc6aeb565bf3064', class: {
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(), h("div", { key: 'dd245b7fced3824abbc97671204310f51f0ba603', class: "typing-indicator-height-animation" }, h("div", { key: 'da9f1fdb805cda959b707598694070ce8b765965' }, this.renderTypingIndicator())), this.renderChatItems()));
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
- default: {
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
- if (item.type === 'chat') {
93
- return this.renderChatItem(item.item);
94
- }
95
- else if (item.type === 'iconList') {
96
- return this.renderIconList(item.item);
97
- }
98
- else if (item.type === 'newItemIndicator') {
99
- return this.renderIndicator();
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
- const groupChatItems = group.items.filter((i) => i.type === 'chat');
103
- const lastChatItem = groupChatItems.at(-1);
104
- const stableId = (lastChatItem === null || lastChatItem === void 0 ? void 0 : lastChatItem.type) === 'chat' ? lastChatItem.item.id : '';
105
- const sectionKey = 'date-group-' + group.heading + '-' + stableId;
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 descending order.\n\nSee the [ChatListItem](#/type/ChatListItem/) type for the kinds of\nitems that can appear in the list."
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 (descending order by timestamp).\nThis prop will only affect the direction of the elements in the user interface,\nsuch as the location of the typing indicator or the scrollbars.\n:::"
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 descending order.
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
- const processedWithIndicator = [];
66
- let newItemIndicatorInserted = context.newItemIndicatorInserted;
67
- for (const item of context.processedItems) {
68
- const timestamp = getProcessedItemTimestamp(item);
69
- if (timestamp !== undefined &&
70
- context.lastVisitedTimestamp &&
71
- !newItemIndicatorInserted &&
72
- new Date(timestamp) <= context.lastVisitedTimestamp &&
73
- context.hasUnseenNotifications) {
74
- processedWithIndicator.push({ type: 'newItemIndicator' });
75
- newItemIndicatorInserted = true;
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
- processedWithIndicator.push(item);
112
+ return placeIndicatorAtBoundary(context, groups, g, group, seenIndex);
78
113
  }
79
- context.processedItems = processedWithIndicator;
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 === 'chat' || item.type === 'iconList') {
94
- const currentTimestamp = new Date(item.item.timestamp);
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
- else if (item.type === 'newItemIndicator') {
171
+ const currentDate = context.dateTimeFormatter.format(new Date(item.item.timestamp), 'date');
172
+ if (currentDate !== lastProcessedDate) {
113
173
  if (currentGroup) {
114
- currentGroup.items.push(item);
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: '3b2b1fe0a578f22ebe2698428c2169d0b98bc0e6', class: "typing-ellipsis" }, h("div", { key: 'b7aa85a8b2aa8f9a52374fb794ff4ef757861f7b', class: "circle" }), h("div", { key: 'ffea2029e1869afa64ca9989be071dee6a4e0936', class: "circle" }), h("div", { key: '6d78b20e1bdd35c74912adf49031998cf30b9b7b', class: "circle" })));
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: '62fa9154e6593b46509a4b4185c425c416fbf1de', class: "actions" }, h("slot", { key: '97476b607eb583b8c26e0164836174b9aeb8faa9', name: "leading" }), this.renderFileInput(), this.renderTriggerActions(), h("slot", { key: '14c2388e0cb0d1ec2a1cd548d11994475da5e949', name: "trailing" })),
87
- h("slot", { key: 'bc290147db27a9a6454cb32031bfe0990fe59e77', name: "primary-action" }),
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: '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 }));
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: '3c5a1725b0cf3887074d0be4b1b7086126447d3b', name: "content" })];
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: '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 }));
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: '0eab8abe9007384af9e7fcd56f8ac37c4aeb6b39', class: "date-pickers" }, this.renderStartDatePicker(), this.renderEndDatePicker()));
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: 'd5bdfcc1c2895820c4613bf907799063e1118899', role: "list", "aria-label": this.accessibleLabel }, this.files.map((file) => this.renderFile(file))));
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: '339a290abba67ded1229568ca8b3bb90238abb43', id: String(this.item.file.id), class: {
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: '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()));
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: '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()));
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 })));