Skip to content

Instantly share code, notes, and snippets.

@michaelcpuckett
Created June 13, 2019 09:49
Show Gist options
  • Save michaelcpuckett/ddb282185a2ac960f250769e640b58c4 to your computer and use it in GitHub Desktop.
Save michaelcpuckett/ddb282185a2ac960f250769e640b58c4 to your computer and use it in GitHub Desktop.
<script>
class XTimestamp extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' }).appendChild(window.document.createElement('slot'))
}
connectedCallback() {
window.requestAnimationFrame(() => {
this.$dateTime = this.querySelector('slot').assignedNodes()[0].innerText.trim()
const templateEl = window.document.createElement('template')
templateEl.innerHTML = `
<time datetime="${this.$datetime}">
Published on [format: ${this.$dateTime}]
</time>
`
this.shadowRoot.firstChild.remove()
this.shadowRoot.appendChild(templateEl.content)
})
}
}
class XIf extends HTMLElement {
constructor() {
super()
}
connectedCallback() {
window.requestAnimationFrame(() => {
const ifEl = this.querySelector('[data-if]')
const attachedEl = ifEl.querySelector('slot').assignedNodes()[0]
const slotEl = window.document.createElement('slot')
if (!attachedEl || !attachedEl.innerText.trim()) {
slotEl.setAttribute('name', 'x-inert')
}
this.attachShadow({ mode: 'open' }).appendChild(slotEl)
})
}
}
class XArticleInner extends HTMLElement {
constructor() {
super()
this
.attachShadow({ mode: 'open' })
.appendChild(window.document.getElementById('x-article-inner-template').content)
}
}
class XArticle extends HTMLElement {
constructor() {
super()
this
.attachShadow({ mode: 'open' })
.appendChild(window.document.createElement('x-article-inner'))
this.$data = new Proxy({}, {
set: (_, prop, newVal) => {
const slot = this.shadowRoot.querySelector(`[slot="${prop}"]`)
if (slot) {
slot.innerHTML = newVal
} else {
const newSlot = window.document.createElement('data')
newSlot.setAttribute('slot', prop)
newSlot.innerHTML = typeof newVal === 'string' ? newVal : (Array.isArray(newVal) ? `<p>${newVal.join('</p><p>')}</p>` : newVal.name)
this.shadowRoot.querySelector('x-article-inner').appendChild(newSlot)
}
return newVal
}
})
}
connectedCallback() {
window.requestAnimationFrame(() => {
this.$handleSlotChange(this.querySelector('script').innerHTML)
this.querySelector('script').addEventListener('slotchange', this.$handleSlotChange)
})
}
$handleSlotChange(data) {
Object.assign(this.$data, {
...(JSON.parse(data))
})
}
}
customElements.define('x-if', XIf)
customElements.define('x-timestamp', XTimestamp)
customElements.define('x-article', XArticle)
customElements.define('x-article-inner', XArticleInner)
;((async function () {
await new Promise(done => window.setTimeout(done, 2000))
window.document.querySelector('x-article').$data.headline = 'Replaced headline!'
})())
</script>
<template id="x-article-inner-template">
<style>
:host>* {
background: red;
--padding: 12px;
padding: var(--padding);
}
* {
margin: 0;
padding: 0;
}
h1 {
font-style: italic;
border-bottom: 1px solid;
padding-bottom: var(--padding);
margin-bottom: var(--padding);
}
</style>
<div role="region" tabindex="0" aria-labelledby="h1">
<h1 id="h1">
<slot name="headline"></slot>
</h1>
<x-timestamp>
<slot name="datePublished"></slot>
</x-timestamp>
<div role="presentation">
<x-if>
By <span data-if><slot name="author"></slot></span>
</x-if>
</div>
<slot name="dateline"></slot> &mdash;
<slot name="articleBody"></slot>
</div>
</template>
<h1>PickPuck.com</h1>
<x-article id="article">
<script type="application/ld+json">
{
"@context": "http://schema.org/",
"type": "NewsArticle",
"headline": "Lorem ipsum sigma fidelitdas sin Dolor policia",
"datePublished": "2019-06-12",
"articleBody": [
"Foo bar",
"Foo bar",
"Foo bar"
],
"dateline": "WASHINGTON",
"author": {
"type": "Person",
"name": "Michael Puckett"
}
}
</script>
</x-article>
<style>
h1 {
color: green;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment