UNPKG

uhtml

Version:
284 lines (263 loc) • 6.15 kB
'use strict'; const udomdiff = (m => /* c8 ignore start */ m.__esModule ? m.default : m /* c8 ignore stop */)(require('udomdiff')); const { empty, gPD, isArray, set } = require('./utils.js'); const { diffFragment } = require('./persistent-fragment.js'); const drop = (require('./range.js')); const setAttribute = (element, name, value) => element.setAttribute(name, value); /** * @param {Element} element * @param {string} name * @returns {void} */ const removeAttribute = (element, name) => element.removeAttribute(name); exports.removeAttribute = removeAttribute; /** * @template T * @param {Element} element * @param {T} value * @returns {T} */ const aria = (element, value) => { for (const key in value) { const $ = value[key]; const name = key === 'role' ? key : `aria-${key}`; if ($ == null) removeAttribute(element, name); else setAttribute(element, name, $); } return value; }; exports.aria = aria; let listeners; /** * @template T * @param {Element} element * @param {T} value * @param {string} name * @returns {T} */ const at = (element, value, name) => { name = name.slice(1); if (!listeners) listeners = new WeakMap; const known = listeners.get(element) || set(listeners, element, {}); let current = known[name]; if (current && current[0]) element.removeEventListener(name, ...current); current = isArray(value) ? value : [value, false]; known[name] = current; if (current[0]) element.addEventListener(name, ...current); return value; }; exports.at = at; /** @type {WeakMap<Node, Element | import("./persistent-fragment.js").PersistentFragment>} */ const holes = new WeakMap; /** * @template T * @param {import("./literals.js").Detail} detail * @param {T} value * @returns {T} */ const hole = (detail, value) => { const { t: node, n: hole } = detail; let nullish = false; switch (typeof value) { case 'object': if (value !== null) { (hole || node).replaceWith((detail.n = value.valueOf())); break; } case 'undefined': nullish = true; default: node.data = nullish ? '' : value; if (hole) { detail.n = null; hole.replaceWith(node); } break; } return value; }; exports.hole = hole; /** * @template T * @param {Element} element * @param {T} value * @returns {T} */ const className = (element, value) => maybeDirect( element, value, value == null ? 'class' : 'className' ); exports.className = className; /** * @template T * @param {Element} element * @param {T} value * @returns {T} */ const data = (element, value) => { const { dataset } = element; for (const key in value) { if (value[key] == null) delete dataset[key]; else dataset[key] = value[key]; } return value; }; exports.data = data; /** * @template T * @param {Element | CSSStyleDeclaration} ref * @param {T} value * @param {string} name * @returns {T} */ const direct = (ref, value, name) => (ref[name] = value); exports.direct = direct; /** * @template T * @param {Element} element * @param {T} value * @param {string} name * @returns {T} */ const dot = (element, value, name) => direct(element, value, name.slice(1)); exports.dot = dot; /** * @template T * @param {Element} element * @param {T} value * @param {string} name * @returns {T} */ const maybeDirect = (element, value, name) => ( value == null ? (removeAttribute(element, name), value) : direct(element, value, name) ); exports.maybeDirect = maybeDirect; /** * @template T * @param {Element} element * @param {T} value * @returns {T} */ const ref = (element, value) => ( (typeof value === 'function' ? value(element) : (value.current = element)), value ); exports.ref = ref; /** * @template T * @param {Element} element * @param {T} value * @param {string} name * @returns {T} */ const regular = (element, value, name) => ( (value == null ? removeAttribute(element, name) : setAttribute(element, name, value)), value ); /** * @template T * @param {Element} element * @param {T} value * @returns {T} */ const style = (element, value) => ( value == null ? maybeDirect(element, value, 'style') : direct(element.style, value, 'cssText') ); exports.style = style; /** * @template T * @param {Element} element * @param {T} value * @param {string} name * @returns {T} */ const toggle = (element, value, name) => ( element.toggleAttribute(name.slice(1), value), value ); exports.toggle = toggle; /** * @param {Node} node * @param {Node[]} value * @param {string} _ * @param {Node[]} prev * @returns {Node[]} */ const array = (node, value, prev) => { // normal diff const { length } = value; node.data = `[${length}]`; if (length) return udomdiff(node.parentNode, prev, value, diffFragment, node); /* c8 ignore start */ switch (prev.length) { case 1: prev[0].remove(); case 0: break; default: drop( diffFragment(prev[0], 0), diffFragment(prev.at(-1), -0), false ); break; } /* c8 ignore stop */ return empty; }; exports.array = array; const attr = new Map([ ['aria', aria], ['class', className], ['data', data], ['ref', ref], ['style', style], ]); exports.attr = attr; /** * @param {HTMLElement | SVGElement} element * @param {string} name * @param {boolean} svg * @returns */ const attribute = (element, name, svg) => { switch (name[0]) { case '.': return dot; case '?': return toggle; case '@': return at; default: return ( svg || ('ownerSVGElement' in element) ? (name === 'ref' ? ref : regular) : (attr.get(name) || ( name in element ? (name.startsWith('on') ? direct : (gPD(element, name)?.set ? maybeDirect : regular) ) : regular ) ) ); } }; exports.attribute = attribute; /** * @template T * @param {Element} element * @param {T} value * @returns {T} */ const text = (element, value) => ( (element.textContent = value == null ? '' : value), value ); exports.text = text;