@bbki.ng/bb-msg-history 0.11.0 → 0.11.2

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.
@@ -5,6 +5,9 @@ export declare class BBMsgHistory extends HTMLElement {
5
5
  private _lastAuthor;
6
6
  private _lastGroupTimestamp;
7
7
  private _scrollButtonVisible;
8
+ private _userHasScrolledManually;
9
+ private _isProgrammaticScroll;
10
+ private _lastScrollTop;
8
11
  static get observedAttributes(): string[];
9
12
  constructor();
10
13
  attributeChangedCallback(name: string): void;
package/dist/component.js CHANGED
@@ -13,6 +13,9 @@ export class BBMsgHistory extends HTMLElement {
13
13
  this._userAuthors = new Map();
14
14
  this._lastAuthor = '';
15
15
  this._scrollButtonVisible = false;
16
+ this._userHasScrolledManually = false;
17
+ this._isProgrammaticScroll = false;
18
+ this._lastScrollTop = 0;
16
19
  this.attachShadow({ mode: 'open' });
17
20
  }
18
21
  attributeChangedCallback(name) {
@@ -115,10 +118,16 @@ export class BBMsgHistory extends HTMLElement {
115
118
  }
116
119
  // Smooth scroll to bottom (skip in infinite mode)
117
120
  if (!this.hasAttribute('infinite')) {
121
+ // Mark this as a programmatic scroll so the scroll handler ignores it
122
+ this._isProgrammaticScroll = true;
118
123
  container.scrollTo({
119
124
  top: container.scrollHeight,
120
125
  behavior: 'smooth',
121
126
  });
127
+ // Reset the flag after smooth scroll animation completes (~300ms)
128
+ setTimeout(() => {
129
+ this._isProgrammaticScroll = false;
130
+ }, 300);
122
131
  // Hide scroll button since we're scrolling to bottom
123
132
  const scrollButton = this.shadowRoot.querySelector('.scroll-to-bottom');
124
133
  if (scrollButton && this._scrollButtonVisible) {
@@ -270,8 +279,13 @@ export class BBMsgHistory extends HTMLElement {
270
279
  const scrollButton = this.shadowRoot.querySelector('.scroll-to-bottom');
271
280
  const isInfinite = this.hasAttribute('infinite');
272
281
  if (container && !isInfinite) {
282
+ // Mark as programmatic scroll to prevent triggering user scroll detection
283
+ this._isProgrammaticScroll = true;
273
284
  container.scrollTop = container.scrollHeight;
274
- this._setupScrollTracking(container, scrollButton);
285
+ requestAnimationFrame(() => {
286
+ this._isProgrammaticScroll = false;
287
+ });
288
+ this._setupScrollTracking(container, scrollButton, { skipInitialCheck: true });
275
289
  }
276
290
  if (scrollButton && !isInfinite) {
277
291
  scrollButton.addEventListener('click', () => {
@@ -304,19 +318,34 @@ export class BBMsgHistory extends HTMLElement {
304
318
  `;
305
319
  }
306
320
  }
307
- _setupScrollTracking(container, button) {
321
+ _setupScrollTracking(container, button, options) {
308
322
  const checkScrollPosition = () => {
323
+ // Ignore programmatic scrolls - they don't indicate user intent
324
+ if (this._isProgrammaticScroll)
325
+ return;
326
+ // Mark that user has manually scrolled
327
+ if (!this._userHasScrolledManually) {
328
+ this._userHasScrolledManually = true;
329
+ }
330
+ const currentScrollTop = container.scrollTop;
331
+ const isScrollingUp = currentScrollTop < this._lastScrollTop;
332
+ this._lastScrollTop = currentScrollTop;
309
333
  const threshold = 50; // pixels from bottom
310
334
  const isAtBottom = container.scrollHeight - container.scrollTop - container.clientHeight < threshold;
311
335
  const hasOverflow = container.scrollHeight > container.clientHeight;
312
- const shouldShow = !isAtBottom && hasOverflow;
336
+ // Only show button when: user has scrolled, is not at bottom, is scrolling up
337
+ const shouldShow = !isAtBottom && hasOverflow && this._userHasScrolledManually && isScrollingUp;
313
338
  if (shouldShow !== this._scrollButtonVisible) {
314
339
  this._scrollButtonVisible = shouldShow;
315
340
  button.classList.toggle('visible', shouldShow);
316
341
  }
317
342
  };
318
- // Check initial state
319
- checkScrollPosition();
343
+ // Initialize last scroll position
344
+ this._lastScrollTop = container.scrollTop;
345
+ // Check initial state unless skipped
346
+ if (!options?.skipInitialCheck) {
347
+ checkScrollPosition();
348
+ }
320
349
  // Listen for scroll events with passive listener for performance
321
350
  container.addEventListener('scroll', checkScrollPosition, { passive: true });
322
351
  // Also check on resize
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbki.ng/bb-msg-history",
3
- "version": "0.11.0",
3
+ "version": "0.11.2",
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
@@ -12,6 +12,9 @@ export class BBMsgHistory extends HTMLElement {
12
12
  private _lastAuthor = '';
13
13
  private _lastGroupTimestamp: string | undefined;
14
14
  private _scrollButtonVisible = false;
15
+ private _userHasScrolledManually = false;
16
+ private _isProgrammaticScroll = false;
17
+ private _lastScrollTop = 0;
15
18
 
16
19
  static get observedAttributes() {
17
20
  return ['theme', 'loading', 'hide-scroll-bar', 'infinite'];
@@ -149,11 +152,19 @@ export class BBMsgHistory extends HTMLElement {
149
152
 
150
153
  // Smooth scroll to bottom (skip in infinite mode)
151
154
  if (!this.hasAttribute('infinite')) {
155
+ // Mark this as a programmatic scroll so the scroll handler ignores it
156
+ this._isProgrammaticScroll = true;
157
+
152
158
  container.scrollTo({
153
159
  top: container.scrollHeight,
154
160
  behavior: 'smooth',
155
161
  });
156
162
 
163
+ // Reset the flag after smooth scroll animation completes (~300ms)
164
+ setTimeout(() => {
165
+ this._isProgrammaticScroll = false;
166
+ }, 300);
167
+
157
168
  // Hide scroll button since we're scrolling to bottom
158
169
  const scrollButton = this.shadowRoot!.querySelector('.scroll-to-bottom') as HTMLButtonElement;
159
170
  if (scrollButton && this._scrollButtonVisible) {
@@ -338,8 +349,13 @@ export class BBMsgHistory extends HTMLElement {
338
349
  const isInfinite = this.hasAttribute('infinite');
339
350
 
340
351
  if (container && !isInfinite) {
352
+ // Mark as programmatic scroll to prevent triggering user scroll detection
353
+ this._isProgrammaticScroll = true;
341
354
  container.scrollTop = container.scrollHeight;
342
- this._setupScrollTracking(container, scrollButton);
355
+ requestAnimationFrame(() => {
356
+ this._isProgrammaticScroll = false;
357
+ });
358
+ this._setupScrollTracking(container, scrollButton, { skipInitialCheck: true });
343
359
  }
344
360
 
345
361
  if (scrollButton && !isInfinite) {
@@ -376,13 +392,30 @@ export class BBMsgHistory extends HTMLElement {
376
392
  }
377
393
  }
378
394
 
379
- private _setupScrollTracking(container: HTMLElement, button: HTMLButtonElement): void {
395
+ private _setupScrollTracking(
396
+ container: HTMLElement,
397
+ button: HTMLButtonElement,
398
+ options?: { skipInitialCheck?: boolean }
399
+ ): void {
380
400
  const checkScrollPosition = () => {
401
+ // Ignore programmatic scrolls - they don't indicate user intent
402
+ if (this._isProgrammaticScroll) return;
403
+
404
+ // Mark that user has manually scrolled
405
+ if (!this._userHasScrolledManually) {
406
+ this._userHasScrolledManually = true;
407
+ }
408
+
409
+ const currentScrollTop = container.scrollTop;
410
+ const isScrollingUp = currentScrollTop < this._lastScrollTop;
411
+ this._lastScrollTop = currentScrollTop;
412
+
381
413
  const threshold = 50; // pixels from bottom
382
414
  const isAtBottom =
383
415
  container.scrollHeight - container.scrollTop - container.clientHeight < threshold;
384
416
  const hasOverflow = container.scrollHeight > container.clientHeight;
385
- const shouldShow = !isAtBottom && hasOverflow;
417
+ // Only show button when: user has scrolled, is not at bottom, is scrolling up
418
+ const shouldShow = !isAtBottom && hasOverflow && this._userHasScrolledManually && isScrollingUp;
386
419
 
387
420
  if (shouldShow !== this._scrollButtonVisible) {
388
421
  this._scrollButtonVisible = shouldShow;
@@ -390,8 +423,13 @@ export class BBMsgHistory extends HTMLElement {
390
423
  }
391
424
  };
392
425
 
393
- // Check initial state
394
- checkScrollPosition();
426
+ // Initialize last scroll position
427
+ this._lastScrollTop = container.scrollTop;
428
+
429
+ // Check initial state unless skipped
430
+ if (!options?.skipInitialCheck) {
431
+ checkScrollPosition();
432
+ }
395
433
 
396
434
  // Listen for scroll events with passive listener for performance
397
435
  container.addEventListener('scroll', checkScrollPosition, { passive: true });