-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathroot-scroll.ts
More file actions
99 lines (79 loc) · 2.44 KB
/
root-scroll.ts
File metadata and controls
99 lines (79 loc) · 2.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import type { ReactiveController, ReactiveControllerHost } from 'lit';
import type { PopoverScrollStrategy } from '../../types.js';
type RootScrollControllerConfig = {
hideCallback?: () => void;
resetListeners?: boolean;
};
type RootScrollControllerHost = ReactiveControllerHost & {
open: boolean;
hide(): void;
scrollStrategy?: PopoverScrollStrategy;
};
type ScrollRecord = { scrollTop: number; scrollLeft: number };
class RootScrollController implements ReactiveController {
private _cache: WeakMap<Element, ScrollRecord>;
constructor(
private readonly host: RootScrollControllerHost,
private config?: RootScrollControllerConfig
) {
this._cache = new WeakMap();
this.host.addController(this);
}
private configureListeners() {
this.host.open ? this.addEventListeners() : this.removeEventListeners();
}
private hide() {
this.config?.hideCallback
? this.config.hideCallback.call(this.host)
: this.host.hide();
}
private addEventListeners() {
if (this.host.scrollStrategy !== 'scroll') {
document.addEventListener('scroll', this, { capture: true });
}
}
private removeEventListeners() {
document.removeEventListener('scroll', this, { capture: true });
this._cache = new WeakMap();
}
public handleEvent(event: Event) {
this.host.scrollStrategy === 'close' ? this.hide() : this._block(event);
}
private _block(event: Event) {
event.preventDefault();
const element = event.target as Element;
const cache = this._cache;
if (!cache.has(element)) {
cache.set(element, {
scrollTop: element.firstElementChild?.scrollTop ?? element.scrollTop,
scrollLeft: element.firstElementChild?.scrollLeft ?? element.scrollLeft,
});
}
const record = cache.get(element)!;
Object.assign(element, record);
if (element.firstElementChild) {
Object.assign(element.firstElementChild, record);
}
}
public update(config?: RootScrollControllerConfig) {
if (config) {
this.config = { ...this.config, ...config };
}
if (config?.resetListeners) {
this.removeEventListeners();
}
this.configureListeners();
}
public hostConnected() {
this.configureListeners();
}
public hostDisconnected() {
this.removeEventListeners();
}
}
export function addRootScrollHandler(
host: RootScrollControllerHost,
config?: RootScrollControllerConfig
) {
return new RootScrollController(host, config);
}