-
Notifications
You must be signed in to change notification settings - Fork 78
/
Copy pathopenCloseComponent.ts
84 lines (73 loc) · 2.43 KB
/
openCloseComponent.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
// @ts-strict-ignore
import { KebabCase } from "type-fest";
import { whenTransitionDone } from "./dom";
/**
* Defines interface for components with open/close public emitter.
* All implementations of this interface must handle the following events: `beforeOpen`, `open`, `beforeClose`, `close`.
*/
export interface OpenCloseComponent {
/** The host element. */
readonly el: HTMLElement;
/**
* Specifies property on which active transition is watched for.
*
* This should be used if the component uses a property other than `open` to trigger a transition.
*/
openProp?: string;
/** Specifies the name of CSS transition property. */
transitionProp?: KebabCase<Extract<keyof CSSStyleDeclaration, string>>;
/** Specifies element that the transition is allowed to emit on. */
transitionEl: HTMLElement;
/** Defines method for `beforeOpen` event handler. */
onBeforeOpen: () => void;
/** Defines method for `open` event handler: */
onOpen: () => void;
/** Defines method for `beforeClose` event handler: */
onBeforeClose: () => void;
/** Defines method for `close` event handler: */
onClose: () => void;
}
function isOpen(component: OpenCloseComponent): boolean {
return component[component.openProp || "open"];
}
/**
* This util helps emit (before)open/close events consistently based on the associated CSS transition property.
*
* Note: this should be called whenever the component's toggling property changes and would trigger a transition.
*
* @example
* import { onToggleOpenCloseComponent, OpenCloseComponent } from "../../utils/openCloseComponent";
*
* override willUpdate(changes: PropertyValues<this>): void {
* if (changes.has("open") && (this.hasUpdated || this.open !== false)) {
* onToggleOpenCloseComponent(this);
* }
* // ...
* }
* @param component - OpenCloseComponent uses `open` prop to emit (before)open/close.
*/
export function onToggleOpenCloseComponent(component: OpenCloseComponent): void {
requestAnimationFrame((): void => {
if (!component.transitionEl) {
return;
}
whenTransitionDone(
component.transitionEl,
component.transitionProp,
() => {
if (isOpen(component)) {
component.onBeforeOpen();
} else {
component.onBeforeClose();
}
},
() => {
if (isOpen(component)) {
component.onOpen();
} else {
component.onClose();
}
},
);
});
}