Skip to content

Instantly share code, notes, and snippets.

@AutoSponge
Last active May 1, 2020 15:13
Show Gist options
  • Save AutoSponge/0b75dd54f60ee49876ed939d50047ba4 to your computer and use it in GitHub Desktop.
Save AutoSponge/0b75dd54f60ee49876ed939d50047ba4 to your computer and use it in GitHub Desktop.
streaming custom element
class TextDecoderStream extends TransformStream {
constructor(encoding = 'utf8') {
super({
start() {
this.decoder = new TextDecoder(encoding);
},
transform(chunk, controller) {
controller.enqueue(this.decoder.decode(chunk, { stream: true }));
},
});
}
}
class HTMLWritableStream extends WritableStream {
constructor(sink, signal = {}) {
super({
write(chunk) {
if (signal.aborted) return;
return new Promise((resolve) => {
requestAnimationFrame(() => {
sink.write(chunk);
resolve();
});
});
},
close() {
sink.close();
},
abort() {
this.close();
},
});
}
}
class X extends HTMLElement {
connectedCallback() {
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `<button type="button">stop</button>`;
const button = shadowRoot.querySelector('button');
const controller = new AbortController();
const { signal, abort } = controller;
button.addEventListener('click', abort.bind(controller));
const sink = document.implementation.createHTMLDocument('shadow-stream');
sink.open();
sink.write('<div style="height: 500px; overflow: scroll;">');
shadowRoot.appendChild(document.adoptNode(sink.body.firstChild));
const decoder = new TextDecoderStream();
const writableStream = new HTMLWritableStream(sink, signal);
fetch('http://localhost:4000/api/html', { signal })
.then((res) => {
res.body.pipeThrough(decoder).pipeTo(writableStream);
})
.catch(console.log);
}
}
customElements.define('x-elm', X);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment