-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathshadow-boxing.js
111 lines (101 loc) · 3.37 KB
/
shadow-boxing.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
let globalStyles
const openStylableElements = new Set()
const elementsToAnchors = new WeakMap()
const delayedConnectedCallbackElements = new WeakSet()
const mode = document.documentElement.getAttribute("shadow-style-mode")
const filter = (mode == 'component-pull' || mode == "page-push" || mode == "page-push-select") ? '' : '[shadow-import]'
const isValidPushMode = (mode == 'page-push-marked' || mode == 'page-push' || mode == 'page-push-select' || mode == 'page-push-select-marked')
const isValidComponentPullMode = (mode == 'component-pull' || mode == 'component-pull-marked')
// Use empty text nodes to know the start and end anchors of where we should insert cloned styles
function getAnchors (element) {
let anchors = elementsToAnchors.get(element)
if (!anchors) {
anchors = [document.createTextNode(''), document.createTextNode('')]
elementsToAnchors.set(element, anchors)
element.shadowRoot.prepend(...anchors)
}
return anchors
}
function clearStyles (element) {
const [startAnchor, endAnchor] = getAnchors(element)
let nextSibling
while ((nextSibling = startAnchor.nextSibling) !== endAnchor) {
nextSibling.remove()
}
}
function maybeSetStyles (element) {
if(mode == 'page-push-select' || mode == 'page-push-select-marked') {
if (element.hasAttribute('shadow-style-select')) {
setStyles(element)
}
} else {
setStyles(element)
}
}
function setStyles (element) {
const [, endAnchor] = getAnchors(element)
for (const node of globalStyles) {
element.shadowRoot.insertBefore(node.cloneNode(true), endAnchor)
}
}
function updateGlobalStyles () {
globalStyles = document.head.querySelectorAll(`style${filter},link[rel="stylesheet"]${filter}`)
}
const observer = new MutationObserver(() => {
updateGlobalStyles()
for (const element of openStylableElements) {
clearStyles(element)
maybeSetStyles(element)
}
})
observer.observe(document.head, {
childList: true,
subtree: true,
characterData: true,
attributes: true
})
updateGlobalStyles()
const OpenStylable = superclass => (class extends superclass {
connectedCallback () {
try {
if (super.connectedCallback) {
super.connectedCallback()
}
} finally {
if (isValidComponentPullMode) {
openStylableElements.add(this)
if (this.shadowRoot) {
maybeSetStyles(this)
} else { // if shadowRoot doesn't exist yet, wait to see if it gets added in connectedCallback
delayedConnectedCallbackElements.add(this) // keep track of which elements needed a delay
Promise.resolve().then(() => maybeSetStyles(this))
}
}
}
}
disconnectedCallback () {
try {
if (super.disconnectedCallback) {
super.disconnectedCallback()
}
} finally {
if (isValidComponentPullMode) {
openStylableElements.delete(this)
if (delayedConnectedCallbackElements.has(this)) { // ensure our disconnected logic runs after our connected logic
Promise.resolve().then(() => clearStyles(this))
} else { // run immediately, no need to delay
clearStyles(this)
}
}
}
}
})
if (isValidPushMode) {
let old = Element.prototype.attachShadow
Element.prototype.attachShadow = function () {
let r = old.call(this, ...arguments)
openStylableElements.add(this)
Promise.resolve().then(() => maybeSetStyles(this))
return r
}
}