@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.
- package/dist/component.d.ts +3 -0
- package/dist/component.js +28 -1
- package/package.json +1 -1
- package/src/component.ts +33 -1
package/dist/component.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
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
|
-
|
|
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();
|