/
index.js
113 lines (96 loc) · 3.28 KB
/
index.js
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
107
108
109
110
111
112
113
import React, { useState, useEffect, forwardRef } from 'react';
import { createPortal } from 'react-dom';
import PropTypes from 'prop-types';
import { decamelize } from 'humps';
function Noop({ children }) {
return children;
}
function getStyleWrapper() {
try {
const styled = require('styled-components');
return styled.StyleSheetManager;
} catch {
return Noop;
}
}
function ShadowContent({ root, children }) {
return createPortal(children, root);
}
ShadowContent.propTypes = {
root: PropTypes.object.isRequired,
children: PropTypes.node,
};
ShadowContent.defaultProps = { children: null };
const tags = new Map();
function createTag(options) {
const ShadowRoot = forwardRef(
({ mode, delegatesFocus, styleSheets, children, ...props }, ref) => {
const [node, setNode] = useState(null);
const [root, setRoot] = useState(null);
const Wrapper = getStyleWrapper();
const key = `node_${mode}${delegatesFocus}`;
useEffect(() => {
if (node) {
try {
const root = node.attachShadow({
mode,
delegatesFocus,
});
styleSheets.length > 0 &&
(root.adoptedStyleSheets = styleSheets);
global.setTimeout(() => {
ref && typeof ref === 'function' && ref(node);
ref && 'current' in ref && (ref.current = node);
});
setRoot(root);
} catch (error) {
switch (error.name) {
case 'NotSupportedError':
styleSheets.length > 0 &&
(root.adoptedStyleSheets = styleSheets);
break;
default:
throw error;
}
}
}
}, [node, styleSheets]);
return (
<options.tag key={key} ref={setNode} {...props}>
{root && (
<Wrapper target={root}>
<ShadowContent root={root}>
{children}
</ShadowContent>
</Wrapper>
)}
</options.tag>
);
},
);
ShadowRoot.propTypes = {
mode: PropTypes.oneOf(['open', 'closed']),
delegatesFocus: PropTypes.bool,
styleSheets: PropTypes.arrayOf(
PropTypes.instanceOf(global.CSSStyleSheet),
),
children: PropTypes.node,
};
ShadowRoot.defaultProps = {
mode: 'open',
delegatesFocus: false,
styleSheets: [],
children: null,
};
return ShadowRoot;
}
export function createProxy(target = {}) {
return new Proxy(target, {
get: function get(_, name) {
const tag = decamelize(name, { separator: '-' });
if (!tags.has(tag)) tags.set(tag, createTag({ tag }));
return tags.get(tag);
},
});
}
export default createProxy();