UNPKG

@zeix/ui-element

Version:

UIElement - a HTML-first library for reactive Web Components

1,050 lines (1,042 loc) 30.6 kB
// node_modules/@zeix/cause-effect/src/util.ts var isFunction = (value) => typeof value === "function"; var isObjectOfType = (value, type) => Object.prototype.toString.call(value) === `[object ${type}]`; var toError = (reason) => reason instanceof Error ? reason : Error(String(reason)); class CircularDependencyError extends Error { constructor(where) { super(`Circular dependency in ${where} detected`); return this; } } // node_modules/@zeix/cause-effect/src/scheduler.ts var active; var pending = new Set; var batchDepth = 0; var updateMap = new Map; var requestId; var updateDOM = () => { requestId = undefined; const updates = Array.from(updateMap.values()); updateMap.clear(); for (const update of updates) { update(); } }; var requestTick = () => { if (requestId) cancelAnimationFrame(requestId); requestId = requestAnimationFrame(updateDOM); }; queueMicrotask(updateDOM); var watch = (notice) => { const cleanups = new Set; const w = notice; w.off = (on) => { cleanups.add(on); }; w.cleanup = () => { for (const cleanup of cleanups) { cleanup(); } cleanups.clear(); }; return w; }; var subscribe = (watchers) => { if (active && !watchers.has(active)) { const watcher = active; watchers.add(watcher); active.off(() => { watchers.delete(watcher); }); } }; var notify = (watchers) => { for (const watcher of watchers) { if (batchDepth) pending.add(watcher); else watcher(); } }; var flush = () => { while (pending.size) { const watchers = Array.from(pending); pending.clear(); for (const watcher of watchers) { watcher(); } } }; var batch = (fn) => { batchDepth++; try { fn(); } finally { flush(); batchDepth--; } }; var observe = (run, watcher) => { const prev = active; active = watcher; try { run(); } finally { active = prev; } }; var enqueue = (fn, dedupe) => new Promise((resolve, reject) => { updateMap.set(dedupe || Symbol(), () => { try { resolve(fn()); } catch (error) { reject(error); } }); requestTick(); }); // node_modules/@zeix/cause-effect/src/state.ts var TYPE_STATE = "State"; var state = (initialValue) => { const watchers = new Set; let value = initialValue; const s = { [Symbol.toStringTag]: TYPE_STATE, get: () => { subscribe(watchers); return value; }, set: (v) => { if (Object.is(value, v)) return; value = v; notify(watchers); if (UNSET === value) watchers.clear(); }, update: (fn) => { s.set(fn(value)); } }; return s; }; var isState = (value) => isObjectOfType(value, TYPE_STATE); // node_modules/@zeix/cause-effect/src/computed.ts var TYPE_COMPUTED = "Computed"; var computed = (fn) => { const watchers = new Set; let value = UNSET; let error; let controller; let dirty = true; let changed = false; let computing = false; const ok = (v) => { if (!Object.is(v, value)) { value = v; changed = true; } error = undefined; dirty = false; }; const nil = () => { changed = UNSET !== value; value = UNSET; error = undefined; }; const err = (e) => { const newError = toError(e); changed = !error || newError.name !== error.name || newError.message !== error.message; value = UNSET; error = newError; }; const settle = (settleFn) => (arg) => { computing = false; controller = undefined; settleFn(arg); if (changed) notify(watchers); }; const mark = watch(() => { dirty = true; controller?.abort("Aborted because source signal changed"); if (watchers.size) notify(watchers); else mark.cleanup(); }); const compute = () => observe(() => { if (computing) throw new CircularDependencyError("computed"); changed = false; if (isFunction(fn) && fn.constructor.name === "AsyncFunction") { if (controller) return value; controller = new AbortController; controller.signal.addEventListener("abort", () => { computing = false; controller = undefined; compute(); }, { once: true }); } let result; computing = true; try { result = controller ? fn(controller.signal) : fn(); } catch (e) { if (e instanceof DOMException && e.name === "AbortError") nil(); else err(e); computing = false; return; } if (result instanceof Promise) result.then(settle(ok), settle(err)); else if (result == null || UNSET === result) nil(); else ok(result); computing = false; }, mark); const c = { [Symbol.toStringTag]: TYPE_COMPUTED, get: () => { subscribe(watchers); flush(); if (dirty) compute(); if (error) throw error; return value; } }; return c; }; var isComputed = (value) => isObjectOfType(value, TYPE_COMPUTED); var isComputedCallback = (value) => isFunction(value) && value.length < 2; // node_modules/@zeix/cause-effect/src/signal.ts var UNSET = Symbol(); var isSignal = (value) => isState(value) || isComputed(value); var toSignal = (value) => isSignal(value) ? value : isComputedCallback(value) ? computed(value) : state(value); // node_modules/@zeix/cause-effect/src/effect.ts function effect(matcher) { const { signals, ok, err = console.error, nil = () => { } } = isFunction(matcher) ? { signals: [], ok: matcher } : matcher; let running = false; const run = watch(() => observe(() => { if (running) throw new CircularDependencyError("effect"); running = true; const errors = []; let pending2 = false; const values = signals.map((signal) => { try { const value = signal.get(); if (value === UNSET) pending2 = true; return value; } catch (e) { errors.push(toError(e)); return UNSET; } }); let cleanup = undefined; try { cleanup = pending2 ? nil() : errors.length ? err(...errors) : ok(...values); } catch (e) { cleanup = err(toError(e)); } finally { if (isFunction(cleanup)) run.off(cleanup); } running = false; }, run)); run(); return () => run.cleanup(); } // src/core/util.ts var DEV_MODE = true; var LOG_DEBUG = "debug"; var LOG_INFO = "info"; var LOG_WARN = "warn"; var LOG_ERROR = "error"; var idString = (id) => id ? `#${id}` : ""; var classString = (classList) => classList?.length ? `.${Array.from(classList).join(".")}` : ""; var isDefinedObject = (value) => !!value && typeof value === "object"; var isString = (value) => typeof value === "string"; var hasMethod = (obj, methodName) => isString(methodName) && (methodName in obj) && isFunction(obj[methodName]); var isElement = (node) => node.nodeType === Node.ELEMENT_NODE; var isCustomElement = (element) => element.localName.includes("-"); var isUpgradedComponent = (element) => { if (!isCustomElement(element)) return true; const ctor = customElements.get(element.localName); return !!ctor && element instanceof ctor; }; var elementName = (el) => el ? `<${el.localName}${idString(el.id)}${classString(el.classList)}>` : "<unknown>"; var valueString = (value) => isString(value) ? `"${value}"` : isDefinedObject(value) ? JSON.stringify(value) : String(value); var typeString = (value) => { if (value === null) return "null"; if (typeof value !== "object") return typeof value; if (Array.isArray(value)) return "Array"; if (Symbol.toStringTag in Object(value)) { return value[Symbol.toStringTag]; } return value.constructor?.name || "Object"; }; var log = (value, msg, level = LOG_DEBUG) => { if (DEV_MODE || [LOG_ERROR, LOG_WARN].includes(level)) console[level](msg, value); return value; }; // src/core/dom.ts class CircularMutationError extends Error { constructor(message) { super(message); this.name = "CircularMutationError"; } } var extractAttributes = (selector) => { const attributes = new Set; if (selector.includes(".")) attributes.add("class"); if (selector.includes("#")) attributes.add("id"); if (selector.includes("[")) { const parts = selector.split("["); for (let i = 1;i < parts.length; i++) { const part = parts[i]; if (!part.includes("]")) continue; const attrName = part.split("=")[0].trim().replace(/[^a-zA-Z0-9_-]/g, ""); if (attrName) attributes.add(attrName); } } return [...attributes]; }; var areElementArraysEqual = (arr1, arr2) => { if (arr1.length !== arr2.length) return false; const set1 = new Set(arr1); for (const el of arr2) { if (!set1.has(el)) return false; } return true; }; var fromEvents = (initialize, selector, events) => (host) => { const watchers = new Set; let value = isFunction(initialize) ? initialize(host) : initialize; const eventMap = new Map; let cleanup; const listen = () => { for (const [type, transform] of Object.entries(events)) { const listener = (e) => { const target = e.target; if (!target) return; const source = target.closest(selector); if (!source || !host.contains(source)) return; e.stopPropagation(); try { const newValue = transform({ event: e, host, target: source, value }); if (newValue == null) return; if (!Object.is(newValue, value)) { value = newValue; if (watchers.size > 0) notify(watchers); else if (cleanup) cleanup(); } } catch (error) { e.stopImmediatePropagation(); throw error; } }; eventMap.set(type, listener); host.addEventListener(type, listener); } cleanup = () => { if (eventMap.size) { for (const [type, listener] of eventMap) { host.removeEventListener(type, listener); } eventMap.clear(); } cleanup = undefined; }; }; return { [Symbol.toStringTag]: TYPE_COMPUTED, get() { subscribe(watchers); if (watchers.size && !eventMap.size) listen(); return value; } }; }; var observeSubtree = (parent, selector, callback) => { const observer = new MutationObserver(callback); const observerConfig = { childList: true, subtree: true }; const observedAttributes = extractAttributes(selector); if (observedAttributes.length) { observerConfig.attributes = true; observerConfig.attributeFilter = observedAttributes; } observer.observe(parent, observerConfig); return observer; }; var fromSelector = (selector) => (host) => { const watchers = new Set; const select = () => Array.from(host.querySelectorAll(selector)); let value = UNSET; let observer; let mutationDepth = 0; const MAX_MUTATION_DEPTH = 2; const observe2 = () => { value = select(); observer = observeSubtree(host, selector, () => { if (!watchers.size) { observer?.disconnect(); observer = undefined; return; } mutationDepth++; if (mutationDepth > MAX_MUTATION_DEPTH) { observer?.disconnect(); observer = undefined; mutationDepth = 0; throw new CircularMutationError("Circular mutation in element selection detected"); } try { const newElements = select(); if (!areElementArraysEqual(value, newElements)) { value = newElements; notify(watchers); } } finally { mutationDepth--; } }); }; return { [Symbol.toStringTag]: TYPE_COMPUTED, get() { subscribe(watchers); if (!watchers.size) value = select(); else if (!observer) observe2(); return value; } }; }; var reduced = (host, selector, reducer, initialValue) => computed(() => fromSelector(selector)(host).get().reduce(reducer, initialValue)); var read = (host, selector, map) => { const source = host.querySelector(selector); return map(source, source ? isUpgradedComponent(source) : false); }; var requireDescendant = (host, selector) => { const target = host.querySelector(selector); if (!target) { throw new Error(`Component ${elementName(host)} does not contain required <${selector}> element`); } return target; }; // src/component.ts var RESET = Symbol(); var RESERVED_WORDS = new Set([ "constructor", "prototype" ]); var HTML_ELEMENT_PROPS = new Set([ "id", "class", "className", "title", "role", "style", "dataset", "lang", "dir", "hidden", "children", "innerHTML", "outerHTML", "textContent", "innerText" ]); var isAttributeParser = (value) => isFunction(value) && value.length >= 2; var validatePropertyName = (prop) => { if (RESERVED_WORDS.has(prop)) { return `Property name "${prop}" is a reserved word`; } if (HTML_ELEMENT_PROPS.has(prop)) { return `Property name "${prop}" conflicts with inherited HTMLElement property`; } return null; }; var run = (fns, host, target = host) => { const cleanups = fns.filter(isFunction).map((fn) => fn(host, target)); return () => { cleanups.filter(isFunction).forEach((cleanup) => cleanup()); cleanups.length = 0; }; }; var select = () => ({ first: (selector, ...fns) => (host) => { const el = (host.shadowRoot || host).querySelector(selector); if (el) run(fns, host, el); }, all: (selector, ...fns) => (host) => { const cleanups = new Map; const root = host.shadowRoot || host; const attach = (target) => { if (!cleanups.has(target)) cleanups.set(target, run(fns, host, target)); }; const detach = (target) => { const cleanup = cleanups.get(target); if (isFunction(cleanup)) cleanup(); cleanups.delete(target); }; const applyToMatching = (fn) => (node) => { if (isElement(node)) { if (node.matches(selector)) fn(node); node.querySelectorAll(selector).forEach(fn); } }; const observer = observeSubtree(root, selector, (mutations) => { for (const mutation of mutations) { mutation.addedNodes.forEach(applyToMatching(attach)); mutation.removedNodes.forEach(applyToMatching(detach)); } }); root.querySelectorAll(selector).forEach(attach); return () => { observer.disconnect(); cleanups.forEach((cleanup) => cleanup()); cleanups.clear(); }; } }); var component = (name, init = {}, setup) => { for (const prop of Object.keys(init)) { const error = validatePropertyName(prop); if (error) { throw new TypeError(`${error} in component "${name}".`); } } customElements.define(name, class extends HTMLElement { debug; #signals = {}; #cleanup; static observedAttributes = Object.entries(init)?.filter(([, ini]) => isAttributeParser(ini)).map(([prop]) => prop) ?? []; constructor() { super(); for (const [prop, ini] of Object.entries(init)) { if (ini == null) continue; const result = isAttributeParser(ini) ? ini(this, null) : isFunction(ini) ? ini(this) : ini; if (result != null) this.setSignal(prop, toSignal(result)); } } connectedCallback() { if (DEV_MODE) { this.debug = this.hasAttribute("debug"); if (this.debug) log(this, "Connected"); } const fns = setup(this, select()); if (!Array.isArray(fns)) throw new TypeError(`Expected array of functions as return value of setup function in ${elementName(this)}`); this.#cleanup = run(fns, this); } disconnectedCallback() { if (isFunction(this.#cleanup)) this.#cleanup(); if (DEV_MODE && this.debug) log(this, "Disconnected"); } attributeChangedCallback(attr, old, value) { if (value === old || isComputed(this.#signals[attr])) return; const parse = init[attr]; if (!isAttributeParser(parse)) return; const parsed = parse(this, value, old); if (DEV_MODE && this.debug) log(value, `Attribute "${attr}" of ${elementName(this)} changed from ${valueString(old)} to ${valueString(value)}, parsed as <${typeString(parsed)}> ${valueString(parsed)}`); this[attr] = parsed; } getSignal(key) { const signal = this.#signals[key]; if (DEV_MODE && this.debug) log(signal, `Get ${typeString(signal)} "${String(key)}" in ${elementName(this)}`); return signal; } setSignal(key, signal) { const error = validatePropertyName(String(key)); if (error) throw new TypeError(`${error} on ${elementName(this)}.`); if (!isSignal(signal)) throw new TypeError(`Expected signal as value for property "${String(key)}" on ${elementName(this)}.`); const prev = this.#signals[key]; const writable = isState(signal); this.#signals[key] = signal; Object.defineProperty(this, key, { get: signal.get, set: writable ? signal.set : undefined, enumerable: true, configurable: writable }); if (prev && isState(prev)) prev.set(UNSET); if (DEV_MODE && this.debug) log(signal, `Set ${typeString(signal)} "${String(key)} in ${elementName(this)}`); } }); }; // src/core/context.ts var CONTEXT_REQUEST = "context-request"; class ContextRequestEvent extends Event { context; callback; subscribe2; constructor(context, callback, subscribe2 = false) { super(CONTEXT_REQUEST, { bubbles: true, composed: true }); this.context = context; this.callback = callback; this.subscribe = subscribe2; } } var provideContexts = (contexts) => (host) => { const listener = (e) => { const { context, callback } = e; if (contexts.includes(context) && isFunction(callback)) { e.stopImmediatePropagation(); callback(host.getSignal(String(context))); } }; host.addEventListener(CONTEXT_REQUEST, listener); return () => host.removeEventListener(CONTEXT_REQUEST, listener); }; var fromContext = (context, fallback) => (host) => { let consumed = toSignal(fallback); host.dispatchEvent(new ContextRequestEvent(context, (value) => { consumed = value; })); return consumed; }; // src/lib/parsers.ts var parseNumber = (parseFn, value) => { if (value == null) return; const parsed = parseFn(value); return Number.isFinite(parsed) ? parsed : undefined; }; var asBoolean = () => (_, value) => value != null && value !== "false"; var asInteger = (fallback = 0) => (_, value) => { if (value == null) return fallback; const trimmed = value.trim(); if (trimmed.toLowerCase().startsWith("0x")) return parseNumber((v) => parseInt(v, 16), trimmed) ?? fallback; const parsed = parseNumber(parseFloat, value); return parsed != null ? Math.trunc(parsed) : fallback; }; var asNumber = (fallback = 0) => (_, value) => parseNumber(parseFloat, value) ?? fallback; var asString = (fallback = "") => (_, value) => value ?? fallback; var asEnum = (valid) => (_, value) => { if (value == null) return valid[0]; const lowerValue = value.toLowerCase(); const matchingValid = valid.find((v) => v.toLowerCase() === lowerValue); return matchingValid ? value : valid[0]; }; var asJSON = (fallback) => (_, value) => { if ((value ?? fallback) == null) throw new ReferenceError("Value and fallback are both null or undefined"); if (value == null) return fallback; if (value === "") throw new SyntaxError("Empty string is not valid JSON"); let result; try { result = JSON.parse(value); } catch (error) { throw new SyntaxError(`Failed to parse JSON: ${String(error)}`, { cause: error }); } return result ?? fallback; }; // src/lib/effects.ts var RESOLVE_ERROR = Symbol("RESOLVE_ERROR"); var resolveReactive = (reactive, host, target, context) => { try { return isString(reactive) ? host.getSignal(reactive).get() : isSignal(reactive) ? reactive.get() : isFunction(reactive) ? reactive(target) : RESET; } catch (error) { if (context) { log(error, `Failed to resolve value of ${valueString(reactive)}${context ? ` for ${context}` : ""} in ${elementName(target)}${host !== target ? ` in ${elementName(host)}` : ""}`, LOG_ERROR); } return RESOLVE_ERROR; } }; var getOperationDescription = (op, name = "") => { const ops = { a: "attribute ", c: "class ", d: "dataset ", h: "inner HTML", m: "method call ", p: "property ", s: "style property ", t: "text content" }; return ops[op] + name; }; var createOperationHandlers = (host, target, operationDesc, resolve, reject) => { const ok = (verb) => () => { if (DEV_MODE && host.debug) { log(target, `${verb} ${operationDesc} of ${elementName(target)} in ${elementName(host)}`); } resolve?.(target); }; const err = (verb) => (error) => { log(error, `Failed to ${verb} ${operationDesc} of ${elementName(target)} in ${elementName(host)}`, LOG_ERROR); reject?.(error); }; return { ok, err }; }; var createDedupeSymbol = (operation, identifier) => { return Symbol(identifier ? `${operation}:${identifier}` : operation); }; var withReactiveValue = (reactive, host, target, context, handler) => { return effect(() => { const value = resolveReactive(reactive, host, target, context); if (value === RESOLVE_ERROR) return; handler(value); }); }; var isSafeURL = (value) => { if (/^(mailto|tel):/i.test(value)) return true; if (value.includes("://")) { try { const url = new URL(value, window.location.origin); return ["http:", "https:", "ftp:"].includes(url.protocol); } catch { return false; } } return true; }; var safeSetAttribute = (element, attr, value) => { if (/^on/i.test(attr)) throw new Error(`Unsafe attribute: ${attr}`); value = String(value).trim(); if (!isSafeURL(value)) throw new Error(`Unsafe URL for ${attr}: ${value}`); element.setAttribute(attr, value); }; var updateElement = (reactive, updater) => (host, target) => { const { op, name = "", read: read2, update } = updater; const fallback = read2(target); const operationDesc = getOperationDescription(op, name); if (isString(reactive) && isString(fallback) && host[reactive] === RESET) { host.attributeChangedCallback(reactive, null, fallback); } const { ok, err } = createOperationHandlers(host, target, operationDesc, updater.resolve, updater.reject); return effect(() => { const updateSymbol = createDedupeSymbol(op, name); const value = resolveReactive(reactive, host, target, operationDesc); if (value === RESOLVE_ERROR) return; const resolvedValue = value === RESET ? fallback : value === UNSET ? updater.delete ? null : fallback : value; if (updater.delete && resolvedValue === null) { enqueue(() => { updater.delete(target); return true; }, updateSymbol).then(ok("Deleted")).catch(err("delete")); } else if (resolvedValue != null) { const current = read2(target); if (Object.is(resolvedValue, current)) return; enqueue(() => { update(target, resolvedValue); return true; }, updateSymbol).then(ok("Updated")).catch(err("update")); } }); }; var insertOrRemoveElement = (reactive, inserter) => (host, target) => { const insertRemoveOk = (verb) => () => { if (DEV_MODE && host.debug) { log(target, `${verb} element in ${elementName(target)} in ${elementName(host)}`); } if (isFunction(inserter?.resolve)) { inserter.resolve(target); } else { const signal = isSignal(reactive) ? reactive : isString(reactive) ? host.getSignal(reactive) : undefined; if (isState(signal)) signal.set(0); } }; const insertRemoveErr = (verb) => (error) => { log(error, `Failed to ${verb} element in ${elementName(target)} in ${elementName(host)}`, LOG_ERROR); inserter?.reject?.(error); }; return effect(() => { const insertSymbol = createDedupeSymbol("i"); const removeSymbol = createDedupeSymbol("r"); const diff = resolveReactive(reactive, host, target, "insertion or deletion"); if (diff === RESOLVE_ERROR) return; const resolvedDiff = diff === RESET ? 0 : diff; if (resolvedDiff > 0) { if (!inserter) throw new TypeError(`No inserter provided`); enqueue(() => { for (let i = 0;i < resolvedDiff; i++) { const element = inserter.create(target); if (!element) continue; target.insertAdjacentElement(inserter.position ?? "beforeend", element); } return true; }, insertSymbol).then(insertRemoveOk("Inserted")).catch(insertRemoveErr("insert")); } else if (resolvedDiff < 0) { enqueue(() => { if (inserter && (inserter.position === "afterbegin" || inserter.position === "beforeend")) { for (let i = 0;i > resolvedDiff; i--) { if (inserter.position === "afterbegin") target.firstElementChild?.remove(); else target.lastElementChild?.remove(); } } else { target.remove(); } return true; }, removeSymbol).then(insertRemoveOk("Removed")).catch(insertRemoveErr("remove")); } }); }; var setText = (reactive) => updateElement(reactive, { op: "t", read: (el) => el.textContent, update: (el, value) => { Array.from(el.childNodes).filter((node) => node.nodeType !== Node.COMMENT_NODE).forEach((node) => node.remove()); el.append(document.createTextNode(value)); } }); var setProperty = (key, reactive = key) => updateElement(reactive, { op: "p", name: String(key), read: (el) => (key in el) ? el[key] : UNSET, update: (el, value) => { el[key] = value; } }); var show = (reactive) => updateElement(reactive, { op: "p", name: "hidden", read: (el) => !el.hidden, update: (el, value) => { el.hidden = !value; } }); var callMethod = (methodName, reactive, args) => updateElement(reactive, { op: "m", name: String(methodName), read: () => null, update: (el, value) => { if (value && hasMethod(el, methodName)) { if (args) el[methodName](...args); else el[methodName](); } } }); var focus = (reactive) => updateElement(reactive, { op: "m", name: "focus", read: (el) => el === document.activeElement, update: (el, value) => { if (value && hasMethod(el, "focus")) el.focus(); } }); var setAttribute = (name, reactive = name) => updateElement(reactive, { op: "a", name, read: (el) => el.getAttribute(name), update: (el, value) => { safeSetAttribute(el, name, value); }, delete: (el) => { el.removeAttribute(name); } }); var toggleAttribute = (name, reactive = name) => updateElement(reactive, { op: "a", name, read: (el) => el.hasAttribute(name), update: (el, value) => { el.toggleAttribute(name, value); } }); var toggleClass = (token, reactive = token) => updateElement(reactive, { op: "c", name: token, read: (el) => el.classList.contains(token), update: (el, value) => { el.classList.toggle(token, value); } }); var setStyle = (prop, reactive = prop) => updateElement(reactive, { op: "s", name: prop, read: (el) => el.style.getPropertyValue(prop), update: (el, value) => { el.style.setProperty(prop, value); }, delete: (el) => { el.style.removeProperty(prop); } }); var dangerouslySetInnerHTML = (reactive, options = {}) => updateElement(reactive, { op: "h", read: (el) => (el.shadowRoot || !options.shadowRootMode ? el : null)?.innerHTML ?? "", update: (el, html) => { const { shadowRootMode, allowScripts } = options; if (!html) { if (el.shadowRoot) el.shadowRoot.innerHTML = "<slot></slot>"; return ""; } if (shadowRootMode && !el.shadowRoot) el.attachShadow({ mode: shadowRootMode }); const target = el.shadowRoot || el; target.innerHTML = html; if (!allowScripts) return ""; target.querySelectorAll("script").forEach((script) => { const newScript = document.createElement("script"); newScript.appendChild(document.createTextNode(script.textContent ?? "")); target.appendChild(newScript); script.remove(); }); return " with scripts"; } }); var on = (type, listener, options = false) => (_, target) => { if (!isFunction(listener)) throw new TypeError(`Invalid event listener provided for "${type} event on element ${elementName(target)}`); target.addEventListener(type, listener, options); return () => target.removeEventListener(type, listener); }; var emitEvent = (type, reactive) => (host, target) => withReactiveValue(reactive, host, target, `custom event "${type}" detail`, (detail) => { if (detail === RESET || detail === UNSET) return; target.dispatchEvent(new CustomEvent(type, { detail, bubbles: true })); }); var pass = (reactives) => (host, target) => { if (!isDefinedObject(reactives)) throw new TypeError(`Reactives must be an object of passed signals`); if (!isCustomElement(target)) throw new TypeError(`Target ${elementName(target)} is not a custom element`); customElements.whenDefined(target.localName).then(() => { if (!hasMethod(target, "setSignal")) throw new TypeError(`Target ${elementName(target)} is not a UIElement component`); for (const [prop, reactive] of Object.entries(reactives)) { target.setSignal(prop, isString(reactive) ? host.getSignal(reactive) : toSignal(reactive)); } }).catch((error) => { throw new Error(`Failed to pass signals to ${elementName(target)}`, { cause: error }); }); }; export { updateElement, toggleClass, toggleAttribute, toSignal, state, show, setText, setStyle, setProperty, setAttribute, requireDescendant, reduced, read, provideContexts, pass, on, log, isState, isSignal, isComputed, insertOrRemoveElement, fromSelector, fromEvents, fromContext, focus, enqueue, emitEvent, effect, dangerouslySetInnerHTML, computed, component, callMethod, batch, asString, asNumber, asJSON, asInteger, asEnum, asBoolean, UNSET, RESET, LOG_WARN, LOG_INFO, LOG_ERROR, LOG_DEBUG };