Skip to content

Commit 3cf0d40

Browse files
Merge pull request #7 from enhance-dev/dom-diffing-slots
Initial pass at enabling dom diffing for custom element templates con…
2 parents ab609f0 + b8e4cc5 commit 3cf0d40

File tree

6 files changed

+229
-8845
lines changed

6 files changed

+229
-8845
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
registry-url: https://registry.npmjs.org/
2525

2626
- name: Install
27-
run: npm ci
27+
run: npm i
2828

2929
- name: Test
3030
run: npm test
@@ -49,7 +49,7 @@ jobs:
4949
registry-url: https://registry.npmjs.org/
5050

5151
- name: Install
52-
run: npm ci
52+
run: npm i
5353

5454
- name: Test
5555
run: npm test

index.mjs

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,57 @@
22
const CustomElementMixin = (superclass) => class extends superclass {
33
constructor() {
44
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)
610
// 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+
}
840

41+
removeStyleTags(el) {
942
// Handle style tags
10-
if (enhanced) {
43+
if (this.enhanced) {
1144
// 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) })
1447
} else {
1548
let tagName = this.tagName
16-
this.template.content.querySelectorAll('style')
49+
el.querySelectorAll('style')
1750
.forEach((tag) => {
1851
let sheet = this.styleTransform({ tag, tagName, scope: tag.getAttribute('scope') })
1952
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]
20-
this.template.content.removeChild(tag)
53+
el.removeChild(tag)
2154
})
2255
}
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-
}
3956
}
4057

4158
toKebabCase(str) {
@@ -46,7 +63,9 @@ const CustomElementMixin = (superclass) => class extends superclass {
4663
const styles = this.parseCSS(tag.textContent)
4764

4865
if (scope === 'global') {
49-
return styles
66+
const sheet = new CSSStyleSheet();
67+
sheet.replaceSync(tag.textContent)
68+
return sheet
5069
}
5170

5271
const rules = styles.cssRules
@@ -110,11 +129,13 @@ const CustomElementMixin = (superclass) => class extends superclass {
110129
}
111130

112131

113-
expandSlots(here) {
132+
expandSlots(str, templateStr) {
114133
const fragment = document.createElement('div')
115-
fragment.innerHTML = here.innerHTML
134+
fragment.innerHTML = str
135+
const template = document.createElement('template')
136+
template.innerHTML = templateStr
116137
fragment.attachShadow({ mode: 'open' }).appendChild(
117-
here.template.content.cloneNode(true)
138+
template.content.cloneNode(true)
118139
)
119140

120141
const children = Array.from(fragment.childNodes)
@@ -127,9 +148,19 @@ const CustomElementMixin = (superclass) => class extends superclass {
127148
if (slot.name) {
128149
if (!namedSlots[slot.name]) namedSlots[slot.name] = { slotNode: slot, contentToSlot: [] }
129150
namedSlots[slot.name].contentToSlot.push(child)
130-
} else {
151+
}
152+
else {
131153
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+
}
133164
}
134165
}
135166
})

0 commit comments

Comments
 (0)