@libs-ui/components-scroll-overlay 0.2.356-25 → 0.2.356-27
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 {
|
|
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
|
-
|
|
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
|
-
|
|
76
|
-
|
|
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()?.
|
|
79
|
-
scrollThumbColor = computed(() => this.options()?.scrollThumbColor ??
|
|
80
|
-
scrollThumbHoverColor = computed(() => this.options()?.scrollThumbHoverColor ??
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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('
|
|
188
|
-
this.Element.classList.add('
|
|
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('
|
|
192
|
-
this.divContainer.classList.add('
|
|
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
|
-
|
|
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
|
-
|
|
279
|
+
this.outScrollTop.emit(event);
|
|
280
|
+
return;
|
|
228
281
|
}
|
|
229
|
-
if (target.scrollHeight <= target.scrollTop + target.offsetHeight +
|
|
230
|
-
|
|
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
|
|
239
|
-
subs.add(
|
|
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(
|
|
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.
|
|
313
|
+
if (this.scrollRafX)
|
|
314
|
+
return;
|
|
315
|
+
this.scrollRafX = requestAnimationFrame(() => {
|
|
316
|
+
this.scrollRafX = undefined;
|
|
317
|
+
handle();
|
|
318
|
+
});
|
|
259
319
|
return;
|
|
260
320
|
}
|
|
261
|
-
if (
|
|
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
|
-
|
|
270
|
-
|
|
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
|
-
|
|
370
|
+
clearTimeout(this.mouseUpTimer);
|
|
371
|
+
this.mouseUpTimer = setTimeout(() => {
|
|
309
372
|
this.isScrollThumb.set(false);
|
|
310
|
-
},
|
|
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(
|
|
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(
|
|
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
|
|
361
|
-
|
|
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
|
|
368
|
-
|
|
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
|
}
|