@libs-ui/components-scroll-overlay 0.2.356-25 → 0.2.356-26

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.
@@ -1,83 +1,107 @@
1
1
  import * as i0 from '@angular/core';
2
- import { computed, signal, input, output, inject, ElementRef, Renderer2, DestroyRef, effect, untracked, Directive } from '@angular/core';
2
+ import { signal, computed, input, output, inject, ElementRef, Renderer2, DestroyRef, effect, untracked, Directive } from '@angular/core';
3
3
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
4
  import { getDragEventByElement, checkMouseOverInContainer } from '@libs-ui/utils';
5
5
  import { Subscription, fromEvent, tap, switchMap, interval, takeUntil } from 'rxjs';
6
6
 
7
- /* eslint-disable @typescript-eslint/no-explicit-any */
7
+ const STYLE_TAG_ID = 'libs-ui-scroll-overlay-style-tag';
8
+ const SCROLL_BOTTOM_TOLERANCE = 3;
9
+ const MIN_THUMB_SIZE = 20;
10
+ const MOUSE_UP_RESET_DELAY = 250;
11
+ const DEFAULT_SCROLLBAR_HOVER_COLOR = '#CDD0D640';
12
+ const DEFAULT_SCROLL_THUMB_COLOR = '#CDD0D6';
13
+ const DEFAULT_SCROLL_THUMB_HOVER_COLOR = '#9CA2AD';
14
+ /**
15
+ * CSS chia sẻ cho mọi instance. Màu/padding lấy qua CSS variables để mỗi
16
+ * directive instance có thể custom độc lập mà không phải re-write style tag.
17
+ */
18
+ const STYLE_CONTENT = `
19
+ .libs-ui-scroll-overlay-container .scrollbar-track {
20
+ background-color: var(--libs-ui-sb-track-color, transparent);
21
+ }
22
+ .libs-ui-scroll-overlay-container .scrollbar-track:hover {
23
+ background-color: var(--libs-ui-sb-track-hover-color, ${DEFAULT_SCROLLBAR_HOVER_COLOR});
24
+ }
25
+ .libs-ui-scroll-overlay-container .scrollbar-track-X {
26
+ width: 100%;
27
+ position: absolute;
28
+ bottom: 0;
29
+ left: 0;
30
+ visibility: hidden;
31
+ cursor: pointer;
32
+ opacity: 0;
33
+ z-index: 1;
34
+ transition: opacity 0.3s ease, visibility 0.3s ease;
35
+ }
36
+ .libs-ui-scroll-overlay-container .scrollbar-track-Y {
37
+ height: 100%;
38
+ position: absolute;
39
+ top: 0;
40
+ right: 0;
41
+ visibility: hidden;
42
+ cursor: pointer;
43
+ opacity: 0;
44
+ z-index: 1;
45
+ transition: opacity 0.3s ease, visibility 0.3s ease;
46
+ }
47
+ .libs-ui-scroll-overlay-container .scrollbar-thumb {
48
+ background-color: var(--libs-ui-sb-thumb-color, ${DEFAULT_SCROLL_THUMB_COLOR});
49
+ }
50
+ .libs-ui-scroll-overlay-container .scrollbar-thumb:hover {
51
+ background-color: var(--libs-ui-sb-thumb-hover-color, ${DEFAULT_SCROLL_THUMB_HOVER_COLOR});
52
+ }
53
+ .libs-ui-scroll-overlay-container .scrollbar-thumb-X {
54
+ height: calc(100% - var(--libs-ui-sb-padding-double, 4px));
55
+ bottom: var(--libs-ui-sb-padding, 2px);
56
+ border-radius: 4px;
57
+ cursor: grabbing;
58
+ transition: background-color 0.3s;
59
+ position: absolute;
60
+ }
61
+ .libs-ui-scroll-overlay-container .scrollbar-thumb-Y {
62
+ width: calc(100% - var(--libs-ui-sb-padding-double, 4px));
63
+ right: var(--libs-ui-sb-padding, 2px);
64
+ border-radius: 4px;
65
+ cursor: grabbing;
66
+ transition: background-color 0.3s;
67
+ position: absolute;
68
+ }
69
+ .libs-ui-scroll-overlay-container .libs-ui-scroll-overlay-element::-webkit-scrollbar {
70
+ width: 0 !important;
71
+ height: 0 !important;
72
+ }
73
+ `;
74
+ const ensureStyleTag = () => {
75
+ if (document.getElementById(STYLE_TAG_ID)) {
76
+ return;
77
+ }
78
+ const styleEl = document.createElement('style');
79
+ styleEl.id = STYLE_TAG_ID;
80
+ styleEl.textContent = STYLE_CONTENT;
81
+ document.head.append(styleEl);
82
+ };
8
83
  class LibsUiComponentsScrollOverlayDirective {
9
84
  // #region PROPERTY
10
- styles = computed(() => `
11
- .scrollbar-track{
12
- background-color:${this.scrollbarColor()};
13
- }
14
- .scrollbar-track:hover{
15
- background-color:${this.scrollbarHoverColor()};
16
- }
17
- .scrollbar-track-X {
18
- width:100%;
19
- position: absolute;
20
- bottom: 0;
21
- left: 0;
22
- visibility: hidden;
23
- cursor: pointer;
24
- opacity: 0;
25
- z-index: 1;
26
- transition: opacity 0.3s ease, visibility 0.3s ease;
27
- }
28
-
29
- .scrollbar-track-Y {
30
- height:100%;
31
- position: absolute;
32
- top: 0;
33
- right: 0;
34
- visibility: hidden;
35
- cursor: pointer;
36
- opacity: 0;
37
- z-index: 1;
38
- transition: opacity 0.3s ease, visibility 0.3s ease;
39
- }
40
-
41
- .scrollbar-thumb{
42
- background-color:${this.scrollThumbColor()};
43
- }
44
- .scrollbar-thumb:hover{
45
- background-color:${this.scrollThumbHoverColor()};
46
- }
47
-
48
- .scrollbar-thumb-X {
49
- height: calc(100% - ${this.scrollbarPadding() * 2}px);
50
- bottom: ${this.scrollbarPadding()}px;
51
- border-radius: 4px;
52
- cursor: grabbing;
53
- transition: background-color 0.3s;
54
- position: absolute;
55
- }
56
-
57
- .scrollbar-thumb-Y {
58
- width: calc(100% - ${this.scrollbarPadding() * 2}px);
59
- right: ${this.scrollbarPadding()}px;
60
- border-radius: 4px;
61
- cursor: grabbing;
62
- transition: background-color 0.3s;
63
- position: absolute;
64
- }
65
-
66
- .lib-ui-scroll-overlay-container .lib-ui-scroll-overlay-element::-webkit-scrollbar {
67
- width: 0 !important;
68
- height: 0 !important;
69
- }
70
- `, {});
71
85
  isScrollThumb = signal(false);
72
86
  keepDisplayThumb = signal(false);
73
87
  subsX = new Subscription();
74
88
  subsY = new Subscription();
75
- scrollbarWidth = computed(() => this.options()?.scrollbarWidth ?? 10); // Chiều rộng thanh cuộn
76
- scrollbarPadding = computed(() => this.options()?.scrollbarPadding ?? 2); // Chiều rộng thanh cuộn
89
+ /** Timer reset isScrollThumb sau drag end phải clear khi destroy để tránh set signal trên instance đã teardown. */
90
+ mouseUpTimer;
91
+ /** rAF throttle cho scroll handler — gom multiple scroll events về 1 lần update DOM mỗi frame. */
92
+ scrollRafX;
93
+ scrollRafY;
94
+ /** Cache dimensions — skip DOM write nếu kích thước không đổi giữa 2 lần poll. */
95
+ lastContentWidth = 0;
96
+ lastContentHeight = 0;
97
+ lastContainerWidth = 0;
98
+ lastContainerHeight = 0;
99
+ scrollbarWidth = computed(() => this.options()?.scrollbarWidth ?? 10);
100
+ scrollbarPadding = computed(() => this.options()?.scrollbarPadding ?? 2);
77
101
  scrollbarColor = computed(() => this.options()?.scrollbarColor ?? '');
78
- scrollbarHoverColor = computed(() => this.options()?.scrollbarColor ?? '#CDD0D640');
79
- scrollThumbColor = computed(() => this.options()?.scrollThumbColor ?? '#CDD0D6');
80
- scrollThumbHoverColor = computed(() => this.options()?.scrollThumbHoverColor ?? '#9CA2AD');
102
+ scrollbarHoverColor = computed(() => this.options()?.scrollbarHoverColor ?? DEFAULT_SCROLLBAR_HOVER_COLOR);
103
+ scrollThumbColor = computed(() => this.options()?.scrollThumbColor ?? DEFAULT_SCROLL_THUMB_COLOR);
104
+ scrollThumbHoverColor = computed(() => this.options()?.scrollThumbHoverColor ?? DEFAULT_SCROLL_THUMB_HOVER_COLOR);
81
105
  divContainer = document.createElement('div');
82
106
  trackX = document.createElement('div');
83
107
  thumbX = document.createElement('div');
@@ -102,58 +126,88 @@ class LibsUiComponentsScrollOverlayDirective {
102
126
  render2 = inject(Renderer2);
103
127
  destroyRef = inject(DestroyRef);
104
128
  constructor() {
129
+ // Setup scrollbars khi options/classContainer đổi.
105
130
  effect(() => {
106
131
  if (this.ignoreInit()) {
107
132
  return;
108
133
  }
109
134
  const options = this.options();
110
- this.divContainer.className = '';
111
- this.classContainer()
112
- ?.split(' ')
113
- .forEach((className) => {
114
- if (!className) {
115
- return;
116
- }
117
- this.divContainer.classList.add(className);
118
- });
135
+ const classContainer = this.classContainer();
119
136
  untracked(() => {
137
+ this.syncContainerExtraClasses(classContainer);
120
138
  this.Element.classList.toggle('overflow-hidden', options?.scrollX === 'hidden' && options?.scrollY === 'hidden');
121
139
  if (options?.scrollX !== 'hidden') {
122
140
  this.subsX.unsubscribe();
141
+ this.subsX = new Subscription();
123
142
  this.trackX.className = '';
124
143
  this.thumbX.className = '';
125
144
  this.trackX.style.height = `${this.scrollbarWidth()}px`;
126
145
  this.createScrollbar('X', this.trackX, this.thumbX);
127
- this.bindEventsScrollBar('X', this.trackX);
128
- this.handlerDragAndDropThumb('X');
129
- this.handlerClickTrack('X');
146
+ this.bindEventsScrollBar('X', this.trackX, this.subsX);
147
+ this.handlerDragAndDropThumb('X', this.subsX);
148
+ this.handlerClickTrack('X', this.subsX);
130
149
  }
131
150
  if (options?.scrollY !== 'hidden') {
151
+ this.subsY.unsubscribe();
152
+ this.subsY = new Subscription();
132
153
  this.trackY.className = '';
133
154
  this.thumbY.className = '';
134
155
  this.trackY.style.width = `${this.scrollbarWidth()}px`;
135
- this.subsY.unsubscribe();
136
156
  this.createScrollbar('Y', this.trackY, this.thumbY);
137
- this.bindEventsScrollBar('Y', this.trackY);
138
- this.handlerDragAndDropThumb('Y');
139
- this.handlerClickTrack('Y');
157
+ this.bindEventsScrollBar('Y', this.trackY, this.subsY);
158
+ this.handlerDragAndDropThumb('Y', this.subsY);
159
+ this.handlerClickTrack('Y', this.subsY);
140
160
  }
141
161
  });
142
162
  });
163
+ // Sync CSS variables per-instance — không đụng style tag chia sẻ giữa các instance.
164
+ effect(() => {
165
+ const sbColor = this.scrollbarColor();
166
+ const sbHover = this.scrollbarHoverColor();
167
+ const thColor = this.scrollThumbColor();
168
+ const thHover = this.scrollThumbHoverColor();
169
+ const padding = this.scrollbarPadding();
170
+ untracked(() => {
171
+ const divStyle = this.divContainer.style;
172
+ divStyle.setProperty('--libs-ui-sb-track-color', sbColor);
173
+ divStyle.setProperty('--libs-ui-sb-track-hover-color', sbHover);
174
+ divStyle.setProperty('--libs-ui-sb-thumb-color', thColor);
175
+ divStyle.setProperty('--libs-ui-sb-thumb-hover-color', thHover);
176
+ divStyle.setProperty('--libs-ui-sb-padding', `${padding}px`);
177
+ divStyle.setProperty('--libs-ui-sb-padding-double', `${padding * 2}px`);
178
+ });
179
+ });
180
+ // Window resize → re-measure thumb size cho cả 2 trục.
181
+ fromEvent(window, 'resize')
182
+ .pipe(takeUntilDestroyed(this.destroyRef))
183
+ .subscribe(() => {
184
+ this.updateScrollbarSize('X');
185
+ this.updateScrollbarSize('Y');
186
+ });
187
+ this.destroyRef.onDestroy(() => {
188
+ clearTimeout(this.mouseUpTimer);
189
+ if (this.scrollRafX)
190
+ cancelAnimationFrame(this.scrollRafX);
191
+ if (this.scrollRafY)
192
+ cancelAnimationFrame(this.scrollRafY);
193
+ this.divContainer.remove();
194
+ this.subsX.unsubscribe();
195
+ this.subsY.unsubscribe();
196
+ });
143
197
  }
144
198
  // #region FUNCTIONS
145
199
  get Element() {
146
200
  return this.elementScroll() || this.element.nativeElement;
147
201
  }
202
+ /** Thêm các class consumer truyền qua [classContainer] mà không xóa class hệ thống đã set. */
203
+ syncContainerExtraClasses(classStr) {
204
+ classStr
205
+ .split(' ')
206
+ .filter(Boolean)
207
+ .forEach((className) => this.divContainer.classList.add(className));
208
+ }
148
209
  createScrollbar(scrollDirection, trackEl, thumbEl) {
149
- const idStyleTag = '#id-style-tag-custom-scroll-overlay';
150
- const styleElCustomScrollOverlay = document.getElementById(idStyleTag);
151
- if (!styleElCustomScrollOverlay) {
152
- const styleEl = document.createElement('style');
153
- styleEl.setAttribute('id', idStyleTag);
154
- styleEl.innerHTML = this.styles();
155
- document.head.append(styleEl);
156
- }
210
+ ensureStyleTag();
157
211
  const stylesProperty = {
158
212
  'box-sizing': 'border-box',
159
213
  'scrollbar-width': 'none',
@@ -162,12 +216,10 @@ class LibsUiComponentsScrollOverlayDirective {
162
216
  'overflow-x': `${this.options()?.scrollX || 'scroll'}`,
163
217
  'overflow-y': `${this.options()?.scrollY || 'scroll'}`,
164
218
  };
165
- Object.keys(stylesProperty).forEach((key) => {
166
- this.render2.setStyle(this.Element, key, stylesProperty[key], 1);
167
- });
168
- trackEl.classList.add(`scrollbar-track`);
219
+ Object.keys(stylesProperty).forEach((key) => this.render2.setStyle(this.Element, key, stylesProperty[key], 1));
220
+ trackEl.classList.add('scrollbar-track');
169
221
  trackEl.classList.add(`scrollbar-track-${scrollDirection}`);
170
- thumbEl.classList.add(`scrollbar-thumb`);
222
+ thumbEl.classList.add('scrollbar-thumb');
171
223
  thumbEl.classList.add(`scrollbar-thumb-${scrollDirection}`);
172
224
  trackEl.appendChild(thumbEl);
173
225
  if (this.Element.className) {
@@ -184,12 +236,12 @@ class LibsUiComponentsScrollOverlayDirective {
184
236
  if (!this.Element.className.includes('min-w-')) {
185
237
  this.divContainer.classList.add('min-w-0');
186
238
  }
187
- if (!this.Element.className.includes('lib-ui-scroll-overlay-element')) {
188
- this.Element.classList.add('lib-ui-scroll-overlay-element');
239
+ if (!this.Element.className.includes('libs-ui-scroll-overlay-element')) {
240
+ this.Element.classList.add('libs-ui-scroll-overlay-element');
189
241
  }
190
242
  }
191
- if (!this.divContainer.classList.contains('lib-ui-scroll-overlay-container')) {
192
- this.divContainer.classList.add('lib-ui-scroll-overlay-container');
243
+ if (!this.divContainer.classList.contains('libs-ui-scroll-overlay-container')) {
244
+ this.divContainer.classList.add('libs-ui-scroll-overlay-container');
193
245
  }
194
246
  this.divContainer.appendChild(trackEl);
195
247
  if (!this.divContainer.style.position) {
@@ -199,11 +251,11 @@ class LibsUiComponentsScrollOverlayDirective {
199
251
  this.divContainer.append(this.Element);
200
252
  this.updateScrollbarSize(scrollDirection);
201
253
  }
202
- bindEventsScrollBar(scrollDirection, trackEl) {
254
+ bindEventsScrollBar(scrollDirection, trackEl, subs) {
203
255
  let scrollLeft = this.Element.scrollLeft;
204
256
  let scrollTop = this.Element.scrollTop;
205
- const subs = fromEvent(this.Element, 'scroll')
206
- .pipe(tap((event) => {
257
+ subs.add(fromEvent(this.Element, 'scroll')
258
+ .pipe(tap((event) => this.scheduleScrollHandle(scrollDirection, () => {
207
259
  const target = this.Element;
208
260
  this.outScroll.emit(event);
209
261
  if (scrollDirection === 'X') {
@@ -224,26 +276,26 @@ class LibsUiComponentsScrollOverlayDirective {
224
276
  scrollTop = target.scrollTop;
225
277
  this.outScrollY.emit(event);
226
278
  if (target.scrollTop === 0) {
227
- return this.outScrollTop.emit(event);
279
+ this.outScrollTop.emit(event);
280
+ return;
228
281
  }
229
- if (target.scrollHeight <= target.scrollTop + target.offsetHeight + 3) {
230
- return this.outScrollBottom.emit(event);
282
+ if (target.scrollHeight <= target.scrollTop + target.offsetHeight + SCROLL_BOTTOM_TOLERANCE) {
283
+ this.outScrollBottom.emit(event);
231
284
  }
232
- }), takeUntilDestroyed(this.destroyRef))
233
- .subscribe();
234
- subs.add(fromEvent(document, 'resize')
235
- .pipe(tap(this.updateScrollbarSize.bind(this, scrollDirection)), takeUntilDestroyed(this.destroyRef))
285
+ })), takeUntilDestroyed(this.destroyRef))
236
286
  .subscribe());
237
287
  const mouseLeave = fromEvent(this.divContainer, 'mouseleave');
238
- const mouseenter = fromEvent(this.divContainer, 'mouseenter');
239
- subs.add(mouseenter
288
+ const mouseEnter = fromEvent(this.divContainer, 'mouseenter');
289
+ subs.add(mouseEnter
240
290
  .pipe(tap(() => {
241
291
  if ((scrollDirection === 'X' && !this.options()?.scrollXOpacity0) || (scrollDirection === 'Y' && !this.options()?.scrollYOpacity0)) {
242
292
  trackEl.style.visibility = 'visible';
243
293
  trackEl.style.opacity = '1';
244
294
  }
245
295
  this.updateScrollbarSize(scrollDirection);
246
- }), switchMap(() => interval(1000).pipe(takeUntil(mouseLeave))), tap(this.updateScrollbarSize.bind(this, scrollDirection)), takeUntilDestroyed(this.destroyRef))
296
+ }), switchMap(() => interval(1000).pipe(takeUntil(mouseLeave))), tap(() => {
297
+ this.updateScrollbarSize(scrollDirection);
298
+ }), takeUntilDestroyed(this.destroyRef))
247
299
  .subscribe());
248
300
  subs.add(mouseLeave
249
301
  .pipe(tap(() => {
@@ -254,20 +306,31 @@ class LibsUiComponentsScrollOverlayDirective {
254
306
  trackEl.style.opacity = '0';
255
307
  }), takeUntilDestroyed(this.destroyRef))
256
308
  .subscribe());
309
+ }
310
+ /** Throttle scroll handler theo frame — gom nhiều scroll event vào 1 lần update DOM. */
311
+ scheduleScrollHandle(scrollDirection, handle) {
257
312
  if (scrollDirection === 'X') {
258
- this.subsX = subs;
313
+ if (this.scrollRafX)
314
+ return;
315
+ this.scrollRafX = requestAnimationFrame(() => {
316
+ this.scrollRafX = undefined;
317
+ handle();
318
+ });
259
319
  return;
260
320
  }
261
- if (scrollDirection === 'Y') {
262
- this.subsY = subs;
321
+ if (this.scrollRafY)
263
322
  return;
264
- }
323
+ this.scrollRafY = requestAnimationFrame(() => {
324
+ this.scrollRafY = undefined;
325
+ handle();
326
+ });
265
327
  }
266
- handlerClickTrack(scrollDirection) {
328
+ handlerClickTrack(scrollDirection, subs) {
267
329
  const elementTrack = scrollDirection === 'X' ? this.trackX : this.trackY;
268
330
  const elementThumb = scrollDirection === 'X' ? this.thumbX : this.thumbY;
269
- const subs = scrollDirection === 'X' ? this.subsX : this.subsY;
270
- subs.add(fromEvent(elementTrack, 'click').subscribe((e) => {
331
+ subs.add(fromEvent(elementTrack, 'click')
332
+ .pipe(takeUntilDestroyed(this.destroyRef))
333
+ .subscribe((e) => {
271
334
  if (this.isScrollThumb()) {
272
335
  return;
273
336
  }
@@ -282,10 +345,9 @@ class LibsUiComponentsScrollOverlayDirective {
282
345
  this.updateScrollPositionByUserAction(scrollDirection, e, 'smooth', -1 * elementThumb.getBoundingClientRect().height);
283
346
  }));
284
347
  }
285
- handlerDragAndDropThumb(scrollDirection) {
348
+ handlerDragAndDropThumb(scrollDirection, subs) {
286
349
  const elementTrack = scrollDirection === 'X' ? this.trackX : this.trackY;
287
350
  const elementThumb = scrollDirection === 'X' ? this.thumbX : this.thumbY;
288
- const subs = scrollDirection === 'X' ? this.subsX : this.subsY;
289
351
  let lengthThumbToPointClick = 0;
290
352
  subs.add(getDragEventByElement({
291
353
  elementMouseDown: elementThumb,
@@ -305,9 +367,10 @@ class LibsUiComponentsScrollOverlayDirective {
305
367
  elementTrack.style.visibility = 'hidden';
306
368
  elementTrack.style.opacity = '0';
307
369
  }
308
- setTimeout(() => {
370
+ clearTimeout(this.mouseUpTimer);
371
+ this.mouseUpTimer = setTimeout(() => {
309
372
  this.isScrollThumb.set(false);
310
- }, 250);
373
+ }, MOUSE_UP_RESET_DELAY);
311
374
  },
312
375
  destroyRef: this.destroyRef,
313
376
  }).subscribe((mouseEvent) => {
@@ -334,45 +397,48 @@ class LibsUiComponentsScrollOverlayDirective {
334
397
  if (scrollDirection === 'X') {
335
398
  const containerWidth = this.Element.offsetWidth;
336
399
  const contentWidth = (this.elementCheckScrollX() || this.Element).scrollWidth;
400
+ if (containerWidth === this.lastContainerWidth && contentWidth === this.lastContentWidth)
401
+ return;
402
+ this.lastContainerWidth = containerWidth;
403
+ this.lastContentWidth = contentWidth;
337
404
  const thumbWidth = (containerWidth / contentWidth) * containerWidth;
338
- this.thumbX.style.width = `${Math.max(20, thumbWidth)}px`;
339
- this.trackX.style.display = 'none';
340
- if (contentWidth > containerWidth) {
341
- this.trackX.style.display = 'block';
342
- }
405
+ this.thumbX.style.width = `${Math.max(MIN_THUMB_SIZE, thumbWidth)}px`;
406
+ this.trackX.style.display = contentWidth > containerWidth ? 'block' : 'none';
343
407
  this.updateScrollbarPosition(scrollDirection);
344
408
  return;
345
409
  }
346
410
  const containerHeight = this.Element.offsetHeight;
347
411
  const contentHeight = (this.elementCheckScrollY() || this.Element).scrollHeight;
412
+ if (containerHeight === this.lastContainerHeight && contentHeight === this.lastContentHeight)
413
+ return;
414
+ this.lastContainerHeight = containerHeight;
415
+ this.lastContentHeight = contentHeight;
348
416
  const thumbHeight = (containerHeight / contentHeight) * containerHeight;
349
- this.thumbY.style.height = `${Math.max(20, thumbHeight)}px`;
350
- this.trackY.style.display = 'none';
351
- if (contentHeight > containerHeight) {
352
- this.trackY.style.display = 'block';
353
- }
417
+ this.thumbY.style.height = `${Math.max(MIN_THUMB_SIZE, thumbHeight)}px`;
418
+ this.trackY.style.display = contentHeight > containerHeight ? 'block' : 'none';
354
419
  this.updateScrollbarPosition('Y');
355
420
  }
356
421
  updateScrollbarPosition(scrollDirection) {
357
422
  if (scrollDirection === 'X') {
358
423
  const containerWidth = this.Element.offsetWidth;
359
424
  const contentWidth = (this.elementCheckScrollX() || this.Element).scrollWidth;
360
- const scrollLeft = this.Element.scrollLeft;
361
- const thumbPosition = (scrollLeft / (contentWidth - containerWidth)) * (containerWidth - this.thumbX.offsetWidth);
425
+ const denominatorScroll = contentWidth - containerWidth;
426
+ if (denominatorScroll <= 0)
427
+ return;
428
+ const denominatorThumb = containerWidth - this.thumbX.offsetWidth;
429
+ const thumbPosition = (this.Element.scrollLeft / denominatorScroll) * denominatorThumb;
362
430
  this.thumbX.style.left = `${thumbPosition}px`;
363
431
  return;
364
432
  }
365
433
  const containerHeight = this.Element.offsetHeight;
366
434
  const contentHeight = (this.elementCheckScrollY() || this.Element).scrollHeight;
367
- const scrollTop = this.Element.scrollTop;
368
- const thumbPosition = (scrollTop / (contentHeight - containerHeight)) * (containerHeight - this.thumbY.offsetHeight);
435
+ const denominatorScroll = contentHeight - containerHeight;
436
+ if (denominatorScroll <= 0)
437
+ return;
438
+ const denominatorThumb = containerHeight - this.thumbY.offsetHeight;
439
+ const thumbPosition = (this.Element.scrollTop / denominatorScroll) * denominatorThumb;
369
440
  this.thumbY.style.top = `${thumbPosition}px`;
370
441
  }
371
- ngOnDestroy() {
372
- this.divContainer.remove();
373
- this.subsX.unsubscribe();
374
- this.subsY.unsubscribe();
375
- }
376
442
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiComponentsScrollOverlayDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
377
443
  static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.14", type: LibsUiComponentsScrollOverlayDirective, isStandalone: true, selector: "[LibsUiComponentsScrollOverlayDirective]", inputs: { debugMode: { classPropertyName: "debugMode", publicName: "debugMode", isSignal: true, isRequired: false, transformFunction: null }, ignoreInit: { classPropertyName: "ignoreInit", publicName: "ignoreInit", isSignal: true, isRequired: false, transformFunction: null }, classContainer: { classPropertyName: "classContainer", publicName: "classContainer", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, elementCheckScrollX: { classPropertyName: "elementCheckScrollX", publicName: "elementCheckScrollX", isSignal: true, isRequired: false, transformFunction: null }, elementCheckScrollY: { classPropertyName: "elementCheckScrollY", publicName: "elementCheckScrollY", isSignal: true, isRequired: false, transformFunction: null }, elementScroll: { classPropertyName: "elementScroll", publicName: "elementScroll", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { outScroll: "outScroll", outScrollX: "outScrollX", outScrollY: "outScrollY", outScrollTop: "outScrollTop", outScrollBottom: "outScrollBottom" }, ngImport: i0 });
378
444
  }