UNPKG

@tempots/dom

Version:

Fully-typed frontend framework alternative to React and Angular

1,375 lines 76.6 kB
var ke = Object.defineProperty; var ie = (s) => { throw TypeError(s); }; var Ne = (s, e, t) => e in s ? ke(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t; var o = (s, e, t) => Ne(s, typeof e != "symbol" ? e + "" : e, t), oe = (s, e, t) => e.has(s) || ie("Cannot " + t); var R = (s, e, t) => (oe(s, e, "read from private field"), t ? t.call(s) : e.get(s)), le = (s, e, t) => e.has(s) ? ie("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(s) : e.set(s, t), Q = (s, e, t, r) => (oe(s, e, "write to private field"), r ? r.call(s, t) : e.set(s, t), t); const $e = (s, e, t) => s + (e - s) * t; const Ie = (s, e, t) => { const r = Math.max(s.length, e.length); let n = ""; for (let i = 0; i < r; i++) { let l = s.charCodeAt(i); isNaN(l) && (l = 97); let c = e.charCodeAt(i); isNaN(c) && (c = 97), n += String.fromCharCode(l + (c - l) * t); } return n; }, Re = (s, e, t) => new Date(s.getTime() + (e.getTime() - s.getTime()) * t), je = (s, e) => e, Ve = (s) => typeof s == "number" ? $e : typeof s == "string" ? Ie : s instanceof Date ? Re : je; var w; class Z { /** * Creates a new instance of `ElementPosition`. * @param index - The index of the element. * @param total - The total number of elements in the collection. */ constructor(e, t) { /** * The counter of the element starting from 1. */ o(this, "counter"); /** * Checks if the element is the first element in the collection. * @returns `true` if the element is the first element, `false` otherwise. */ o(this, "isFirst"); /** * Checks if the counter of the element is even. * @returns `true` if the counter is even, `false` otherwise. */ o(this, "isEven"); /** * Checks if the counter of the element is odd. * @returns `true` if the counter is odd, `false` otherwise. */ o(this, "isOdd"); le(this, w); o(this, "dispose", () => { var e; (e = R(this, w)) == null || e.dispose(), Q(this, w, void 0); }); this.index = e, this.total = t, this.counter = e + 1, this.isFirst = e === 0, this.isEven = e % 2 === 1, this.isOdd = e % 2 === 0; } /** * Checks if the element is the last element in the collection. * @returns `true` if the element is the last element, `false` otherwise. */ get isLast() { return R(this, w) == null && Q(this, w, this.total.map((e) => this.counter === e)), R(this, w); } } w = new WeakMap(); const k = class k { /** * Represents a signal with a value of type T. * * @param value - The initial value of the signal. * @param equals - A function that determines whether two values of type T are equal. * @public */ constructor(e, t) { /** * @internal */ o(this, "$__signal__", !0); /** * @internal */ o(this, "_value"); /** * @internal */ o(this, "_derivatives", []); /** * @internal */ o(this, "_onValueListeners", []); /** * @internal */ o(this, "_onDisposeListeners", []); /** * Gets the current value of the signal. * @returns The current value of the signal. */ o(this, "get", () => this._value); /** * Checks if the signal has any registered listeners. * @returns `true` if the signal has listeners, `false` otherwise. */ o(this, "hasListeners", () => this._onValueListeners.length > 0); /** * Registers a listener function to be called whenever the value of the signal changes. * The listener function will be immediately called with the current value of the signal. * Returns a function that can be called to unregister the listener. * * @param listener - The listener function to be called when the value of the signal changes. * @param options - Options for the listener. */ o(this, "on", (e, t = {}) => { t.skipInitial || e(this.get(), void 0); const r = t.once ? (i, l) => { n(), e(i, l); } : e; this._onValueListeners.push(r); const n = () => { this._onValueListeners.splice( this._onValueListeners.indexOf(r), 1 ), t.abortSignal != null && t.abortSignal.removeEventListener("abort", n); }; return t.abortSignal != null && t.abortSignal.addEventListener("abort", n), n; }); /** * @internal */ o(this, "_setAndNotify", (e) => { if (this._disposed) return; const t = this._value; this.equals(t, e) || (this._value = e, this._onValueListeners.forEach((n) => n(e, t))); }); /** * @internal */ o(this, "_disposed", !1); /** * Checks whether the signal is disposed. * @returns True if the signal is disposed, false otherwise. */ o(this, "isDisposed", () => this._disposed); /** * Adds a listener function to be called when the object is disposed. * @param listener - The listener function to be called when the object is disposed. * @returns A function that can be called to remove the listener. */ o(this, "onDispose", (e) => { this._onDisposeListeners.push(e); }); /** * Disposes the signal, releasing any resources associated with it. */ o(this, "dispose", () => { this._disposed || (this._disposed = !0, this._onDisposeListeners.forEach((e) => e()), this._onDisposeListeners.length = 0, this._derivatives.length = 0); }); /** * Creates a new computed signal by applying a transformation function to this signal's value. * * The `map` method is one of the most commonly used signal operations. It creates a new * computed signal that automatically updates whenever the source signal changes. The * transformation function is called with the current value and should return the new value. * * @example * ```typescript * const count = prop(5) * * // Transform to different types * const doubled = count.map(n => n * 2) * const message = count.map(n => `Count is ${n}`) * const isEven = count.map(n => n % 2 === 0) * * // Use in UI * html.div( * html.div('Original: ', count.map(String)), * html.div('Doubled: ', doubled.map(String)), * html.div('Message: ', message), * html.div('Is even: ', isEven.map(String)) * ) * ``` * * @example * ```typescript * // Chain multiple transformations * const user = prop({ name: 'John', age: 30 }) * const greeting = user * .map(u => u.name) * .map(name => name.toUpperCase()) * .map(name => `Hello, ${name}!`) * ``` * * @example * ```typescript * // With custom equality function for objects * const items = prop([{ id: 1, name: 'Item 1' }]) * const itemNames = items.map( * items => items.map(item => item.name), * (a, b) => JSON.stringify(a) === JSON.stringify(b) // deep equality * ) * ``` * * @typeParam O - The type of the transformed value * @param fn - Function that transforms the signal's value to a new value * @param equals - Optional function to determine if two transformed values are equal (defaults to strict equality) * @returns A new computed signal with the transformed value */ o(this, "map", (e, t = (r, n) => r === n) => { const r = new L(() => { try { return e(this.get()); } catch (n) { throw console.error("Error in Signal.map:", n), n; } }, t); return this.setDerivative(r), r; }); /** * Returns a new Signal that applies the given function to the value of the current Signal, * and then flattens the resulting Signal. * * @typeParam O - The type of the value emitted by the resulting Signal. * @param fn - The function to apply to the value of the current Signal. * @param equals - A function that determines whether two values of type O are equal. * Defaults to a strict equality check (===). * @returns A new Signal that emits the values of the resulting Signal. */ o(this, "flatMap", (e, t = (r, n) => r === n) => { const r = new L(() => { try { return e(this.get()).get(); } catch (n) { throw console.error("Error in Signal.flatMap:", n), n; } }, t); return this.setDerivative(r), r; }); /** * Invokes a callback function with the current value of the signal, without modifying the signal. * * @param fn - The callback function to be invoked with the current value of the signal. * @returns A new signal that emits the same value as the original signal and invokes the callback function. */ o(this, "tap", (e) => this.map((t) => (e(t), t))); /** * Returns a new Signal that emits the value at the specified key of the current value. * * @param key - The key of the value to retrieve. * @returns A new Signal that emits the value at the specified key. */ o(this, "at", (e) => this.map((t) => t[e])); /** * @internal */ o(this, "_$"); o(this, "filter", (e, t) => { let r = t ?? this.get(); const n = new L(() => { try { const i = this.get(); return r = e(i) ? i : r; } catch (i) { throw console.error("Error in Signal.filter:", i), i; } }, this.equals); return this.setDerivative(n), n; }); /** * Returns a new Computed object that applies the provided mapping function to the value of this Signal, * and filters out values that are `undefined` or `null`. * * @typeParam O - The type of the mapped value. * @param fn - The mapping function to apply to the value of this Signal. * @param startValue - The initial value for the Computed object. * @param equals - Optional equality function to determine if two values are equal. * @returns - A new Computed object with the mapped and filtered values. */ o(this, "filterMap", (e, t, r = (n, i) => n === i) => { let n = t; const i = new L(() => { try { const l = this.get(), c = e(l); return n = c ?? n; } catch (l) { throw console.error("Error in Signal.filterMap:", l), l; } }, r); return this.setDerivative(i), i; }); /** * Maps the values emitted by the signal to a new value asynchronously using the provided function. * If the function throws an error, it will be caught and logged. * If a recovery function is provided, it will be called with the error and its return value will be used as the mapped value. * If no recovery function is provided, the error will be logged as an unhandled promise rejection. * * @typeParam O - The type of the mapped value. * @param fn - The function to map the values emitted by the signal. The second argument to this function allows to cancel the previously running mapping function if it has not completed by the time a new value is emitted. * @param alt - The alternate value to use if the signal is disposed or the mapping function throws an error. * @param recover - The recovery function to handle errors thrown by the mapping function. * @param equals - The equality function to compare the mapped values for equality. * @returns A property that holds the mapped value and can be observed for changes. */ o(this, "mapAsync", (e, t, r, n = (i, l) => i === l) => { const i = _(t, n); let l = 0, c = new AbortController(); return i.onDispose( this.on(async (u) => { const a = ++l; c.abort(), c = new AbortController(); try { const h = await e(u, { abortSignal: c.signal }); a === l && i.set(h); } catch (h) { if (a === l) if (r != null) i.set(r(h)); else throw h; } }) ), i; }); /** * Maps the values of the signal using the provided function `fn`, and returns a new signal * containing the mapped values. If the mapped value is `undefined` or `null`, it is replaced * with the provided `alt` value. * * @typeParam O - The type of the mapped value. * @param fn - The function used to map the values of the signal. * @param alt - The alternative value to use when the mapped value is `undefined` or `null`. * @returns A new signal containing the mapped values. */ o(this, "mapMaybe", (e, t) => this.map((r) => e(r) ?? t)); /** * Feeds a property into the signal and sets up disposal behavior. * @param prop - The property to feed into the signal. * @param autoDisposeProp - Determines whether the property should be automatically disposed when the signal is disposed. * @returns The input property. */ o(this, "feedProp", (e, t = !1) => { const r = this.on(e.set); return e.onDispose(r), t ? this.onDispose(e.dispose) : this.onDispose(r), e; }); /** * Derives a new property from the current signal. * @param options - The options for the derived property. * @param options.autoDisposeProp - Determines whether the derived property should be automatically disposed. * @param options.equals - A function that determines if two values are equal. * @returns The derived property. */ o(this, "deriveProp", ({ autoDisposeProp: e = !0, equals: t } = {}) => this.feedProp(_(this.get(), t), e)); /** * Derives a new signal from the current signal. Useful to create a new signal that emits the same values as the current signal but can be disposed independently. * @returns A new signal that emits the same values as the current signal. */ o(this, "derive", () => this.map((e) => e)); /** * Returns a signal that emits the count of values received so far. * @returns A signal that emits the count of values received so far. */ o(this, "count", () => { let e = 0; return this.map(() => ++e); }); /** * Adds a computed value as a derivative of the signal. * When the computed value is disposed, it is automatically removed from the derivatives list. * Additionally, when the computed value is disposed, it sets the signal as dirty. * @param computed - The computed value to add as a derivative. */ o(this, "setDerivative", (e) => { this._derivatives.push(e), e.onDispose(() => { this._derivatives.splice( this._derivatives.indexOf(e), 1 ); }), e.onDispose(this.on(e.setDirty)), this.onDispose(e.dispose); }); this.equals = t, this._value = e; } /** * Gets the value of the signal. * @returns The current value of the signal. */ get value() { return this._value; } /** * Represents a collection of signals mapping to each key/field in the wrapped value. * @typeParam T - The type of the signals. */ get $() { return this._$ !== void 0 ? this._$ : this._$ = new Proxy(this, { get: (e, t) => this.at(t) }); } }; /** * Creates a Signal that holds the result of a Promise, with proper error handling. * * This static method creates a signal that starts with an initial value and updates * when the promise resolves. If the promise rejects, an optional recovery function * can provide a fallback value. * * @example * ```typescript * // Basic usage with API call * const userData = Signal.ofPromise( * fetch('/api/user').then(r => r.json()), * { loading: true }, // initial state * error => ({ error: error.message, loading: false }) // error recovery * ) * * // Use in UI * Ensure(userData, * (user) => html.div('Welcome, ', user.map(u => u.name)), * () => html.div('Loading...') * ) * ``` * * @example * ```typescript * // With custom equality function * const config = Signal.ofPromise( * loadConfig(), * {}, * () => ({}), * (a, b) => JSON.stringify(a) === JSON.stringify(b) // deep equality * ) * ``` * * @typeParam O - The type of the value returned by the Promise * @param promise - The Promise to use to feed the Signal * @param init - The initial value of the Signal before the Promise resolves * @param recover - Optional function to recover from Promise rejection and provide an alternative value * @param equals - Function to compare two values for equality (defaults to strict equality) * @returns A Signal that represents the result of the Promise */ o(k, "ofPromise", (e, t, r, n = (i, l) => i === l) => { const i = new k(t, n); return e.then((l) => i._setAndNotify(l)).catch((l) => { r != null ? i._setAndNotify(r(l)) : console.error( "Unhandled promise rejection in Signal.ofPromise:", l ); }), i; }), /** * Checks if a value is a Signal. * * @param value - The value to check. * @returns `true` if the value is a Signal, `false` otherwise. */ o(k, "is", (e) => ( // eslint-disable-next-line @typescript-eslint/no-explicit-any e != null && e.$__signal__ === !0 )); let d = k; const qe = typeof queueMicrotask == "function" ? queueMicrotask : (s) => Promise.resolve().then(s); class L extends d { /** * Represents a Signal object. * @param _fn - The function that returns the value of the signal. * @param equals - The function used to compare two values of type T for equality. */ constructor(t, r) { super(void 0, r); /** * @internal */ o(this, "$__computed__", !0); /** * @internal */ o(this, "_isDirty", !1); /** * Marks the signal as dirty, indicating that its value has changed and needs to be recalculated. * If the signal is already dirty or disposed, this method does nothing. * It also marks all dependent signals as dirty and schedules a notification to update their values. */ o(this, "setDirty", () => { this._isDirty || this._disposed || (this._isDirty = !0, this._derivatives.forEach((t) => t.setDirty()), this._scheduleNotify()); }); /** * @internal */ o(this, "_scheduleCount", 0); /** * Schedules a notification to be executed asynchronously. * If the signal is dirty, it will be updated and notified. * @internal */ o(this, "_scheduleNotify", () => { const t = ++this._scheduleCount; qe(() => { this._scheduleCount !== t || this._disposed || this._isDirty && (this._isDirty = !1, this._setAndNotify(this._fn())); }); }); /** {@inheritDoc Signal.get} */ o(this, "get", () => (this._isDirty && (this._isDirty = !1, this._setAndNotify(this._fn())), this._value)); this._fn = t, this.setDirty(); } /** * Checks if a value is an instance of `Computed`. * * @param value - The value to check. * @returns `true` if the value is an instance of `Computed`, `false` otherwise. */ static is(t) { return t != null && t.$__computed__ === !0; } /** {@inheritDoc Signal.value} */ get value() { return this.get(); } } const G = class G extends d { constructor() { super(...arguments); /** * @internal */ o(this, "$__prop__", !0); /** * Changes the value of the property and notifies its listeners. * * @param value - The new value of the property. */ o(this, "set", (t) => { this._setAndNotify(t); }); /** * Updates the value of the signal by applying the provided function to the current value. * @param fn - The function to apply to the current value. */ o(this, "update", (t) => { this._setAndNotify(t(this.get())); }); /** * Creates a reducer function that combines the provided reducer function and effects. * @param fn - The reducer function that takes the current state and an action, and returns the new state. * @param effects - An array of effects to be executed after the state is updated. * @returns A dispatch function that can be used to update the state and trigger the effects. */ o(this, "reducer", (t, ...r) => { const n = this; return function i(l) { const c = n.value; n.update((u) => t(u, l)), !n.equals(c, n.value) && r.forEach( (u) => u({ previousState: c, state: n.value, action: l, dispatch: i }) ); }; }); /** * Creates an isomorphism for the Signal. * An isomorphism is a pair of functions that convert values between two types, * along with an equality function to compare values of the second type. * * @param to - A function that converts values from type T to type O. * @param from - A function that converts values from type O to type T. * @param equals - An optional function that compares values of type O for equality. * Defaults to a strict equality check (===). * @returns A Prop object representing the isomorphism. */ o(this, "iso", (t, r, n = (i, l) => i === l) => { const i = new G(t(this.get()), n); return i.onDispose(this.on((l) => i.set(t(l)))), i.on((l) => this._setAndNotify(r(l))), i; }); /** * Returns a `Prop` that represents the value at the specified key of the current value. * * @param key - The key of the value to access. * @returns A `Prop` that represents the value at the specified key. */ o(this, "atProp", (t) => this.iso( (r) => r[t], (r) => ({ ...this.value, [t]: r }) )); } /** * Access for the current value of the property. */ get value() { return this.get(); } set value(t) { this._setAndNotify(t); } }; /** * Checks if a value is a Prop. * @param value - The value to check. * @returns `true` if the value is a Prop, `false` otherwise. */ o(G, "is", (t) => ( // eslint-disable-next-line @typescript-eslint/no-explicit-any t != null && t.$__prop__ === !0 )); let V = G; const K = (s, e, t = (r, n) => r === n) => { const r = new L(s, t); return e.forEach((n) => n.setDerivative(r)), r; }, Fe = (s, e, t = {}) => { let r = t.once ? () => { i(), s(); } : s; if (t.skipInitial) { let l = !1; const c = r; r = () => { l ? c() : l = !0; }; } const n = K(r, e), i = () => { n.dispose(), t.abortSignal != null && t.abortSignal.removeEventListener("abort", i); }; return t.abortSignal != null && t.abortSignal.addEventListener("abort", i), i; }, _ = (s, e = (t, r) => t === r) => new V(s, e), X = (s, e = (t, r) => t === r) => new d(s, e), ue = () => ( /* c8 ignore next */ typeof window < "u" ? window : void 0 ), y = { /** * Maps a value or a Signal to a new value. * If the value is a Signal, it returns a new Signal with the mapped value. * If the value is not a Signal, it returns the mapped value. * * @typeParam T - The type of the value. * @typeParam U - The type of the new value. * @param value - The value or Signal to map. * @param fn - The function to map the value. * @returns The mapped value. */ map: (s, e) => d.is(s) ? s.map(e) : e(s), /** * Wraps a value or a Signal instance into a Signal. * If the value is already a Signal, it returns the value itself. * If the value is not a Signal, it creates a new Signal instance with the given value. * * @typeParam O - The type of the value. * @param value - The value or Signal instance to wrap. * @param equals - A function that determines if two values are equal. Defaults to strict equality (===). * @returns A Signal instance. */ toSignal: (s, e) => d.is(s) ? s : X(s, e), /** * Wraps a value in a `Signal` if it is not already a `Signal`. * If the value is `null` or `undefined`, it returns `null` or `undefined` respectively. * @param value - The value to wrap or check. * @returns The wrapped value if it is not `null` or `undefined`, otherwise `null` or `undefined`. */ maybeToSignal: (s, e) => { if (s != null) return y.toSignal(s, e); }, /** * Gets the value from a `Signal` or the value itself if it is not a `Signal`. * @param value - The value or Signal instance to get the value from. * @returns The value. */ get: (s) => d.is(s) ? s.get() : s, /** * Adds a listener to a `Signal` or calls the listener immediately if it is not a `Signal`. * @param value - The value or Signal instance to add the listener to. * @param listener - The listener to call when the value changes. * @returns A function to remove the listener. */ on: (s, e) => d.is(s) ? s.on(e) : (e(s), () => { }), /** * Disposes of a value or a Signal. * If the value is a Signal, it disposes of the Signal. * If the value is not a Signal, it does nothing. * @param value - The value or Signal instance to dispose of. */ dispose: (s) => { d.is(s) && s.dispose(); }, /** * Derives a Prop from a Signal. * If the value is a Signal, it returns a new Prop with the derived value. * If the value is not a Signal, it returns a new Prop with the value. * @param value - The value or Signal instance to derive the Prop from. * @param options - The options for the derived Prop. * @param options.autoDisposeProp - Determines whether the derived Prop should be automatically disposed. * @param options.equals - A function that determines if two values are equal. * @returns A Prop instance. */ deriveProp: (s, { autoDisposeProp: e = !0, equals: t } = {}) => d.is(s) ? s.deriveProp({ autoDisposeProp: e, equals: t }) : _(s, t) }, ee = (...s) => (e, t) => { if (s.length === 1) return y.toSignal(s[0]).map(e); const r = s.filter((n) => d.is(n)); return K( () => e(...s.map((n) => y.get(n))), r, t ); }, Dt = (s) => { const e = Object.keys(s); return ee(...Object.values(s))( (...t) => Object.fromEntries(e.map((r, n) => [r, t[n]])) ); }, Ot = (...s) => (e, t = {}) => { const r = s.filter((n) => d.is(n)); return Fe( () => e(...s.map(y.get)), r, t ); }; class ae { constructor() { o(this, "_store", /* @__PURE__ */ new Map()); /** * Retrieves the value associated with the specified key from the memory store. * @param key - The key to retrieve the value for. * @returns The value associated with the key, or `null` if the key is not found. */ o(this, "getItem", (e) => this._store.get(e) ?? null); /** * Sets the value associated with the specified key in the memory store. * @param key - The key to set the value for. * @param value - The value to set. */ o(this, "setItem", (e, t) => { this._store.set(e, t); }); } } const he = ({ key: s, defaultValue: e, store: t, serialize: r = JSON.stringify, deserialize: n = JSON.parse, equals: i = (c, u) => c === u, onLoad: l = (c) => c }) => { const c = t.getItem(s), u = new V( c != null ? l(n(c)) : typeof e == "function" ? e() : e, i ); return u.on((a) => t.setItem(s, r(a))), u; }, Mt = (s) => { var e; return he({ ...s, /* c8 ignore next 3 */ store: ((e = ue()) == null ? void 0 : e.localStorage) ?? new ae() }); }, Ht = (s) => { var e; return he({ ...s, /* c8 ignore next 3 */ store: ((e = ue()) == null ? void 0 : e.sessionStorage) ?? new ae() }); }; function ce(s) { return typeof requestAnimationFrame == "function" ? requestAnimationFrame(s) : setTimeout(s, 0); } const Be = (s, e, t, r) => { const n = (r == null ? void 0 : r.duration) ?? 300, i = (r == null ? void 0 : r.easing) ?? ((A) => A), l = (r == null ? void 0 : r.equals) ?? ((A, I) => A === I); let c = r == null ? void 0 : r.interpolate, u = s, a = e(), h = performance.now(), g = null, T = !0; const p = new L(e, l), m = _(s, l); m.onDispose(() => { g !== null && cancelAnimationFrame(g); }), m.onDispose(p.dispose), t.forEach((A) => { A.setDerivative(p), A.onDispose(m.dispose); }); const C = (A) => { a = A, h = performance.now(), u = m.value, T && (T = !1, g = ce(re)); }, re = () => { const I = (performance.now() - h) / y.get(n), He = i(I); c == null && (c = Ve(u)); let ne = c(u, a, He); I >= 1 ? (T = !0, ne = a) : g = ce(re), m.set(ne); }; return p.on(C), m; }, kt = (s, e) => { const { initialValue: t, ...r } = e ?? {}; return Be( /* c8 ignore next 2 */ t ?? s.get(), s.get, [s], r ); }, We = (s, e) => { const t = Object.values(s).filter(d.is), r = Object.keys(s); return K(() => { const n = {}; for (const i of r) n[i] = y.get(s[i]); return e(n); }, t); }, Nt = (s) => We(s, (e) => e), $t = (s, e) => { const t = _(s.get()); let r = null; const n = s.on((i) => { r != null && clearTimeout(r), r = setTimeout( () => { r = null, t.set(i); }, typeof e == "function" ? e(i) : e ); }); return t.onDispose(() => { n(), r != null && clearTimeout(r); }), t; }, It = (s) => (...e) => ee( s, ...e )((t, ...r) => t(...r)); function Rt(...s) { return ee(...s)((...e) => { for (const t of e) if (t != null) return t; }); } const fe = /* @__PURE__ */ new Set(["checked", "disabled", "hidden"]), de = /* @__PURE__ */ new Set(["selected"]), pe = /* @__PURE__ */ new Set([ "rowSpan", "colSpan", "tabIndex", "valueAsNumber" ]), ge = /* @__PURE__ */ new Set(["valueAsDate"]), me = /* @__PURE__ */ new Set([ "value", "textContent", "innerText", "innerHTML", "outerHTML", "className", "classList" ]), Ue = (s, e) => de.has(s) ? (t) => { t == null || t !== !0 ? e.removeAttribute(s) : e.setAttribute(s, ""); } : fe.has(s) ? (t) => { t == null ? e[s] = null : e[s] = !!t; } : pe.has(s) ? (t) => { t == null ? e[s] = null : e[s] = Number(t); } : ge.has(s) ? (t) => { t == null ? e[s] = null : e[s] = t; } : me.has(s) ? (t) => { t == null ? e[s] = null : e[s] = String(t); } : (t) => { t == null ? e.removeAttribute(s) : e.setAttribute(s, t); }, Je = (s, e) => de.has(s) ? () => e.hasAttribute(s) : fe.has(s) ? () => !!e[s] : pe.has(s) ? () => Number(e[s]) : ge.has(s) ? () => e[s] : me.has(s) ? () => String(e[s]) : () => e.getAttribute(s), q = (s) => { const e = s; e && e.onblur && (e.onblur = null), !(!s || s.ownerDocument === void 0) && s.parentElement && s.parentElement.removeChild(s); }, Ge = (s) => ye(s) ? s : s.parentElement, ye = (s) => s.nodeType === 1; class Te extends Error { constructor(e) { super(`Provider not found: ${e.description}`); } } class D { /** * Constructs a new `DOMContext` instance. * * @param document - The `Document` instance associated with this context. * @param element - The `Element` instance associated with this context. * @param reference - An optional `Node` instance that serves as a reference for this context. * @param providers - The `Providers` instance associated with this context. * @param isFirstLevel - A boolean value indicating whether this context is at the first level, meaning the outermost node in the generated DOM. */ constructor(e, t, r, n) { /** * Creates a new DOM element (eg: HTML or SVG) with the specified tag name and namespace. * * @param tagName - The tag name of the element to create. * @param namespace - The namespace URI to create the element in, or `undefined` to create a standard HTML element. * @returns The newly created element. */ o(this, "createElement", (e, t) => t !== void 0 ? this.document.createElementNS(t, e) : this.document.createElement(e)); /** * Creates a new child element and appends it to the current element, returning a new context. * * This method creates a new DOM element with the specified tag name and namespace, * appends it to the current element, and returns a new DOMContext focused on the * newly created child element. This is the primary method for building DOM trees. * * @example * ```typescript * // Create HTML elements * const divCtx = ctx.makeChildElement('div', undefined) * const spanCtx = divCtx.makeChildElement('span', undefined) * * // Create SVG elements * const svgCtx = ctx.makeChildElement('svg', 'http://www.w3.org/2000/svg') * const circleCtx = svgCtx.makeChildElement('circle', 'http://www.w3.org/2000/svg') * ``` * * @param tagName - The tag name of the element to create (e.g., 'div', 'span', 'svg') * @param namespace - The namespace URI for the element, or undefined for HTML elements * @returns A new DOMContext focused on the newly created child element */ o(this, "makeChildElement", (e, t) => { const r = this.createElement(e, t); return this.appendOrInsert(r), this.withElement(r); }); /** * Creates a new text node with the specified text content. * @param text - The text content for the new text node. * @returns A new `Text` node with the specified text content. */ o(this, "createText", (e) => this.document.createTextNode(e)); /** * Creates a new text node with the specified text content and appends it to the current element. * @param text - The text content for the new text node. * @returns A new `DOMContext` with a reference to the new text node. */ o(this, "makeChildText", (e) => { const t = this.createText(e); return this.appendOrInsert(t), this.withReference(t); }); /** * Sets the text content of the current element. * @param text - The text content to set. */ o(this, "setText", (e) => { this.reference.nodeValue = e; }); /** * Gets the text content of the current element or text node. * @returns The text content of the current element or text node. */ o(this, "getText", () => { var e; return ((e = this.reference) == null ? void 0 : e.nodeValue) ?? this.element.textContent ?? ""; }); /** * Creates a new `DOMContext` with a reference to a newly created text node. * The text node is appended or inserted to the current `DOMContext`. * The new `DOMContext` with the reference is returned. */ o(this, "makeRef", () => { const e = this.createText(""); return this.appendOrInsert(e), this.withReference(e); }); /** * Appends or inserts a child node to the element, depending on whether a reference node is provided. * * @param child - The child node to append or insert. */ o(this, "appendOrInsert", (e) => { this.reference === void 0 ? this.element.appendChild(e) : this.element.insertBefore(e, this.reference); }); /** * Creates a new `DOMContext` instance with the provided `element`. * @param element - The DOM element to use in the new `DOMContext` instance. * @returns A new `DOMContext` instance with the provided `element`. */ o(this, "withElement", (e) => new D(this.document, e, void 0, this.providers)); /** * Creates a portal to render content in a different part of the DOM tree. * * Portals allow you to render child components into a DOM node that exists outside * the parent component's DOM hierarchy. This is useful for modals, tooltips, * dropdowns, and other UI elements that need to break out of their container's * styling or z-index context. * * @example * ```typescript * // Portal to a modal container * const modalCtx = ctx.makePortal('#modal-root') * const modal = modalCtx.makeChildElement('div', undefined) * * // Add modal content * modal.makeChildText('This renders in #modal-root') * ``` * * @example * ```typescript * // Portal to an existing element reference * const tooltipContainer = document.getElementById('tooltip-container')! * const tooltipCtx = ctx.makePortal(tooltipContainer) * * // Render tooltip content * const tooltip = tooltipCtx.makeChildElement('div', undefined) * tooltip.addClasses(['tooltip', 'tooltip-top']) * ``` * * @example * ```typescript * // Portal for dropdown menu * const dropdownCtx = ctx.makePortal('body') // Render at body level * const dropdown = dropdownCtx.makeChildElement('div', undefined) * dropdown.addClasses(['dropdown-menu']) * dropdown.setStyle('position', 'absolute') * dropdown.setStyle('top', '100px') * dropdown.setStyle('left', '50px') * ``` * * @param selector - CSS selector string or HTMLElement reference for the portal target * @returns A new DOMContext focused on the portal target element * @throws {Error} When the selector doesn't match any element in the document */ o(this, "makePortal", (e) => { const t = typeof e == "string" ? this.document.querySelector(e) : e; if (t == null) throw new Error(`Cannot find element by selector for portal: ${e}`); return this.withElement(t); }); /** * Creates a new `DOMContext` instance with the specified reference. * * @param reference - The optional `Text` node to use as the reference for the new `DOMContext`. * @returns A new `DOMContext` instance with the specified reference. */ o(this, "withReference", (e) => new D(this.document, this.element, e, this.providers)); /** * Sets a provider for the given provider mark. * * @param mark - The provider mark to set the provider for. * @param value - The provider to set for the given mark. * @returns A new `DOMContext` instance with the specified provider. */ o(this, "setProvider", (e, t, r) => new D(this.document, this.element, this.reference, { ...this.providers, [e]: [t, r] })); /** * Retrieves a provider for the given provider mark. * * @param mark - The provider mark to retrieve the provider for. * @returns The provider for the given mark. * @throws Throws `ProviderNotFoundError` if the provider for the given mark is not found. */ o(this, "getProvider", (e) => { if (this.providers[e] === void 0) throw new Te(e); const [t, r] = this.providers[e]; return { value: t, onUse: r }; }); o(this, "clear", (e) => { e && (this.reference !== void 0 ? q(this.reference) : q(this.element)); }); /** * Adds classes to the element. * @param tokens - The class names to add. */ o(this, "addClasses", (e) => { this.element.classList.add(...e); }); /** * Removes classes from the element. * @param tokens - The class names to remove. */ o(this, "removeClasses", (e) => { this.element.classList.remove(...e); }); /** * Gets the classes of the element. * @returns The classes of the element. */ o(this, "getClasses", () => Array.from(this.element.classList)); /** * Adds an event listener to the element. * @param event - The event to listen for. * @param listener - The listener to call when the event occurs. * @param options - The options for the event listener. * @returns A function to remove the event listener. */ o(this, "on", (e, t, r) => { const n = (i) => t(i, this); return this.element.addEventListener(e, n, r), (i) => { i && this.element.removeEventListener(e, n, r); }; }); /** * Returns `true` if the context is a browser DOM context. * @returns `true` if the context is a browser DOM context. * @deprecated Use `isBrowser()` instead. */ o(this, "isBrowserDOM", () => !0); /** * Returns `true` if the context is a browser context. * @returns `true` if the context is a browser context. */ o(this, "isBrowser", () => !0); /** * Returns `true` if the context is a headless DOM context. * @returns `true` if the context is a headless DOM context. */ o(this, "isHeadlessDOM", () => !1); /** * Returns `true` if the context is a headless context. * @returns `true` if the context is a headless context. */ o(this, "isHeadless", () => !1); /** * Sets the style of the element. * @param name - The name of the style to set. * @param value - The value of the style to set. */ o(this, "setStyle", (e, t) => { this.element.style[e] = t; }); /** * Gets the style of the element. * @param name - The name of the style to get. * @returns The value of the style. */ o(this, "getStyle", (e) => this.element.style[e]); o(this, "makeAccessors", (e) => ({ get: Je(e, this.element), set: Ue(e, this.element) })); o(this, "getWindow", () => this.document.defaultView); this.document = e, this.element = t, this.reference = r, this.providers = n; } /** * Creates a new `DOMContext` instance for the given `Element` and optional reference `Node`. * * @param element - The `HTMLElement` to create the `DOMContext` for. * @param ref - A reference `Node` to associate with the `DOMContext` or undefined . * @param providers - The providers to associate with the `DOMContext`. * @returns A new `DOMContext` instance. */ static of(e, t, r) { return new D(e.ownerDocument, e, t, r); } } const Xe = (s) => Symbol(s), te = (s, e) => { const t = s(e); return (r = !0) => t(r); }, jt = (s, e, { doc: t, clear: r, disposeWithParent: n = !0, providers: i = {} } = {}) => { const l = typeof e == "string" ? (t ?? document).querySelector(e) : e; if (l === null) throw new Ye( `Cannot find element by selector for render: ${e}` ); r !== !1 && (t ?? l.ownerDocument) != null && l.nodeType === 1 && (l.innerHTML = ""); const c = Ge(l), u = ye(l) ? void 0 : l, a = D.of(c, u, i), h = te(s, a); let g; return n && (g = new MutationObserver((T) => { var p; (p = T[0]) == null || p.removedNodes.forEach((m) => { m === l && (g == null || g.disconnect(), h(l.nodeType !== 1)); }); }), g.observe(l.parentElement, { childList: !0, subtree: !1, attributes: !1 })), () => { g == null || g.disconnect(), h(!0); }; }, Vt = (s, { startUrl: e = "https://example.com", selector: t, providers: r = {} } = { selector: "body" }) => { const n = y.toSignal(e).deriveProp(), i = new Ee(t, void 0), l = new O(i, void 0, { currentURL: n }, r); return { clear: te(s(), l), root: i, currentURL: n }; }; class Ye extends Error { constructor(e) { super(e); } } const be = "data-tts-node", F = "data-tts-class", B = "data-tts-style", W = "data-tts-html", U = "data-tts-text", J = "data-tts-attrs"; class qt { constructor({ select: e, getAttribute: t, setAttribute: r, getClass: n, setClass: i, getStyles: l, setStyles: c, appendHTML: u, getInnerHTML: a, setInnerHTML: h, getInnerText: g, setInnerText: T }) { /** * Selects elements from the headless environment. * @param selector - The selector to select elements from. The supported selectors are CSS selectors whose complexity depends on the adapter implementation. * @returns An array of elements. */ o(this, "select"); /** * Gets the value of an attribute from an element. * @param el - The element to get the attribute from. * @param attr - The attribute to get the value from. * @returns The value of the attribute or null if the attribute is not set. */ o(this, "getAttribute"); /** * Sets the value of an attribute on an element. * @param el - The element to set the attribute on. * @param attr - The attribute to set the value of. * @param value - The value to set the attribute to. */ o(this, "setAttribute"); /** * Gets the class of an element. * @param el - The element to get the class from. * @returns The class of the element or an empty string if the class is not set. */ o(this, "getClass"); /** * Sets the class of an element. * @param el - The element to set the class on. * @param cls - The class to set. */ o(this, "setClass"); /** * Gets the styles of an element. * @param el - The element to get the styles from. * @returns The styles of the element. */ o(this, "getStyles"); /** * Sets the styles of an element. * @param el - The element to set the styles on. */ o(this, "setStyles"); /** * Appends HTML to an element. * @param el - The element to append the HTML to. * @param html - The HTML to append. */ o(this, "appendHTML"); /** * Gets the inner HTML of an element. * @param el - The element to get the inner HTML from. * @returns The inner HTML of the element or an empty string if the inner HTML is not set. */ o(this, "getInnerHTML"); /** * Sets the inner HTML of an element. * @param el - The element to set the inner HTML on. * @param html - The inner HTML to set. */ o(this, "setInnerHTML"); /** * Gets the inner text of an element. * @param el - The element to get the inner text from. * @returns The inner text of the element or an empty string if the inner text is not set. */ o(this, "getInnerText"); /** * Sets the inner text of an element. * @param el - The element to set the inner text on. * @param text - The inner text to set. */ o(this, "setInnerText"); /** * Sets the content of the root element from a HeadlessPortal. Generally this will be the same instance that is * returned by `runHeadless`. * * @param root - The HeadlessPortal containing the content to set. * @param setPlaceholders - Whether to set placeholders for the content. This allows you to restore the original content * when you render on the server and then hydrate on the client. */ o(this, "setFromRoot", (e, t) => { e.getPortals().forEach((n) => { const i = typeof n.selector == "string" ? this.select(n.selector) : [n.selector]; for (const l of i) { if (l == null) throw new Error( `Cannot find element by selector for render: ${n.selector}` ); if (n.hasChildren() && this.appendHTML(l, n.contentToHTML(t)), n.hasInnerHTML()) { if (t) { const c = this.getInnerHTML(l); c != null && this.setAttribute(l, W, c); } this.setInnerHTML(l, n.getInnerHTML()); } if (n.hasInnerText()) { if (t) { const c = this.getInnerText(l); c != null && this.setAttribute(l, U, c); } this.setInnerText(l, n.getInnerText()); } if (n.hasClasses()) { if (t) { const c = this.getClass(l); c != null && this.setAttribute(l, F, c); } this.setClass(l, n.getClasses().join(" ")); } if (n.hasStyles()) { if (t) { const c = this.getStyles(l); Object.keys(c).length > 0 && this.setAttribute( l, B, JSON.stringify(c) ); } this.setStyles(l, n.getStyles()); } if (n.hasAttributes()) { const c = n.getAttributes(); if (t) { const u = []; c.forEach(([a]) => { const h = this.getAttribute(l, a); h != null && u.push([a, h]); }), u.length > 0 && this.setAttribute( l, J, JSON.stringify(Object.fromEntries(u)) ); } c.forEach(([u, a]) => { this.setAttribute(l, u, a); }); } } }); }); this.select = e, this.getAttribute = t, this.setAttribute = r, this.getClass = n, this.setClass = i, this.getStyles = l, this.setStyles = c, this.appendHTML = u, this.getInnerHTML = a, this.setInnerHTML = h, this.getInnerText = g, this.setInnerText = T; } } const ze = () => { document.querySelectorAll(`[${be}]`).forEach(q); }, Qe = (s) => { const e = s.getAttribute(F); s.removeAttribute(F), e != null && s.setAttribute("class", e); }, Ze = () => { document.querySelectorAll(`[${F}]`).forEach((e) => Qe(e)); }, Ke = (s) => { const e = s.getAttribute(W); s.removeAttribute(W), e != null && (s.innerHTML = e); }, et = () => { document.querySelectorAll(`[${W}]`).forEach((e) => Ke(e)); }, tt = (s) => { const e = s.getAttribute(U); s.removeAttribute(U), e != null && (s.innerText = e); }, st = () => { document.querySelectorAll(`[${U}]`).forEach((e) => tt(e)); }, Se = (s) => JSON.parse(s.replace(/&quot;/g, '"')), rt = (s) => { const e = s.getAttribute(B); if (s.removeAttribute(B), e != null) { const t = Se(e); Object.entries(t).forEach(([r, n]) => { s.style.setProperty(r, n); }); } }, nt = () => { document.querySelectorAll(`[${B}]`).forEach((e) => rt(e)); }, it = (s) => { const e = s.getAttribute(J); if (s.removeAttribute(J), e != null) { const t = Se(e); Object.entries(t).forEach(([r, n]) => { n == null ? s.removeAttribute(r) : s.setAttribute(r, n); }); } }, ot = () => { document.querySelectorAll(`[${J}]`).forEach((e) => it(e)); }, Ft = () => { ze(), Ze(), st(), et(), nt(), ot(); }, S = Symbol("class"), v = Symbol("style"), P = Symbol("handler"), Ae = () => Math.random().toString(36).substring(2, 15), lt = (s) => s.replace(/<[^>]*>?/g, ""); class _e { constructor(e) { o(this, "id", Ae()); o(this, "pr