@bbki.ng/bb-msg-history 0.11.1 → 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,7 +279,12 @@ 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;
285
+ requestAnimationFrame(() => {
286
+ this._isProgrammaticScroll = false;
287
+ });
274
288
  this._setupScrollTracking(container, scrollButton, { skipInitialCheck: true });
275
289
  }
276
290
  if (scrollButton && !isInfinite) {
@@ -306,15 +320,28 @@ export class BBMsgHistory extends HTMLElement {
306
320
  }
307
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
  };
343
+ // Initialize last scroll position
344
+ this._lastScrollTop = container.scrollTop;
318
345
  // Check initial state unless skipped
319
346
  if (!options?.skipInitialCheck) {
320
347
  checkScrollPosition();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbki.ng/bb-msg-history",
3
- "version": "0.11.1",
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,7 +349,12 @@ 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;
355
+ requestAnimationFrame(() => {
356
+ this._isProgrammaticScroll = false;
357
+ });
342
358
  this._setupScrollTracking(container, scrollButton, { skipInitialCheck: true });
343
359
  }
344
360
 
@@ -382,11 +398,24 @@ export class BBMsgHistory extends HTMLElement {
382
398
  options?: { skipInitialCheck?: boolean }
383
399
  ): void {
384
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
+
385
413
  const threshold = 50; // pixels from bottom
386
414
  const isAtBottom =
387
415
  container.scrollHeight - container.scrollTop - container.clientHeight < threshold;
388
416
  const hasOverflow = container.scrollHeight > container.clientHeight;
389
- 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;
390
419
 
391
420
  if (shouldShow !== this._scrollButtonVisible) {
392
421
  this._scrollButtonVisible = shouldShow;
@@ -394,6 +423,9 @@ export class BBMsgHistory extends HTMLElement {
394
423
  }
395
424
  };
396
425
 
426
+ // Initialize last scroll position
427
+ this._lastScrollTop = container.scrollTop;
428
+
397
429
  // Check initial state unless skipped
398
430
  if (!options?.skipInitialCheck) {
399
431
  checkScrollPosition();