Last active
January 25, 2023 22:04
-
-
Save deebloo/ae17637ac180c3a877162e2847fb22b9 to your computer and use it in GitHub Desktop.
Small utility for apply html template and construct-able stylesheets to custom elements.
This file contains hidden or 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
import { shadow, ShadowTemplate, html, css } from './shadow.js'; | |
const template: ShadowTemplate = { | |
css: css` | |
:host { | |
display: contents; | |
} | |
`. | |
html: html` | |
<slot></slot> | |
` | |
} | |
class MyElement extends HTMlElement { | |
#shadow = shadow(this, template); | |
} |
This file contains hidden or 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
export abstract class Result<T> { | |
#raw: TemplateStringsArray; | |
#stringRes: string | null = null; | |
#valueRes: T | null = null; | |
constructor(raw: TemplateStringsArray) { | |
this.#raw = raw; | |
} | |
toString(): string { | |
if (!this.#stringRes) { | |
this.#stringRes = this.#raw.toString(); | |
} | |
return this.#stringRes; | |
} | |
toValue(): T { | |
if (!this.#valueRes) { | |
this.#valueRes = this.createValue(this.toString()); | |
} | |
return this.#valueRes; | |
} | |
abstract createValue(str: string): T; | |
} | |
export class TemplatResult extends Result<HTMLTemplateElement> { | |
createValue(str: string): HTMLTemplateElement { | |
const el = document.createElement("template"); | |
el.innerHTML = str; | |
return el; | |
} | |
} | |
export class CSSResult extends Result<CSSStyleSheet | HTMLStyleElement> { | |
createValue(str: string): CSSStyleSheet | HTMLStyleElement { | |
if (document.adoptedStyleSheets) { | |
const sheet = new CSSStyleSheet(); | |
sheet.replaceSync(str); | |
return sheet; | |
} | |
const style = document.createElement("style"); | |
style.innerHTML = str; | |
return style; | |
} | |
} | |
export function html(strings: TemplateStringsArray) { | |
return new TemplatResult(strings); | |
} | |
export function css(strings: TemplateStringsArray) { | |
return new CSSResult(strings); | |
} | |
export interface ShadowTemplate { | |
css?: CSSResult | CSSResult[]; | |
html?: TemplatResult; | |
} | |
export function shadow(el: HTMLElement, template?: ShadowTemplate) { | |
if (el.shadowRoot) { | |
return el.shadowRoot; | |
} | |
const shadow = el.attachShadow({ mode: "open" }); | |
if (template?.css) { | |
if (Array.isArray(template.css)) { | |
template.css.forEach((css) => { | |
appendSheet(shadow, css); | |
}); | |
} else { | |
appendSheet(shadow, template.css); | |
} | |
} | |
if (template?.html) { | |
shadow.append(template.html.toValue().content.cloneNode(true)); | |
} | |
return shadow; | |
} | |
function appendSheet(root: ShadowRoot, css: CSSResult) { | |
const value = css.toValue(); | |
if (value instanceof CSSStyleSheet) { | |
root.adoptedStyleSheets = [...root.adoptedStyleSheets, value]; | |
} else if (value instanceof HTMLStyleElement) { | |
root.append(value); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment