@bbki.ng/bb-msg-history 0.11.2 → 0.13.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.
@@ -41,6 +41,13 @@ export declare class BBMsgHistory extends HTMLElement {
41
41
  * el.appendMessage({ author: 'bob', text: 'How are you?' });
42
42
  */
43
43
  appendMessage(message: Message): this;
44
+ /**
45
+ * Scroll to the bottom of the message history.
46
+ *
47
+ * @example
48
+ * el.scrollToBottom(); // Scroll with smooth animation
49
+ */
50
+ scrollToBottom(): this;
44
51
  private _appendSingleMessage;
45
52
  connectedCallback(): void;
46
53
  disconnectedCallback(): void;
package/dist/component.js CHANGED
@@ -6,7 +6,7 @@ import { buildMessageRowHtml, setupTooltipForElement } from './utils/message-bui
6
6
  import { buildScrollButtonHtml } from './utils/scroll-button.js';
7
7
  export class BBMsgHistory extends HTMLElement {
8
8
  static get observedAttributes() {
9
- return ['theme', 'loading', 'hide-scroll-bar', 'infinite'];
9
+ return ['theme', 'loading', 'hide-scroll-bar', 'infinite', 'hide-scroll-button'];
10
10
  }
11
11
  constructor() {
12
12
  super();
@@ -19,7 +19,7 @@ export class BBMsgHistory extends HTMLElement {
19
19
  this.attachShadow({ mode: 'open' });
20
20
  }
21
21
  attributeChangedCallback(name) {
22
- if (name === 'theme' || name === 'loading' || name === 'hide-scroll-bar' || name === 'infinite') {
22
+ if (name === 'theme' || name === 'loading' || name === 'hide-scroll-bar' || name === 'infinite' || name === 'hide-scroll-button') {
23
23
  this.render();
24
24
  }
25
25
  }
@@ -76,6 +76,26 @@ export class BBMsgHistory extends HTMLElement {
76
76
  this._setupMutationObserver();
77
77
  return this;
78
78
  }
79
+ /**
80
+ * Scroll to the bottom of the message history.
81
+ *
82
+ * @example
83
+ * el.scrollToBottom(); // Scroll with smooth animation
84
+ */
85
+ scrollToBottom() {
86
+ if (this.hasAttribute('infinite')) {
87
+ return this;
88
+ }
89
+ const container = this.shadowRoot?.querySelector('.history');
90
+ if (!container) {
91
+ return this;
92
+ }
93
+ container.scrollTo({
94
+ top: container.scrollHeight,
95
+ behavior: 'smooth',
96
+ });
97
+ return this;
98
+ }
79
99
  _appendSingleMessage(message) {
80
100
  const container = this.shadowRoot.querySelector('.history');
81
101
  // If empty state or no container, do full render first
@@ -133,6 +153,12 @@ export class BBMsgHistory extends HTMLElement {
133
153
  if (scrollButton && this._scrollButtonVisible) {
134
154
  this._scrollButtonVisible = false;
135
155
  scrollButton.classList.remove('visible');
156
+ // Dispatch hide event
157
+ this.dispatchEvent(new CustomEvent('bb-scrollbuttonhide', {
158
+ bubbles: true,
159
+ composed: true,
160
+ detail: { visible: false }
161
+ }));
136
162
  }
137
163
  }
138
164
  }
@@ -233,12 +259,13 @@ export class BBMsgHistory extends HTMLElement {
233
259
  <div class="loading-spinner"></div>
234
260
  </div>`
235
261
  : '';
262
+ const hideScrollButton = this.hasAttribute('hide-scroll-button');
236
263
  this.shadowRoot.innerHTML = `
237
264
  <style>${MAIN_STYLES}${LOADING_STYLES}</style>
238
265
  <div class="history" role="log" aria-live="polite" aria-label="Message history">
239
266
  ${messagesHtml}
240
267
  </div>
241
- ${buildScrollButtonHtml()}
268
+ ${hideScrollButton ? '' : buildScrollButtonHtml()}
242
269
  ${loadingOverlay}
243
270
  `;
244
271
  this._setupAfterRender();
@@ -276,9 +303,12 @@ export class BBMsgHistory extends HTMLElement {
276
303
  _setupAfterRender() {
277
304
  requestAnimationFrame(() => {
278
305
  const container = this.shadowRoot.querySelector('.history');
279
- const scrollButton = this.shadowRoot.querySelector('.scroll-to-bottom');
306
+ const hideScrollButton = this.hasAttribute('hide-scroll-button');
307
+ const scrollButton = hideScrollButton
308
+ ? null
309
+ : this.shadowRoot.querySelector('.scroll-to-bottom');
280
310
  const isInfinite = this.hasAttribute('infinite');
281
- if (container && !isInfinite) {
311
+ if (container && !isInfinite && !hideScrollButton) {
282
312
  // Mark as programmatic scroll to prevent triggering user scroll detection
283
313
  this._isProgrammaticScroll = true;
284
314
  container.scrollTop = container.scrollHeight;
@@ -338,6 +368,12 @@ export class BBMsgHistory extends HTMLElement {
338
368
  if (shouldShow !== this._scrollButtonVisible) {
339
369
  this._scrollButtonVisible = shouldShow;
340
370
  button.classList.toggle('visible', shouldShow);
371
+ // Dispatch custom event
372
+ this.dispatchEvent(new CustomEvent(shouldShow ? 'bb-scrollbuttonshow' : 'bb-scrollbuttonhide', {
373
+ bubbles: true,
374
+ composed: true,
375
+ detail: { visible: shouldShow }
376
+ }));
341
377
  }
342
378
  };
343
379
  // Initialize last scroll position
@@ -71,6 +71,11 @@ export const MAIN_STYLES = `
71
71
  display: none;
72
72
  }
73
73
 
74
+ /* Hide scroll button when explicitly disabled */
75
+ :host([hide-scroll-button]) .scroll-to-bottom {
76
+ display: none;
77
+ }
78
+
74
79
  /* Scroll to bottom button */
75
80
  .scroll-to-bottom {
76
81
  position: absolute;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbki.ng/bb-msg-history",
3
- "version": "0.11.2",
3
+ "version": "0.13.0",
4
4
  "description": "A chat-style message history web component",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
package/src/component.ts CHANGED
@@ -17,7 +17,7 @@ export class BBMsgHistory extends HTMLElement {
17
17
  private _lastScrollTop = 0;
18
18
 
19
19
  static get observedAttributes() {
20
- return ['theme', 'loading', 'hide-scroll-bar', 'infinite'];
20
+ return ['theme', 'loading', 'hide-scroll-bar', 'infinite', 'hide-scroll-button'];
21
21
  }
22
22
 
23
23
  constructor() {
@@ -26,7 +26,7 @@ export class BBMsgHistory extends HTMLElement {
26
26
  }
27
27
 
28
28
  attributeChangedCallback(name: string) {
29
- if (name === 'theme' || name === 'loading' || name === 'hide-scroll-bar' || name === 'infinite') {
29
+ if (name === 'theme' || name === 'loading' || name === 'hide-scroll-bar' || name === 'infinite' || name === 'hide-scroll-button') {
30
30
  this.render();
31
31
  }
32
32
  }
@@ -92,6 +92,30 @@ export class BBMsgHistory extends HTMLElement {
92
92
  return this;
93
93
  }
94
94
 
95
+ /**
96
+ * Scroll to the bottom of the message history.
97
+ *
98
+ * @example
99
+ * el.scrollToBottom(); // Scroll with smooth animation
100
+ */
101
+ scrollToBottom(): this {
102
+ if (this.hasAttribute('infinite')) {
103
+ return this;
104
+ }
105
+
106
+ const container = this.shadowRoot?.querySelector('.history') as HTMLElement | null;
107
+ if (!container) {
108
+ return this;
109
+ }
110
+
111
+ container.scrollTo({
112
+ top: container.scrollHeight,
113
+ behavior: 'smooth',
114
+ });
115
+
116
+ return this;
117
+ }
118
+
95
119
  private _appendSingleMessage(message: Message): void {
96
120
  const container = this.shadowRoot!.querySelector('.history') as HTMLElement;
97
121
 
@@ -170,6 +194,15 @@ export class BBMsgHistory extends HTMLElement {
170
194
  if (scrollButton && this._scrollButtonVisible) {
171
195
  this._scrollButtonVisible = false;
172
196
  scrollButton.classList.remove('visible');
197
+
198
+ // Dispatch hide event
199
+ this.dispatchEvent(
200
+ new CustomEvent('bb-scrollbuttonhide', {
201
+ bubbles: true,
202
+ composed: true,
203
+ detail: { visible: false }
204
+ })
205
+ );
173
206
  }
174
207
  }
175
208
  }
@@ -293,12 +326,14 @@ export class BBMsgHistory extends HTMLElement {
293
326
  </div>`
294
327
  : '';
295
328
 
329
+ const hideScrollButton = this.hasAttribute('hide-scroll-button');
330
+
296
331
  this.shadowRoot!.innerHTML = `
297
332
  <style>${MAIN_STYLES}${LOADING_STYLES}</style>
298
333
  <div class="history" role="log" aria-live="polite" aria-label="Message history">
299
334
  ${messagesHtml}
300
335
  </div>
301
- ${buildScrollButtonHtml()}
336
+ ${hideScrollButton ? '' : buildScrollButtonHtml()}
302
337
  ${loadingOverlay}
303
338
  `;
304
339
 
@@ -345,17 +380,20 @@ export class BBMsgHistory extends HTMLElement {
345
380
  private _setupAfterRender(): void {
346
381
  requestAnimationFrame(() => {
347
382
  const container = this.shadowRoot!.querySelector('.history') as HTMLElement;
348
- const scrollButton = this.shadowRoot!.querySelector('.scroll-to-bottom') as HTMLButtonElement;
383
+ const hideScrollButton = this.hasAttribute('hide-scroll-button');
384
+ const scrollButton = hideScrollButton
385
+ ? null
386
+ : (this.shadowRoot!.querySelector('.scroll-to-bottom') as HTMLButtonElement);
349
387
  const isInfinite = this.hasAttribute('infinite');
350
388
 
351
- if (container && !isInfinite) {
389
+ if (container && !isInfinite && !hideScrollButton) {
352
390
  // Mark as programmatic scroll to prevent triggering user scroll detection
353
391
  this._isProgrammaticScroll = true;
354
392
  container.scrollTop = container.scrollHeight;
355
393
  requestAnimationFrame(() => {
356
394
  this._isProgrammaticScroll = false;
357
395
  });
358
- this._setupScrollTracking(container, scrollButton, { skipInitialCheck: true });
396
+ this._setupScrollTracking(container, scrollButton!, { skipInitialCheck: true });
359
397
  }
360
398
 
361
399
  if (scrollButton && !isInfinite) {
@@ -420,6 +458,15 @@ export class BBMsgHistory extends HTMLElement {
420
458
  if (shouldShow !== this._scrollButtonVisible) {
421
459
  this._scrollButtonVisible = shouldShow;
422
460
  button.classList.toggle('visible', shouldShow);
461
+
462
+ // Dispatch custom event
463
+ this.dispatchEvent(
464
+ new CustomEvent(shouldShow ? 'bb-scrollbuttonshow' : 'bb-scrollbuttonhide', {
465
+ bubbles: true,
466
+ composed: true,
467
+ detail: { visible: shouldShow }
468
+ })
469
+ );
423
470
  }
424
471
  };
425
472
 
@@ -72,6 +72,11 @@ export const MAIN_STYLES = `
72
72
  display: none;
73
73
  }
74
74
 
75
+ /* Hide scroll button when explicitly disabled */
76
+ :host([hide-scroll-button]) .scroll-to-bottom {
77
+ display: none;
78
+ }
79
+
75
80
  /* Scroll to bottom button */
76
81
  .scroll-to-bottom {
77
82
  position: absolute;