2
2
const CustomElementMixin = ( superclass ) => class extends superclass {
3
3
constructor ( ) {
4
4
super ( )
5
-
5
+ this . expandSlots = this . expandSlots . bind ( this )
6
+ this . removeStyleTags = this . removeStyleTags . bind ( this )
7
+ this . removeScriptTags = this . removeScriptTags . bind ( this )
8
+ this . expandTemplate = this . expandTemplate . bind ( this )
9
+ this . scrubTemplate = this . scrubTemplate . bind ( this )
6
10
// Has this element been server side rendered
7
- const enhanced = this . hasAttribute ( 'enhanced' )
11
+ this . enhanced = this . hasAttribute ( 'enhanced' )
12
+ // Expands the Custom Element with the template content
13
+ this . hasSlots = Boolean ( this . template . content . querySelectorAll ( 'slot' ) ?. length )
14
+ this . scrubTemplate ( this . template . content )
15
+ this . expandTemplate ( )
16
+ }
17
+
18
+ scrubTemplate ( el ) {
19
+ this . removeStyleTags ( el )
20
+ this . removeScriptTags ( el )
21
+ return el
22
+ }
23
+
24
+ expandTemplate ( ) {
25
+ // If the Custom Element was already expanded by SSR it will have the "enhanced" attribute so do not replaceChildren
26
+ if ( ! this . enhanced && ! this . hasSlots ) {
27
+ this . replaceChildren ( this . scrubTemplate ( this . template . content . cloneNode ( true ) ) )
28
+ // If this Custom Element was added dynamically with JavaScript then use the template contents to expand the element
29
+ } else if ( ! this . enhanced && this . hasSlots ) {
30
+ this . innerHTML = this . expandSlots ( this . innerHTML , this . template . innerHTML )
31
+ }
32
+ }
33
+
34
+ removeScriptTags ( el ) {
35
+ // Removes script tags as they are already appended to the body by SSR
36
+ // TODO: If only added dynamically in the browser we need to insert the script tag after running the script transform on it. As well as handle deduplication.
37
+ el . querySelectorAll ( 'script' )
38
+ . forEach ( ( tag ) => { el . content . removeChild ( tag ) } )
39
+ }
8
40
41
+ removeStyleTags ( el ) {
9
42
// Handle style tags
10
- if ( enhanced ) {
43
+ if ( this . enhanced ) {
11
44
// Removes style tags as they are already inserted into the head by SSR
12
- this . template . content . querySelectorAll ( 'style' )
13
- . forEach ( ( tag ) => { this . template . content . removeChild ( tag ) } )
45
+ el . querySelectorAll ( 'style' )
46
+ . forEach ( ( tag ) => { el . removeChild ( tag ) } )
14
47
} else {
15
48
let tagName = this . tagName
16
- this . template . content . querySelectorAll ( 'style' )
49
+ el . querySelectorAll ( 'style' )
17
50
. forEach ( ( tag ) => {
18
51
let sheet = this . styleTransform ( { tag, tagName, scope : tag . getAttribute ( 'scope' ) } )
19
52
document . adoptedStyleSheets = [ ...document . adoptedStyleSheets , sheet ]
20
- this . template . content . removeChild ( tag )
53
+ el . removeChild ( tag )
21
54
} )
22
55
}
23
-
24
- // Removes script tags as they are already appended to the body by SSR
25
- // TODO: If only added dynamically in the browser we need to insert the script tag after running the script transform on it. As well as handle deduplication.
26
- this . template . content . querySelectorAll ( 'script' )
27
- . forEach ( ( tag ) => { this . template . content . removeChild ( tag ) } )
28
-
29
- // Expands the Custom Element with the template content
30
- const hasSlots = this . template . content . querySelectorAll ( 'slot' ) ?. length
31
-
32
- // If the Custom Element was already expanded by SSR it will have the "enhanced" attribute so do not replaceChildren
33
- // If this Custom Element was added dynamically with JavaScript then use the template contents to expand the element
34
- if ( ! enhanced && ! hasSlots ) {
35
- this . replaceChildren ( this . template . content . cloneNode ( true ) )
36
- } else if ( ! enhanced && hasSlots ) {
37
- this . innerHTML = this . expandSlots ( this )
38
- }
39
56
}
40
57
41
58
toKebabCase ( str ) {
@@ -46,7 +63,9 @@ const CustomElementMixin = (superclass) => class extends superclass {
46
63
const styles = this . parseCSS ( tag . textContent )
47
64
48
65
if ( scope === 'global' ) {
49
- return styles
66
+ const sheet = new CSSStyleSheet ( ) ;
67
+ sheet . replaceSync ( tag . textContent )
68
+ return sheet
50
69
}
51
70
52
71
const rules = styles . cssRules
@@ -110,11 +129,13 @@ const CustomElementMixin = (superclass) => class extends superclass {
110
129
}
111
130
112
131
113
- expandSlots ( here ) {
132
+ expandSlots ( str , templateStr ) {
114
133
const fragment = document . createElement ( 'div' )
115
- fragment . innerHTML = here . innerHTML
134
+ fragment . innerHTML = str
135
+ const template = document . createElement ( 'template' )
136
+ template . innerHTML = templateStr
116
137
fragment . attachShadow ( { mode : 'open' } ) . appendChild (
117
- here . template . content . cloneNode ( true )
138
+ template . content . cloneNode ( true )
118
139
)
119
140
120
141
const children = Array . from ( fragment . childNodes )
@@ -127,9 +148,19 @@ const CustomElementMixin = (superclass) => class extends superclass {
127
148
if ( slot . name ) {
128
149
if ( ! namedSlots [ slot . name ] ) namedSlots [ slot . name ] = { slotNode : slot , contentToSlot : [ ] }
129
150
namedSlots [ slot . name ] . contentToSlot . push ( child )
130
- } else {
151
+ }
152
+ else {
131
153
if ( ! unnamedSlot [ "slotNode" ] ) unnamedSlot = { slotNode : slot , contentToSlot : [ ] }
132
- unnamedSlot . contentToSlot . push ( child )
154
+ if ( child [ 'setAttribute' ] ) {
155
+ child . setAttribute ( 'slot' , '' )
156
+ unnamedSlot . contentToSlot . push ( child )
157
+ }
158
+ else {
159
+ const wrapperSpan = document . createElement ( 'span' )
160
+ wrapperSpan . setAttribute ( 'slot' , '' )
161
+ wrapperSpan . appendChild ( child )
162
+ unnamedSlot . contentToSlot . push ( wrapperSpan )
163
+ }
133
164
}
134
165
}
135
166
} )
0 commit comments