thorish
Version:
This is a library of useful JS concepts and data structures for Node and the browser. It it, unashamedly, a dumping ground for code needed by [@samthor](https://twitter.com/samthor)'s projects.
190 lines (183 loc) • 5.49 kB
JavaScript
import {
abortedSignal,
escapeStringFor,
preprocessHtmlTemplateTag,
tickRunner
} from "../../chunk-FJEQHTCW.js";
// src/dom/index.ts
function css(arr, ...rest) {
const parts = [arr[0]];
for (let i = 1; i < arr.length; ++i) {
parts.push(String(rest[i - 1]), arr[i]);
}
const styleSheet = new CSSStyleSheet();
styleSheet.replaceSync(parts.join(""));
return styleSheet;
}
function html(arr, ...rest) {
const idToReplace = /* @__PURE__ */ new Map();
const states = preprocessHtmlTemplateTag(arr);
if (states.length + 1 !== arr.length || states.length !== rest.length) {
throw new Error(`unexpected html tag length`);
}
const parts = [];
for (let i = 0; i < states.length; ++i) {
parts.push(arr[i]);
const state = states[i];
const inner = rest[i];
if (inner instanceof Node) {
if (state !== 0 /* Normal */) {
throw new Error(`can only place Node within regular HTML`);
}
const id = `__html_${Math.random()}`;
parts.push(`<link id="${id}" />`);
idToReplace.set(id, inner);
continue;
}
parts.push(escapeStringFor(state, inner));
}
parts.push(arr[arr.length - 1]);
const node = document.createElement("div");
node.innerHTML = parts.join("");
const frag = document.createDocumentFragment();
frag.append(...node.children);
for (const [id, node2] of idToReplace) {
const target = frag.getElementById(id);
if (node2 instanceof DocumentFragment) {
target.replaceWith(node2.cloneNode(true));
} else {
target.replaceWith(node2);
}
}
return frag;
}
function buildShadow(src, ...styles) {
return buildShadowOptions({}, src, ...styles);
}
function buildShadowOptions(options, src, ...styles) {
return (target) => {
const root = target.attachShadow({ mode: "open", delegatesFocus: options.delegatesFocus });
root.append(src.cloneNode(true));
root.adoptedStyleSheets = styles;
return root;
};
}
var SignalHTMLElement = class extends HTMLElement {
abort = (reason) => {
};
signal = abortedSignal;
connectedCallback() {
this.maybeRefresh();
}
disconnectedCallback() {
if (this.isConnected && !this.reparentShouldInvalidate()) {
} else {
this.abort("disconnected");
}
}
/**
* Call to cause a refresh, e.g., some value has changed.
*/
invalidate() {
this.abort("invalidate");
this.maybeRefresh();
}
/**
* Return `true` if this should cause a refresh/signal to be aborted.
*/
reparentShouldInvalidate() {
return false;
}
maybeRefresh() {
if (!this.isConnected || !this.signal.aborted) {
return;
}
const c = new AbortController();
this.signal = c.signal;
this.abort = (reason) => c.abort(reason);
this.refresh(this.signal);
}
};
var SizingElement = class extends HTMLElement {
constructor() {
super();
const s = buildShadow(
html`<div id="inner"><slot></slot></div>`,
css`
:host {
all: inherit;
display: inline-flex;
}
:host([block]) {
display: flex;
}
#inner {
margin: var(--sizing-negative-margin);
flex-grow: 1;
position: relative;
width: 0;
}
#inner {
display: flex;
flex-flow: row;
}
::slotted(*) {
flex-grow: 1 ;
}
:host([grid]) #inner {
display: grid;
grid-template-columns: minmax(0, 1fr);
grid-template-rows: minmax(0, 1fr);
}
:host([grid]) ::slotted(*) {
grid-column: 1 / -1 ;
grid-row: 1 / -1 ;
max-width: 100% ;
max-height: 100% ;
box-sizing: border-box ;
}
`
);
const root = s(this);
const inner = root.firstElementChild;
const prop = (prop2, value) => inner.style.setProperty(prop2, value);
const refreshState = () => {
const cs = window.getComputedStyle(this);
const { paddingTop, paddingRight, paddingBottom, paddingLeft, padding } = cs;
const negativePadding = `-${paddingTop} -${paddingRight} -${paddingBottom} -${paddingLeft}`;
const width = cs.getPropertyValue("width");
const height = cs.getPropertyValue("height");
prop("--sizing-extra-width", `calc(${paddingLeft} + ${paddingRight})`);
prop("--sizing-extra-height", `calc(${paddingTop} + ${paddingBottom})`);
prop("--sizing-padding", padding);
if (cs.boxSizing === "border-box") {
const { borderLeftWidth, borderRightWidth, borderTopWidth, borderBottomWidth } = cs;
prop(
"--sizing-inner-width",
`calc(${width} - (${paddingLeft} + ${paddingRight} + ${borderLeftWidth} + ${borderRightWidth}))`
);
prop(
"--sizing-inner-height",
`calc(${height} - (${paddingTop} + ${paddingBottom} + ${borderTopWidth} + ${borderBottomWidth}))`
);
} else {
prop("--sizing-inner-width", width);
prop("--sizing-inner-height", height);
}
prop("--sizing-negative-margin", negativePadding);
};
const ro = new ResizeObserver(tickRunner(refreshState));
ro.observe(this, { box: "content-box" });
ro.observe(this, { box: "border-box" });
ro.observe(inner);
}
};
export {
SignalHTMLElement,
SizingElement,
buildShadow,
buildShadowOptions,
css,
html
};
//# sourceMappingURL=index.js.map