-
Notifications
You must be signed in to change notification settings - Fork 2.7k
/
usePortalBox.ts
58 lines (46 loc) · 1.47 KB
/
usePortalBox.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
import { useIsomorphicLayoutEffect } from '@fluentui/react-bindings';
import * as React from 'react';
import { isBrowser } from '../../utils/isBrowser';
type UsePortalBoxOptions = {
className: string;
rtl: boolean;
target: Document | undefined;
};
export const usePortalBox = (options: UsePortalBoxOptions): HTMLDivElement | null => {
const { className, rtl, target } = options;
const element: HTMLDivElement | null = React.useMemo(() => {
const newElement = isBrowser() && target ? target.createElement('div') : null;
// Element should be attached to DOM during render to make elements that will be rendered
// inside accessible in effects of child components
if (newElement) {
target.body.appendChild(newElement);
}
return newElement;
}, [target]);
useIsomorphicLayoutEffect(() => {
const classes = className.split(' ').filter(Boolean);
if (element) {
element.classList.add(...classes);
if (rtl) {
element.setAttribute('dir', 'rtl');
} else {
element.removeAttribute('dir');
}
}
return () => {
if (element) {
element.classList.remove(...classes);
element.removeAttribute('dir');
}
};
}, [className, element, rtl]);
// This effect should always last as it removes element from HTML tree
React.useEffect(() => {
return () => {
if (element) {
target.body.removeChild(element);
}
};
}, [element, target]);
return element;
};