Last active
April 2, 2019 18:39
-
-
Save peerreynders/084079c213e9da2abfcbd613fdeb6abc to your computer and use it in GitHub Desktop.
Web Components in Action v6: 7.5 Entering the Shadow DOM with Slots (p.152)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// file: components/biz-card/biz-card.mjs | |
import { patchContentMap } from '../../helpers/templateLoader.mjs' | |
/* | |
#1: | |
See patchContentMap call below which creates | |
static get _contentMap | |
static set _templateUri | |
#2: | |
- private (static) fields are at stage 3 | |
- public (static) fields are available in Chrome 72 | |
- alternately store in module scoped variables | |
https://github.com/tc39/proposal-static-class-features#user-content-static-private-fields | |
https://developers.google.com/web/updates/2018/12/class-fields#conclusion | |
#3: | |
https://developers.google.com/web/fundamentals/web-components/best-practices#create-your-shadow-root-in-the-constructor | |
https://developer.mozilla.org/en-US/docs/Web/API/Element/shadowRoot | |
> The Element.shadowRoot read-only property represents the shadow root hosted by the element. | |
> Use Element.attachShadow() to add a shadow root to an existing element. | |
#4: | |
- Waiting for connectedCallback in order to access the attribute value | |
- 'templateFile' isn't observed as it doesn't react to changes. | |
The first BizCard._templateUri value set resolves the promise | |
for the uri - then the fetch can start. | |
#5: | |
No class constructor/static block yet | |
https://github.com/tc39/proposal-class-static-block | |
*/ | |
class BizCard extends HTMLElement { | |
static get observedAttributes() { | |
return ['layout'] | |
} | |
// #1 | |
// #2 | |
static _noContent = document.createDocumentFragment() | |
// #3 | |
constructor() { | |
super() | |
this._populateCard = this._populateCard.bind(this) | |
this.attachShadow({mode: 'open'}); | |
} | |
attributeChangedCallback(name, _oldValue, newValue) { | |
if (name === 'layout') { | |
Promise.all([newValue, BizCard._contentMap]) | |
.then(this._populateCard) | |
} | |
} | |
connectedCallback() { | |
if(this.hasAttribute('templateFile')) { | |
// #4 | |
BizCard._templateUri = this.getAttribute('templateFile') | |
} | |
} | |
_populateCard([name, contentMap]) { | |
const content = contentMap.get(name) || BizCard._noContent | |
const clone = document.importNode(content, true) | |
this._removeChildren() | |
this.shadowRoot.appendChild(clone) | |
} | |
_removeChildren() { | |
let root = this.shadowRoot | |
for(let child = root.lastChild; child; child = root.lastChild) { | |
root.removeChild(child) | |
} | |
} | |
} | |
// #5 | |
patchContentMap(BizCard, '_templateUri', '_contentMap') | |
if (!customElements.get('biz-card')) { | |
customElements.define('biz-card', BizCard) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<!-- | |
file: index.html | |
Web Components in Action MEAP v6 | |
https://livebook.manning.com/#!/book/web-components-in-action/chapter-7/v-6/comment-487676 | |
original: | |
- https://github.com/bengfarrell/webcomponentsinaction/tree/master/chapter7/7.5-slotsandtemplates | |
- https://github.com/bengfarrell/webcomponentsinaction/blob/master/chapter7/7.5-slotsandtemplates/background-pattern.png | |
- https://github.com/bengfarrell/webcomponentsinaction/blob/master/chapter7/7.5-slotsandtemplates/biz-card-logo.png | |
#1: | |
prevent undefined custom element FOUC (flash of unstyled content) | |
polyfill alternative: | |
https://polymer-library.polymer-project.org/3.0/docs/devguide/style-shadow-dom#style-undefined-elements | |
--> | |
<title>Business Card</title> | |
<style> | |
biz-card:not(:defined) { | |
display: none; /* #1 */ | |
} | |
</style> | |
</head> | |
<body> | |
<p> | |
<select> | |
<option value="none">none</option> | |
<option value="default-card">default</option> | |
<option value="variation">variation</option> | |
</select> | |
</p> | |
<biz-card layout="none" templateFile="./templates.html"> | |
<span slot="firstname">Emmet</span> | |
<span slot="lastname">Brown</span> | |
<span slot="title">Student of all Sciences</span> | |
<span slot="phone">555.555.4385</span> | |
<span slot="email">emmett@docbrown.flux</span> | |
<span slot="website">www.docbrown.flux</span> | |
</biz-card> | |
<script type="module" src="./components/biz-card/biz-card.mjs"></script> | |
<script> | |
function updateLayout({ target: { value } }) { | |
bizcard.setAttribute('layout', value) | |
} | |
const bizcard = document.querySelector('biz-card') | |
document.querySelector('select').addEventListener('change', updateLayout) | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// file: helpers/templateLoader.mjs | |
export function fetchContentMap(uri) { | |
const init = { | |
method: 'GET', | |
headers: { | |
'Accept': 'text/html' | |
} | |
} | |
const request = new Request(uri, init) | |
return fetch(request) | |
.then(checkResponse) | |
.then(makeContentMap) | |
} | |
function checkResponse(response) { | |
if (!response.ok) { | |
throw new Error('HTTP error, status = ' + response.status) | |
} | |
return response.text() | |
} | |
function makeContentMap(html) { | |
let element = document.createElement('div') | |
element.innerHTML = html | |
const contentPairs = Array.prototype.reduce.call( | |
element.children, | |
appendContent, | |
[] | |
) | |
return new Map(contentPairs) | |
} | |
function appendContent(pairs, template) { | |
const name = template.classList.item(0) | |
if (name) { | |
pairs.push([name, template.content]) | |
} | |
return pairs | |
} | |
export function patchContentMap(classObject, uriSetter, mapGetter) { | |
let contentMap | |
// function to accept the Uri for the template file | |
function uriExecutor(resolve, _reject) { | |
Object.defineProperty(classObject, uriSetter, {set: resolve}) | |
} | |
// function to return the content map extracted from the templates | |
function getContentMap() { | |
return contentMap | |
} | |
Object.defineProperty(classObject, mapGetter, {get: getContentMap}) | |
contentMap = | |
new Promise(uriExecutor) // URI Promise | |
.then(fetchContentMap) // contentMap Promise | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template class="default-card"> | |
<style> | |
.biz-card { | |
font-size: 16px; | |
font-family: sans-serif; | |
color: white; | |
width: 700px; | |
height: 400px; | |
display: inline-block; | |
border-color: #9a9a9a; | |
background-size: 5%; | |
background-image: url("background-pattern.png"); | |
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); | |
} | |
.biz-card .logo { | |
height: 100px; | |
margin-top: 10%; | |
text-align: center; | |
background-image: url("biz-card-logo.png"); | |
background-size: contain; | |
background-position-x: center; | |
background-repeat: no-repeat; | |
} | |
.biz-card .top-text { | |
text-align: center; | |
} | |
.biz-card .top-text h1 { | |
font-size: 2.5em; | |
margin-bottom: 0; | |
} | |
.biz-card .top-text h3 { | |
margin: 0; | |
} | |
.biz-card .bottom-text { | |
text-align: center; | |
margin-top: 10%; | |
} | |
.biz-card .bottom-text h3 { | |
margin: 0; | |
} | |
</style> | |
<div class="biz-card"> | |
<div class="logo"></div> | |
<div class="top-text"> | |
<h1> | |
<slot name="firstname">First</slot> <slot | |
name="lastname">LastName</slot> | |
</h1> | |
<h3><slot name="title">Job Title</slot></h3> | |
</div> | |
<div class="bottom-text"> | |
<h3>phone: <slot name="phone">#xxx.xxx.xxxx</slot></h3> | |
<h3> | |
<slot name="email">email@email.com</slot> / <slot | |
name="website">http://website.com</slot> | |
</h3> | |
</div> | |
</div> | |
</template> | |
<template class="variation"> | |
<style> | |
.biz-card { | |
position: relative; | |
font-size: 16px; | |
font-family: sans-serif; | |
color: white; | |
width: 700px; | |
height: 400px; | |
display: inline-block; | |
border-color: #9a9a9a; | |
background-size: 5%; | |
background-image: url("background-pattern.png"); | |
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); | |
} | |
.biz-card .logo { | |
width: 100px; | |
height: 100px; | |
position: absolute; | |
top: 10px; | |
left: 10px; | |
background-image: url("biz-card-logo.png"); | |
background-size: contain; | |
background-position-x: center; | |
background-repeat: no-repeat; | |
} | |
.biz-card .name { | |
margin-bottom: 0; | |
margin-top: 50px; | |
margin-left: 130px; | |
} | |
.biz-card .title { | |
font-size: 48px; | |
margin-top: 100px; | |
text-align: center; | |
} | |
.biz-card .info { | |
text-align: right; | |
width: 95%; | |
} | |
</style> | |
<div class="biz-card"> | |
<div class="logo"></div> | |
<h2 class="name"><slot name="firstname">First</slot> <slot name="lastname">LastName</slot></h2> | |
<h1 class="title"><slot name="title">Job Title</slot></h1> | |
<h4 class="info">phone: <slot name="phone">#xxx.xxx.xxxx</slot></h4> | |
<h4 class="info"><slot name="email">email@email.com</slot></h4> | |
<h4 class="info"><slot name="website">http://website.com</slot></h4> | |
</div> | |
</template> | |
<template class="none"> | |
<style> | |
.biz-card { | |
position: relative; | |
font-size: 16px; | |
font-family: sans-serif; | |
color: white; | |
width: 700px; | |
height: 400px; | |
display: inline-block; | |
border-color: #9a9a9a; | |
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); | |
} | |
</style> | |
<div class="biz-card"></div> | |
</template> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment