@inizioevoke/astro-core 1.1.1 → 1.3.0
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/package.json +4 -4
- package/src/components/ISI/RightISI.astro +43 -0
- package/src/components/ISI/RightISI.css +31 -0
- package/src/components/ISI/RightISI.md +23 -0
- package/src/components/ISI/RightISI.ts +27 -0
- package/src/components/Modal/Modal.ts +83 -4
- package/src/components/Modal/index.ts +2 -0
- package/src/components/PdfViewer/PdfViewer.ts +2 -2
- package/src/components/PdfViewer/pdf.min.mjs +4 -4
- package/src/components/PdfViewer/pdf.worker.min.mjs +4 -4
- package/src/components/ScrollContainer/ScrollContainer.astro +1 -1
- package/src/components/ScrollContainer/ScrollContainer.ts +207 -96
- package/src/components/ScrollContainer/archive-20260417.ts +150 -0
- package/src/components/ScrollContainer/index.ts +2 -0
- package/src/components/PdfViewer/pdf.mjs +0 -26465
- package/src/components/PdfViewer/pdf.mjs.map +0 -1
- package/src/components/PdfViewer/pdf.worker.mjs +0 -63057
- package/src/components/PdfViewer/pdf.worker.mjs.map +0 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { HTMLAttributes } from 'astro/types';
|
|
3
3
|
import './ScrollContainer.css';
|
|
4
4
|
|
|
5
|
-
interface Props extends HTMLAttributes<'div'> {
|
|
5
|
+
export interface Props extends HTMLAttributes<'div'> {
|
|
6
6
|
outerWidth?: string | number;
|
|
7
7
|
innerWidth?: string | number;
|
|
8
8
|
height?: string | number;
|
|
@@ -1,97 +1,225 @@
|
|
|
1
|
+
export class ScrollContainer {
|
|
2
|
+
#container: HTMLElement;
|
|
3
|
+
#content: HTMLElement;
|
|
4
|
+
#track: HTMLElement;
|
|
5
|
+
#thumb: HTMLElement;
|
|
6
|
+
#isDragging: boolean;
|
|
7
|
+
#startY: number;
|
|
8
|
+
#startScrollTop: number;
|
|
9
|
+
// #enabled: boolean;
|
|
10
|
+
|
|
11
|
+
#mouseupHandler: any = undefined; // [] = [];
|
|
12
|
+
#mousemoveHandler: any = undefined; // [] = [];
|
|
1
13
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
constructor(container: HTMLElement) { // , { enabled }: { enabled?: boolean } = {}) {
|
|
15
|
+
const _this = this;
|
|
16
|
+
this.#container = container;
|
|
17
|
+
this.#content = container.querySelector('.evo-scroll-content') as HTMLElement;
|
|
18
|
+
this.#track = container.querySelector('.evo-scroll-track') as HTMLElement;
|
|
19
|
+
this.#thumb = container.querySelector('.evo-scroll-thumb') as HTMLElement;
|
|
20
|
+
this.#isDragging = false;
|
|
21
|
+
this.#startY = 0;
|
|
22
|
+
this.#startScrollTop = 0;
|
|
23
|
+
// this.#enabled = enabled ?? true;
|
|
24
|
+
|
|
25
|
+
this.#thumb.addEventListener('mousedown', (e) => {
|
|
26
|
+
_this.#thumbMouseDown(e);
|
|
27
|
+
}, { passive: true });
|
|
28
|
+
|
|
29
|
+
// this.#thumb.addEventListener('mouseup', (e) => {
|
|
30
|
+
// _this.#thumbMouseUp(e);
|
|
31
|
+
// }, { passive: true });
|
|
32
|
+
|
|
33
|
+
this.#content.addEventListener('scroll', () => {
|
|
34
|
+
_this.#scrollContent();
|
|
35
|
+
}, { passive: true });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// enable(flag?: boolean) {
|
|
39
|
+
// this.#enabled = flag ?? true;
|
|
40
|
+
// }
|
|
41
|
+
// get isEnabled() {
|
|
42
|
+
// return this.#enabled === true;
|
|
43
|
+
// }
|
|
44
|
+
|
|
45
|
+
scroll({ left, top, behavior = 'auto' }: { left?: number | string, top?: number | string, behavior?: 'auto' | 'instant' | 'smooth' } = {}) {
|
|
46
|
+
const current = this.getScroll();
|
|
47
|
+
if (typeof left == 'string') {
|
|
48
|
+
if (/^[\+\-]=?\d+/.test(left)) {
|
|
49
|
+
left = current.left + parseInt(left.replace(/=/,''), 10);
|
|
50
|
+
} else {
|
|
51
|
+
left = parseInt(left, 10);
|
|
52
|
+
}
|
|
53
|
+
if (isNaN(left)) {
|
|
54
|
+
left = undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (typeof top == 'string') {
|
|
58
|
+
if (/^[\+\-]=?\d+/.test(top)) {
|
|
59
|
+
top = current.top + parseInt(top.replace(/=/,''), 10);
|
|
60
|
+
} else {
|
|
61
|
+
top = parseInt(top, 10);
|
|
62
|
+
}
|
|
63
|
+
if (isNaN(top)) {
|
|
64
|
+
top = undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
left = left ?? current.left;
|
|
68
|
+
top = top ?? current.top;
|
|
69
|
+
if (left < 0) {
|
|
70
|
+
left = 0;
|
|
71
|
+
}
|
|
72
|
+
if (top < 0) {
|
|
73
|
+
top = 0;
|
|
74
|
+
}
|
|
75
|
+
this.#content.scroll({ left: left, top: top, behavior });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getScroll() {
|
|
79
|
+
return {
|
|
80
|
+
left: this.#content.scrollLeft,
|
|
81
|
+
top: this.#content.scrollTop
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
resize({ height }: { height?: number }) {
|
|
86
|
+
if (typeof height == 'number') {
|
|
87
|
+
this.#container.style.height = `${height}px`;
|
|
88
|
+
refresh();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
refresh() {
|
|
93
|
+
this.#updateThumbSize();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
#updateThumbSize() {
|
|
97
|
+
const scrollHeight = this.#content.scrollHeight - getPaddingY(this.#content);
|
|
98
|
+
const containerHeight = this.#container.clientHeight - getPaddingY(this.#container);
|
|
99
|
+
const visibleRatio = containerHeight / scrollHeight;
|
|
100
|
+
const thumbHeight = visibleRatio * containerHeight;
|
|
101
|
+
this.#thumb.style.height = `${thumbHeight > 20 ? thumbHeight : 20}px`;
|
|
102
|
+
this.#track.style.display = visibleRatio < 1 ? 'block' : 'none';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#thumbMouseDown(e: MouseEvent) {
|
|
106
|
+
this.#mouseupHandler = this.#mouseDetach.bind(this); // .push(this.#mouseDetach.bind(this));
|
|
107
|
+
this.#mousemoveHandler = this.#thumbMouseMove.bind(this); // .push(this.#thumbMouseMove.bind(this));
|
|
108
|
+
document.addEventListener('mouseup', this.#mouseupHandler, { passive: true }); // this.#mouseupHandler[this.#mouseupHandler.length - 1]);
|
|
109
|
+
document.addEventListener('dragend', this.#mouseupHandler, { passive: true }); // this.#mouseupHandler[this.#mouseupHandler.length - 1]);
|
|
110
|
+
document.addEventListener('mousemove', this.#mousemoveHandler, { passive: true }); // this.#mousemoveHandler[this.#mousemoveHandler.length - 1]);
|
|
111
|
+
this.#isDragging = true;
|
|
112
|
+
this.#startY = e.clientY;
|
|
113
|
+
this.#startScrollTop = this.#content.scrollTop;
|
|
114
|
+
this.#thumb.style.cursor = 'grabbing';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
#thumbMouseMove(e: MouseEvent) {
|
|
118
|
+
// console.log(performance.now());
|
|
119
|
+
if (this.#isDragging) {
|
|
120
|
+
const deltaY = e.clientY - this.#startY;
|
|
121
|
+
const scrollRatio =
|
|
122
|
+
(this.#content.scrollHeight - this.#container.clientHeight) /
|
|
123
|
+
(this.#container.clientHeight - this.#thumb.clientHeight);
|
|
124
|
+
this.#content.scrollTop = this.#startScrollTop + deltaY * scrollRatio;
|
|
125
|
+
} else {
|
|
126
|
+
this.#mouseDetach();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// #thumbMouseUp(e: MouseEvent) {
|
|
131
|
+
// this.#isDragging = false;
|
|
132
|
+
// this.#thumb.style.cursor = 'grab';
|
|
133
|
+
// }
|
|
134
|
+
|
|
135
|
+
#mouseDetach() {
|
|
136
|
+
this.#isDragging = false;
|
|
137
|
+
this.#thumb.style.cursor = 'grab';
|
|
138
|
+
document.removeEventListener('mouseup', this.#mouseupHandler);
|
|
139
|
+
document.removeEventListener('dragend', this.#mouseupHandler);
|
|
140
|
+
document.removeEventListener('mousemove', this.#mousemoveHandler);
|
|
141
|
+
// for (const m of this.#mouseupHandler) {
|
|
142
|
+
// document.removeEventListener('mouseup', m);
|
|
143
|
+
// document.removeEventListener('dragend', m);
|
|
144
|
+
// }
|
|
145
|
+
// for (const m of this.#mousemoveHandler) {
|
|
146
|
+
// document.removeEventListener('mousemove', m);
|
|
147
|
+
// }
|
|
148
|
+
// this.#mouseupHandler = [];
|
|
149
|
+
// this.#mousemoveHandler = [];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#scrollContent() {
|
|
153
|
+
const scrollHeight = this.#content.scrollHeight - getPaddingY(this.#content);
|
|
154
|
+
const containerHeight = this.#container.clientHeight - getPaddingY(this.#container);
|
|
155
|
+
const scrollRatio = this.#content.scrollTop / (scrollHeight - containerHeight);
|
|
156
|
+
const top = scrollRatio * (this.#track.clientHeight - this.#thumb.clientHeight);
|
|
157
|
+
this.#thumb.style.top = `${top}px`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function resize(scrollContainer: string | HTMLElement | ScrollContainer, { height }: { height?: number }) {
|
|
163
|
+
if (typeof height == 'number') {
|
|
164
|
+
const sc = getScrollContainer(scrollContainer);
|
|
165
|
+
if (sc) {
|
|
166
|
+
sc.resize({ height })
|
|
167
|
+
}
|
|
168
|
+
}
|
|
10
169
|
}
|
|
11
170
|
|
|
12
|
-
export function refresh(scrollContainer?:
|
|
171
|
+
export function refresh(scrollContainer?: string | HTMLElement | ScrollContainer) {
|
|
13
172
|
if (scrollContainer) {
|
|
14
|
-
|
|
173
|
+
const sc = getScrollContainer(scrollContainer);
|
|
174
|
+
if (sc) {
|
|
175
|
+
sc.refresh()
|
|
176
|
+
}
|
|
15
177
|
} else {
|
|
16
178
|
[...scrollContainers.values()].forEach((scrollContainer) => {
|
|
17
|
-
|
|
179
|
+
scrollContainer.refresh();
|
|
18
180
|
});
|
|
19
181
|
}
|
|
20
182
|
}
|
|
21
183
|
|
|
22
|
-
export function getScroll(scrollContainer:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
184
|
+
export function getScroll(scrollContainer: string | HTMLElement | ScrollContainer): { left: number, top: number } | undefined {
|
|
185
|
+
const sc = getScrollContainer(scrollContainer);
|
|
186
|
+
if (sc) {
|
|
187
|
+
return sc.getScroll();
|
|
188
|
+
} else {
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
27
191
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
192
|
+
|
|
193
|
+
export function scroll(scrollContainer: string | HTMLElement | ScrollContainer, { left, top, behavior = 'auto' }: { left?: number, top?: number, behavior?: 'auto' | 'instant' | 'smooth' } = {}) {
|
|
194
|
+
const sc = getScrollContainer(scrollContainer);
|
|
195
|
+
if (sc) {
|
|
196
|
+
const current = sc.getScroll();
|
|
197
|
+
sc.scroll({ left: left ?? current.left, top: top ?? current.top, behavior });
|
|
198
|
+
}
|
|
31
199
|
}
|
|
32
200
|
|
|
33
|
-
export function getScrollContainer(selector: string | HTMLElement):
|
|
201
|
+
export function getScrollContainer(selector: string | HTMLElement | ScrollContainer): ScrollContainer | undefined {
|
|
202
|
+
if (selector instanceof ScrollContainer) {
|
|
203
|
+
return selector;
|
|
204
|
+
}
|
|
34
205
|
const key = typeof selector == 'string' ? document.querySelector<HTMLElement>(selector) : selector;
|
|
35
206
|
return key ? scrollContainers.get(key) : undefined;
|
|
36
207
|
}
|
|
37
208
|
|
|
38
|
-
function thumbMouseDown(scrollContainer: IScrollContainer, e: MouseEvent) {
|
|
39
|
-
scrollContainer.isDragging = true;
|
|
40
|
-
scrollContainer.startY = e.clientY;
|
|
41
|
-
scrollContainer.startScrollTop = scrollContainer.content.scrollTop;
|
|
42
|
-
scrollContainer.thumb.style.cursor = 'grabbing';
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function updateThumbSize(scrollContainer: IScrollContainer) {
|
|
46
|
-
const { container, content, thumb, track } = scrollContainer;
|
|
47
|
-
const scrollHeight = content.scrollHeight - getPaddingY(content);
|
|
48
|
-
const containerHeight = container.clientHeight - getPaddingY(container);
|
|
49
|
-
const visibleRatio = containerHeight / scrollHeight;
|
|
50
|
-
const thumbHeight = visibleRatio * containerHeight;
|
|
51
|
-
thumb.style.height = `${thumbHeight > 20 ? thumbHeight : 20}px`;
|
|
52
|
-
track.style.display = visibleRatio < 1 ? 'block' : 'none';
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function scrollContent(scrollContainer: IScrollContainer) {
|
|
56
|
-
const { container, content, thumb, track } = scrollContainer;
|
|
57
|
-
const scrollHeight = content.scrollHeight - getPaddingY(content);
|
|
58
|
-
const containerHeight = container.clientHeight - getPaddingY(container);
|
|
59
|
-
const scrollRatio = content.scrollTop / (scrollHeight - containerHeight);
|
|
60
|
-
const top = scrollRatio * (track.clientHeight - thumb.clientHeight);
|
|
61
|
-
thumb.style.top = `${top}px`;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
209
|
function getPaddingY(content: HTMLElement): number {
|
|
65
210
|
const style = getComputedStyle(content);
|
|
66
211
|
return parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);
|
|
67
212
|
}
|
|
68
213
|
|
|
69
|
-
|
|
70
|
-
let container = element;
|
|
71
|
-
while (container && !container.classList.contains('scroll-container')) {
|
|
72
|
-
container = container.parentElement as HTMLElement;
|
|
73
|
-
}
|
|
74
|
-
return container;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function createScrollContainer(container: HTMLElement): IScrollContainer {
|
|
78
|
-
return {
|
|
79
|
-
container: container,
|
|
80
|
-
content: container.querySelector('.evo-scroll-content') as HTMLElement,
|
|
81
|
-
track: container.querySelector('.evo-scroll-track') as HTMLElement,
|
|
82
|
-
thumb: container.querySelector('.evo-scroll-thumb') as HTMLElement,
|
|
83
|
-
isDragging: false,
|
|
84
|
-
startY: 0,
|
|
85
|
-
startScrollTop: 0
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const scrollContainers = new Map<HTMLElement, IScrollContainer>();
|
|
214
|
+
const scrollContainers = new Map<HTMLElement, ScrollContainer>();
|
|
90
215
|
|
|
91
216
|
const intersectionObserver = new IntersectionObserver((entries) => {
|
|
92
217
|
entries.forEach((entry) => {
|
|
93
218
|
if (entry.isIntersecting) {
|
|
94
|
-
|
|
219
|
+
const sc = getScrollContainer(entry.target as HTMLElement);
|
|
220
|
+
if (sc) {
|
|
221
|
+
sc.refresh();
|
|
222
|
+
}
|
|
95
223
|
}
|
|
96
224
|
});
|
|
97
225
|
},
|
|
@@ -99,45 +227,28 @@ const intersectionObserver = new IntersectionObserver((entries) => {
|
|
|
99
227
|
);
|
|
100
228
|
|
|
101
229
|
const mutationObserver = new MutationObserver((mutations) => {
|
|
102
|
-
const containers
|
|
230
|
+
const containers = new Set<ScrollContainer>();
|
|
103
231
|
mutations.forEach((mutation) => {
|
|
104
|
-
const sc =
|
|
105
|
-
if (sc
|
|
106
|
-
containers.
|
|
232
|
+
const sc = getScrollContainer(mutation.target as HTMLElement);
|
|
233
|
+
if (sc) {
|
|
234
|
+
containers.add(sc);
|
|
107
235
|
}
|
|
108
236
|
});
|
|
109
|
-
containers.forEach((container) => {
|
|
110
|
-
|
|
237
|
+
[...containers].forEach((container) => {
|
|
238
|
+
container.refresh();
|
|
111
239
|
});
|
|
112
240
|
});
|
|
113
241
|
|
|
114
242
|
document.querySelectorAll<HTMLElement>('.evo-scroll-container').forEach((container) => {
|
|
115
|
-
const scrollContainer =
|
|
243
|
+
const scrollContainer = new ScrollContainer(container);
|
|
116
244
|
scrollContainers.set(container, scrollContainer);
|
|
117
|
-
|
|
118
|
-
scrollContainer.content.addEventListener('scroll', (e) => { scrollContent(scrollContainer); }, { passive: true });
|
|
245
|
+
|
|
119
246
|
intersectionObserver.observe(container);
|
|
120
|
-
mutationObserver.observe(container, { childList: true, subtree: true });
|
|
121
|
-
});
|
|
122
|
-
document.addEventListener('mousemove', (e) => {
|
|
123
|
-
[...scrollContainers.values()].forEach((scrollContainer) => {
|
|
124
|
-
if (!scrollContainer.isDragging) return;
|
|
125
|
-
const { container, content, thumb } = scrollContainer;
|
|
126
|
-
const deltaY = e.clientY - scrollContainer.startY;
|
|
127
|
-
const scrollRatio =
|
|
128
|
-
(content.scrollHeight - container.clientHeight) /
|
|
129
|
-
(container.clientHeight - thumb.clientHeight);
|
|
130
|
-
scrollContainer.content.scrollTop = scrollContainer.startScrollTop + deltaY * scrollRatio;
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
document.addEventListener('mouseup', () => {
|
|
134
|
-
[...scrollContainers.values()].forEach((scrollContainer) => {
|
|
135
|
-
scrollContainer.isDragging = false;
|
|
136
|
-
scrollContainer.thumb.style.cursor = 'grab';
|
|
137
|
-
});
|
|
247
|
+
mutationObserver.observe(container, { attributes: true, childList: true, subtree: true });
|
|
138
248
|
});
|
|
249
|
+
|
|
139
250
|
window.addEventListener('resize', () => {
|
|
140
251
|
[...scrollContainers.values()].forEach((scrollContainer) => {
|
|
141
|
-
|
|
252
|
+
scrollContainer.refresh();
|
|
142
253
|
});
|
|
143
254
|
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
|
|
2
|
+
export interface IScrollContainer {
|
|
3
|
+
container: HTMLElement;
|
|
4
|
+
content: HTMLElement;
|
|
5
|
+
track: HTMLElement;
|
|
6
|
+
thumb: HTMLElement;
|
|
7
|
+
isDragging: boolean;
|
|
8
|
+
startY: number;
|
|
9
|
+
startScrollTop: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function resize(scrollContainer: IScrollContainer, { height }: { height?: number }) {
|
|
13
|
+
if (typeof height == 'number') {
|
|
14
|
+
scrollContainer.container.style.height = `${height}px`;
|
|
15
|
+
refresh(scrollContainer);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function refresh(scrollContainer?: IScrollContainer) {
|
|
20
|
+
if (scrollContainer) {
|
|
21
|
+
updateThumbSize(scrollContainer);
|
|
22
|
+
} else {
|
|
23
|
+
[...scrollContainers.values()].forEach((scrollContainer) => {
|
|
24
|
+
updateThumbSize(scrollContainer);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getScroll(scrollContainer: IScrollContainer): { left: number, top: number } {
|
|
30
|
+
return {
|
|
31
|
+
left: scrollContainer.content.scrollLeft,
|
|
32
|
+
top: scrollContainer.content.scrollTop
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function scroll(scrollContainer: IScrollContainer, { left, top, behavior = 'auto' }: { left?: number, top?: number, behavior?: 'auto' | 'instant' | 'smooth' } = {}) {
|
|
36
|
+
const current = getScroll(scrollContainer);
|
|
37
|
+
scrollContainer.content.scroll({ left: left ?? current.left, top: top ?? current.top, behavior });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getScrollContainer(selector: string | HTMLElement): IScrollContainer | undefined {
|
|
41
|
+
const key = typeof selector == 'string' ? document.querySelector<HTMLElement>(selector) : selector;
|
|
42
|
+
return key ? scrollContainers.get(key) : undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function thumbMouseDown(scrollContainer: IScrollContainer, e: MouseEvent) {
|
|
46
|
+
scrollContainer.isDragging = true;
|
|
47
|
+
scrollContainer.startY = e.clientY;
|
|
48
|
+
scrollContainer.startScrollTop = scrollContainer.content.scrollTop;
|
|
49
|
+
scrollContainer.thumb.style.cursor = 'grabbing';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function updateThumbSize(scrollContainer: IScrollContainer) {
|
|
53
|
+
const { container, content, thumb, track } = scrollContainer;
|
|
54
|
+
const scrollHeight = content.scrollHeight - getPaddingY(content);
|
|
55
|
+
const containerHeight = container.clientHeight - getPaddingY(container);
|
|
56
|
+
const visibleRatio = containerHeight / scrollHeight;
|
|
57
|
+
const thumbHeight = visibleRatio * containerHeight;
|
|
58
|
+
thumb.style.height = `${thumbHeight > 20 ? thumbHeight : 20}px`;
|
|
59
|
+
track.style.display = visibleRatio < 1 ? 'block' : 'none';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function scrollContent(scrollContainer: IScrollContainer) {
|
|
63
|
+
const { container, content, thumb, track } = scrollContainer;
|
|
64
|
+
const scrollHeight = content.scrollHeight - getPaddingY(content);
|
|
65
|
+
const containerHeight = container.clientHeight - getPaddingY(container);
|
|
66
|
+
const scrollRatio = content.scrollTop / (scrollHeight - containerHeight);
|
|
67
|
+
const top = scrollRatio * (track.clientHeight - thumb.clientHeight);
|
|
68
|
+
thumb.style.top = `${top}px`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function getPaddingY(content: HTMLElement): number {
|
|
72
|
+
const style = getComputedStyle(content);
|
|
73
|
+
return parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getScrollContainerElement(element: HTMLElement) {
|
|
77
|
+
let container = element;
|
|
78
|
+
while (container && !container.classList.contains('scroll-container')) {
|
|
79
|
+
container = container.parentElement as HTMLElement;
|
|
80
|
+
}
|
|
81
|
+
return container;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function createScrollContainer(container: HTMLElement): IScrollContainer {
|
|
85
|
+
return {
|
|
86
|
+
container: container,
|
|
87
|
+
content: container.querySelector('.evo-scroll-content') as HTMLElement,
|
|
88
|
+
track: container.querySelector('.evo-scroll-track') as HTMLElement,
|
|
89
|
+
thumb: container.querySelector('.evo-scroll-thumb') as HTMLElement,
|
|
90
|
+
isDragging: false,
|
|
91
|
+
startY: 0,
|
|
92
|
+
startScrollTop: 0
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const scrollContainers = new Map<HTMLElement, IScrollContainer>();
|
|
97
|
+
|
|
98
|
+
const intersectionObserver = new IntersectionObserver((entries) => {
|
|
99
|
+
entries.forEach((entry) => {
|
|
100
|
+
if (entry.isIntersecting) {
|
|
101
|
+
updateThumbSize(scrollContainers.get(entry.target as HTMLElement) as IScrollContainer);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
{ threshold: 0.1 }
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const mutationObserver = new MutationObserver((mutations) => {
|
|
109
|
+
const containers: IScrollContainer[] = [];
|
|
110
|
+
mutations.forEach((mutation) => {
|
|
111
|
+
const sc = scrollContainers.get(getScrollContainerElement(mutation.target as HTMLElement));
|
|
112
|
+
if (sc && !containers.includes(sc)) {
|
|
113
|
+
containers.push(sc);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
containers.forEach((container) => {
|
|
117
|
+
updateThumbSize(container);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
document.querySelectorAll<HTMLElement>('.evo-scroll-container').forEach((container) => {
|
|
122
|
+
const scrollContainer = createScrollContainer(container);
|
|
123
|
+
scrollContainers.set(container, scrollContainer);
|
|
124
|
+
scrollContainer.thumb.addEventListener('mousedown', (e) => { thumbMouseDown(scrollContainer, e); }, { passive: true });
|
|
125
|
+
scrollContainer.content.addEventListener('scroll', (e) => { scrollContent(scrollContainer); }, { passive: true });
|
|
126
|
+
intersectionObserver.observe(container);
|
|
127
|
+
mutationObserver.observe(container, { childList: true, subtree: true });
|
|
128
|
+
});
|
|
129
|
+
document.addEventListener('mousemove', (e) => {
|
|
130
|
+
[...scrollContainers.values()].forEach((scrollContainer) => {
|
|
131
|
+
if (!scrollContainer.isDragging) return;
|
|
132
|
+
const { container, content, thumb } = scrollContainer;
|
|
133
|
+
const deltaY = e.clientY - scrollContainer.startY;
|
|
134
|
+
const scrollRatio =
|
|
135
|
+
(content.scrollHeight - container.clientHeight) /
|
|
136
|
+
(container.clientHeight - thumb.clientHeight);
|
|
137
|
+
scrollContainer.content.scrollTop = scrollContainer.startScrollTop + deltaY * scrollRatio;
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
document.addEventListener('mouseup', () => {
|
|
141
|
+
[...scrollContainers.values()].forEach((scrollContainer) => {
|
|
142
|
+
scrollContainer.isDragging = false;
|
|
143
|
+
scrollContainer.thumb.style.cursor = 'grab';
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
window.addEventListener('resize', () => {
|
|
147
|
+
[...scrollContainers.values()].forEach((scrollContainer) => {
|
|
148
|
+
updateThumbSize(scrollContainer);
|
|
149
|
+
});
|
|
150
|
+
});
|