-
Notifications
You must be signed in to change notification settings - Fork 77
/
Copy pathPopoverManager.ts
106 lines (83 loc) · 3.15 KB
/
PopoverManager.ts
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
100
101
102
103
104
105
106
// @ts-strict-ignore
import { ReferenceElement } from "../../utils/floating-ui";
import { isActivationKey } from "../../utils/key";
import { isKeyboardTriggeredClick } from "../../utils/dom";
import type { Popover } from "./popover";
export default class PopoverManager {
// --------------------------------------------------------------------------
//
// Private Properties
//
// --------------------------------------------------------------------------
private registeredElements = new Map<ReferenceElement, Popover["el"]>();
private registeredElementCount = 0;
// --------------------------------------------------------------------------
//
// Public Methods
//
// --------------------------------------------------------------------------
registerElement(referenceEl: ReferenceElement, popover: Popover["el"]): void {
this.registeredElementCount++;
this.registeredElements.set(referenceEl, popover);
if (this.registeredElementCount === 1) {
this.addListeners();
}
}
unregisterElement(referenceEl: ReferenceElement): void {
if (this.registeredElements.delete(referenceEl)) {
this.registeredElementCount--;
}
if (this.registeredElementCount === 0) {
this.removeListeners();
}
}
// --------------------------------------------------------------------------
//
// Private Methods
//
// --------------------------------------------------------------------------
private queryPopover = (composedPath: EventTarget[]): Popover["el"] => {
const { registeredElements } = this;
const registeredElement = (composedPath as HTMLElement[]).find((pathEl) => registeredElements.has(pathEl));
return registeredElements.get(registeredElement);
};
private togglePopovers = (event: KeyboardEvent | PointerEvent): void => {
const composedPath = event.composedPath();
const togglePopover = this.queryPopover(composedPath);
if (togglePopover && !togglePopover.triggerDisabled) {
togglePopover.open = !togglePopover.open;
}
Array.from(this.registeredElements.values())
.filter(
(popover) => popover !== togglePopover && popover.autoClose && popover.open && !composedPath.includes(popover),
)
.forEach((popover) => (popover.open = false));
};
private closeAllPopovers(): void {
Array.from(this.registeredElements.values()).forEach((popover) => (popover.open = false));
}
private keyDownHandler = (event: KeyboardEvent): void => {
if (event.defaultPrevented) {
return;
}
if (event.key === "Escape") {
this.closeAllPopovers();
} else if (isActivationKey(event.key)) {
this.togglePopovers(event);
}
};
private clickHandler = (event: PointerEvent): void => {
if (isKeyboardTriggeredClick(event) || event.defaultPrevented) {
return;
}
this.togglePopovers(event);
};
private addListeners(): void {
window.addEventListener("click", this.clickHandler);
window.addEventListener("keydown", this.keyDownHandler);
}
private removeListeners(): void {
window.removeEventListener("click", this.clickHandler);
window.removeEventListener("keydown", this.keyDownHandler);
}
}