From 8201c3dbbfeaaac50a12ff5508dbf59edbeb4d99 Mon Sep 17 00:00:00 2001 From: Jan Andrle Date: Thu, 7 Nov 2024 10:17:54 +0100 Subject: [PATCH] :zap: :bug: finalizing WC + simulateSlots --- bs/build.js | 2 +- dist/dde-with-signals.js | 1210 ++++++++++------- dist/dde.js | 800 ++++++----- dist/esm-with-signals.d.ts | 17 +- dist/esm-with-signals.js | 1210 ++++++++++------- dist/esm.d.ts | 17 +- dist/esm.js | 800 ++++++----- docs/global.css | 1 + docs/index.html | 2 +- docs/p02-elements.html | 14 +- docs/p03-events.html | 10 +- docs/p04-signals.html | 12 +- docs/p05-scopes.html | 6 +- docs/p06-customElement.html | 68 +- docs_src/components/code.html.js | 3 +- .../examples/customElement/shadowRoot.js | 15 +- .../examples/customElement/simulateSlots.js | 41 + .../components/mnemonic/customElement-init.js | 8 +- docs_src/p06-customElement.html.js | 52 +- examples/index.js | 10 +- index.d.ts | 17 +- src/dom.js | 9 +- 22 files changed, 2569 insertions(+), 1755 deletions(-) create mode 100644 docs_src/components/examples/customElement/simulateSlots.js diff --git a/bs/build.js b/bs/build.js index 947928c..ba04b6b 100755 --- a/bs/build.js +++ b/bs/build.js @@ -17,7 +17,7 @@ $.api("", true) "npx esbuild '::file::'", "--platform=neutral", "--bundle", - minify==="full" ? "--minify" : "--minify-syntax --minify-identifiers", + //minify==="full" ? "--minify" : "--minify-syntax --minify-identifiers", "--legal-comments=inline", "--packages=external", "--outfile='::out::'" diff --git a/dist/dde-with-signals.js b/dist/dde-with-signals.js index 1535b54..fba7e15 100644 --- a/dist/dde-with-signals.js +++ b/dist/dde-with-signals.js @@ -1,51 +1,58 @@ //deka-dom-el library is available via global namespace `dde` (()=> { // src/signals-common.js -var k = { - isSignal(t) { - return !1; +var signals_global = { + isSignal(attributes) { + return false; }, - processReactiveAttribute(t, e, n, r) { - return n; + processReactiveAttribute(obj, key, attr, set) { + return attr; } }; -function B(t, e = !0) { - return e ? Object.assign(k, t) : (Object.setPrototypeOf(t, k), t); +function registerReactivity(def, global = true) { + if (global) return Object.assign(signals_global, def); + Object.setPrototypeOf(def, signals_global); + return def; } -function W(t) { - return k.isPrototypeOf(t) && t !== k ? t : k; +function signals(_this) { + return signals_global.isPrototypeOf(_this) && _this !== signals_global ? _this : signals_global; } // src/helpers.js -var T = (...t) => Object.prototype.hasOwnProperty.call(...t); -function S(t) { - return typeof t > "u"; +var hasOwn = (...a) => Object.prototype.hasOwnProperty.call(...a); +function isUndef(value) { + return typeof value === "undefined"; } -function X(t) { - let e = typeof t; - return e !== "object" ? e : t === null ? "null" : Object.prototype.toString.call(t); +function typeOf(v) { + const t = typeof v; + if (t !== "object") return t; + if (v === null) return "null"; + return Object.prototype.toString.call(v); } -function q(t, e) { - if (!t || !(t instanceof AbortSignal)) - return !0; - if (!t.aborted) - return t.addEventListener("abort", e), function() { - t.removeEventListener("abort", e); - }; +function onAbort(signal2, listener) { + if (!signal2 || !(signal2 instanceof AbortSignal)) + return true; + if (signal2.aborted) + return; + signal2.addEventListener("abort", listener); + return function cleanUp() { + signal2.removeEventListener("abort", listener); + }; } -function F(t, e) { - let { observedAttributes: n = [] } = t.constructor; - return n.reduce(function(r, o) { - return r[pt(o)] = e(t, o), r; +function observedAttributes(instance, observedAttribute2) { + const { observedAttributes: observedAttributes3 = [] } = instance.constructor; + return observedAttributes3.reduce(function(out, name) { + out[kebabToCamel(name)] = observedAttribute2(instance, name); + return out; }, {}); } -function pt(t) { - return t.replace(/-./g, (e) => e[1].toUpperCase()); +function kebabToCamel(name) { + return name.replace(/-./g, (x) => x[1].toUpperCase()); } // src/dom-common.js -var d = { - setDeleteAttr: lt, +var enviroment = { + setDeleteAttr, ssr: "", D: globalThis.document, F: globalThis.DocumentFragment, @@ -53,644 +60,823 @@ var d = { S: globalThis.SVGElement, M: globalThis.MutationObserver }; -function lt(t, e, n) { - if (Reflect.set(t, e, n), !!S(n)) { - if (Reflect.deleteProperty(t, e), t instanceof d.H && t.getAttribute(e) === "undefined") - return t.removeAttribute(e); - if (Reflect.get(t, e) === "undefined") - return Reflect.set(t, e, ""); - } +function setDeleteAttr(obj, prop, val) { + Reflect.set(obj, prop, val); + if (!isUndef(val)) return; + Reflect.deleteProperty(obj, prop); + if (obj instanceof enviroment.H && obj.getAttribute(prop) === "undefined") + return obj.removeAttribute(prop); + if (Reflect.get(obj, prop) === "undefined") + return Reflect.set(obj, prop, ""); } -var C = "__dde_lifecyclesToEvents", _ = "dde:connected", O = "dde:disconnected", M = "dde:attributeChanged"; +var keyLTE = "__dde_lifecyclesToEvents"; +var evc = "dde:connected"; +var evd = "dde:disconnected"; +var eva = "dde:attributeChanged"; // src/dom.js -var A = [{ +var scopes = [{ get scope() { - return d.D.body; + return enviroment.D.body; }, - host: (t) => t ? t(d.D.body) : d.D.body, - prevent: !0 -}], m = { + host: (c) => c ? c(enviroment.D.body) : enviroment.D.body, + prevent: true +}]; +var scope = { get current() { - return A[A.length - 1]; + return scopes[scopes.length - 1]; }, get host() { return this.current.host; }, preventDefault() { - let { current: t } = this; - return t.prevent = !0, t; + const { current } = this; + current.prevent = true; + return current; }, get state() { - return [...A]; + return [...scopes]; }, - push(t = {}) { - return A.push(Object.assign({}, this.current, { prevent: !1 }, t)); + push(s = {}) { + return scopes.push(Object.assign({}, this.current, { prevent: false }, s)); }, pushRoot() { - return A.push(A[0]); + return scopes.push(scopes[0]); }, pop() { - if (A.length !== 1) - return A.pop(); + if (scopes.length === 1) return; + return scopes.pop(); } }; -function Y(...t) { - return this.appendOriginal(...t), this; +function append(...els) { + this.appendOriginal(...els); + return this; } -function ht(t) { - return t.append === Y || (t.appendOriginal = t.append, t.append = Y), t; +function chainableAppend(el) { + if (el.append === append) return el; + el.appendOriginal = el.append; + el.append = append; + return el; } -var $; -function j(t, e, ...n) { - let r = W(this), o = 0, c, i; - switch ((Object(e) !== e || r.isSignal(e)) && (e = { textContent: e }), !0) { - case typeof t == "function": { - o = 1, m.push({ scope: t, host: (...v) => v.length ? (o === 1 ? n.unshift(...v) : v.forEach((l) => l(i)), void 0) : i }), c = t(e || void 0); - let a = c instanceof d.F; - if (c.nodeName === "#comment") - break; - let h = j.mark({ +var namespace; +function createElement(tag, attributes, ...addons) { + const s = signals(this); + let scoped = 0; + let el, el_host; + if (Object(attributes) !== attributes || s.isSignal(attributes)) + attributes = { textContent: attributes }; + switch (true) { + case typeof tag === "function": { + scoped = 1; + scope.push({ scope: tag, host: (...c) => c.length ? (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0) : el_host }); + el = tag(attributes || void 0); + const is_fragment = el instanceof enviroment.F; + if (el.nodeName === "#comment") break; + const el_mark = createElement.mark({ type: "component", - name: t.name, - host: a ? "this" : "parentElement" + name: tag.name, + host: is_fragment ? "this" : "parentElement" }); - c.prepend(h), a && (i = h); + el.prepend(el_mark); + if (is_fragment) el_host = el_mark; break; } - case t === "#text": - c = P.call(this, d.D.createTextNode(""), e); + case tag === "#text": + el = assign.call(this, enviroment.D.createTextNode(""), attributes); break; - case (t === "<>" || !t): - c = P.call(this, d.D.createDocumentFragment(), e); + case (tag === "<>" || !tag): + el = assign.call(this, enviroment.D.createDocumentFragment(), attributes); break; - case !!$: - c = P.call(this, d.D.createElementNS($, t), e); + case Boolean(namespace): + el = assign.call(this, enviroment.D.createElementNS(namespace, tag), attributes); break; - case !c: - c = P.call(this, d.D.createElement(t), e); + case !el: + el = assign.call(this, enviroment.D.createElement(tag), attributes); } - return ht(c), i || (i = c), n.forEach((a) => a(i)), o && m.pop(), o = 2, c; + chainableAppend(el); + if (!el_host) el_host = el; + addons.forEach((c) => c(el_host)); + if (scoped) scope.pop(); + scoped = 2; + return el; } -function Wt(t, e = t, n = void 0) { - let r = Symbol.for("default"), o = Array.from(e.querySelectorAll("slot")).reduce((i, a) => Reflect.set(i, a.name || r, a) && i, {}), c = T(o, r); - if (t.append = new Proxy(t.append, { - apply(i, a, h) { - if (!h.length) - return t; - let v = d.D.createDocumentFragment(); - for (let l of h) { - if (!l || !l.slot) { - c && v.appendChild(l); +function simulateSlots(element, root, mapper) { + if (typeof root !== "object") { + mapper = root; + root = element; + } + const _default = Symbol.for("default"); + const slots = Array.from(root.querySelectorAll("slot")).reduce((out, curr) => Reflect.set(out, curr.name || _default, curr) && out, {}); + const has_d = hasOwn(slots, _default); + element.append = new Proxy(element.append, { + apply(orig, _, els) { + if (els[0] === root) return orig.apply(element, els); + if (!els.length) return element; + const d = enviroment.D.createDocumentFragment(); + for (const el of els) { + if (!el || !el.slot) { + if (has_d) d.append(el); continue; } - let x = l.slot, y = o[x]; - vt(l, "remove", "slot"), y && (bt(y, l, n), Reflect.deleteProperty(o, x)); + const name = el.slot; + const slot = slots[name]; + elementAttribute(el, "remove", "slot"); + if (!slot) continue; + simulateSlotReplace(slot, el, mapper); + Reflect.deleteProperty(slots, name); } - return c && (o[r].replaceWith(v), Reflect.deleteProperty(o, r)), t.append = i, t; + if (has_d) { + slots[_default].replaceWith(d); + Reflect.deleteProperty(slots, _default); + } + element.append = orig; + return element; } - }), t !== e) { - let i = Array.from(t.childNodes); - i.forEach((a) => a.remove()), t.append(...i); + }); + if (element !== root) { + const els = Array.from(element.childNodes); + els.forEach((el) => el.remove()); + element.append(...els); } - return e; + return root; } -function bt(t, e, n) { - n && n(t, e); +function simulateSlotReplace(slot, element, mapper) { + if (mapper) mapper(slot, element); try { - t.replaceWith(P(e, { className: [e.className, t.className], dataset: { ...t.dataset } })); - } catch { - t.replaceWith(e); + slot.replaceWith(assign(element, { className: [element.className, slot.className], dataset: { ...slot.dataset } })); + } catch (_) { + slot.replaceWith(element); } } -j.mark = function(t, e = !1) { - t = Object.entries(t).map(([o, c]) => o + `="${c}"`).join(" "); - let n = e ? "" : "/", r = d.D.createComment(``); - return e && (r.end = d.D.createComment("")), r; +createElement.mark = function(attrs, is_open = false) { + attrs = Object.entries(attrs).map(([n, v]) => n + `="${v}"`).join(" "); + const end = is_open ? "" : "/"; + const out = enviroment.D.createComment(``); + if (is_open) out.end = enviroment.D.createComment(""); + return out; }; -function qt(t) { - let e = this; - return function(...r) { - $ = t; - let o = j.call(e, ...r); - return $ = void 0, o; +function createElementNS(ns) { + const _this = this; + return function createElementNSCurried(...rest) { + namespace = ns; + const el = createElement.call(_this, ...rest); + namespace = void 0; + return el; }; } -var U = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: tt } = d; -function P(t, ...e) { - if (!e.length) - return t; - U.set(t, rt(t, this)); - for (let [n, r] of Object.entries(Object.assign({}, ...e))) - nt.call(this, t, n, r); - return U.delete(t), t; +var assign_context = /* @__PURE__ */ new WeakMap(); +var { setDeleteAttr: setDeleteAttr2 } = enviroment; +function assign(element, ...attributes) { + if (!attributes.length) return element; + assign_context.set(element, assignContext(element, this)); + for (const [key, value] of Object.entries(Object.assign({}, ...attributes))) + assignAttribute.call(this, element, key, value); + assign_context.delete(element); + return element; } -function nt(t, e, n) { - let { setRemoveAttr: r, s: o } = rt(t, this), c = this; - n = o.processReactiveAttribute( - t, - e, - n, - (a, h) => nt.call(c, t, a, h) +function assignAttribute(element, key, value) { + const { setRemoveAttr, s } = assignContext(element, this); + const _this = this; + value = s.processReactiveAttribute( + element, + key, + value, + (key2, value2) => assignAttribute.call(_this, element, key2, value2) ); - let [i] = e; - if (i === "=") - return r(e.slice(1), n); - if (i === ".") - return et(t, e.slice(1), n); - if (/(aria|data)([A-Z])/.test(e)) - return e = e.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(), r(e, n); - switch (e === "className" && (e = "class"), e) { - case "xlink:href": - return r(e, n, "http://www.w3.org/1999/xlink"); - case "textContent": - return tt(t, e, n); - case "style": - if (typeof n != "object") - break; - case "dataset": - return I(o, n, et.bind(null, t[e])); - case "ariaset": - return I(o, n, (a, h) => r("aria-" + a, h)); - case "classList": - return gt.call(c, t, n); + const [k] = key; + if ("=" === k) return setRemoveAttr(key.slice(1), value); + if ("." === k) return setDelete(element, key.slice(1), value); + if (/(aria|data)([A-Z])/.test(key)) { + key = key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(); + return setRemoveAttr(key, value); } - return Et(t, e) ? tt(t, e, n) : r(e, n); + if ("className" === key) key = "class"; + switch (key) { + case "xlink:href": + return setRemoveAttr(key, value, "http://www.w3.org/1999/xlink"); + case "textContent": + return setDeleteAttr2(element, key, value); + case "style": + if (typeof value !== "object") break; + /* falls through */ + case "dataset": + return forEachEntries(s, value, setDelete.bind(null, element[key])); + case "ariaset": + return forEachEntries(s, value, (key2, val) => setRemoveAttr("aria-" + key2, val)); + case "classList": + return classListDeclarative.call(_this, element, value); + } + return isPropSetter(element, key) ? setDeleteAttr2(element, key, value) : setRemoveAttr(key, value); } -function rt(t, e) { - if (U.has(t)) - return U.get(t); - let r = (t instanceof d.S ? xt : mt).bind(null, t, "Attribute"), o = W(e); - return { setRemoveAttr: r, s: o }; +function assignContext(element, _this) { + if (assign_context.has(element)) return assign_context.get(element); + const is_svg = element instanceof enviroment.S; + const setRemoveAttr = (is_svg ? setRemoveNS : setRemove).bind(null, element, "Attribute"); + const s = signals(_this); + return { setRemoveAttr, s }; } -function gt(t, e) { - let n = W(this); - return I( - n, - e, - (r, o) => t.classList.toggle(r, o === -1 ? void 0 : !!o) - ), t; +function classListDeclarative(element, toggle) { + const s = signals(this); + forEachEntries( + s, + toggle, + (class_name, val) => element.classList.toggle(class_name, val === -1 ? void 0 : Boolean(val)) + ); + return element; } -function Ft(t) { - return Array.from(t.children).forEach((e) => e.remove()), t; +function empty(el) { + Array.from(el.children).forEach((el2) => el2.remove()); + return el; } -function vt(t, e, n, r) { - return t instanceof d.H ? t[e + "Attribute"](n, r) : t[e + "AttributeNS"](null, n, r); +function elementAttribute(element, op, key, value) { + if (element instanceof enviroment.H) + return element[op + "Attribute"](key, value); + return element[op + "AttributeNS"](null, key, value); } -function Et(t, e) { - if (!(e in t)) - return !1; - let n = ot(t, e); - return !S(n.set); +function isPropSetter(el, key) { + if (!(key in el)) return false; + const des = getPropDescriptor(el, key); + return !isUndef(des.set); } -function ot(t, e) { - if (t = Object.getPrototypeOf(t), !t) - return {}; - let n = Object.getOwnPropertyDescriptor(t, e); - return n || ot(t, e); +function getPropDescriptor(p, key) { + p = Object.getPrototypeOf(p); + if (!p) return {}; + const des = Object.getOwnPropertyDescriptor(p, key); + if (!des) return getPropDescriptor(p, key); + return des; } -function I(t, e, n) { - if (!(typeof e != "object" || e === null)) - return Object.entries(e).forEach(function([o, c]) { - o && (c = t.processReactiveAttribute(e, o, c, n), n(o, c)); - }); +function forEachEntries(s, obj, cb) { + if (typeof obj !== "object" || obj === null) return; + return Object.entries(obj).forEach(function process([key, val]) { + if (!key) return; + val = s.processReactiveAttribute(obj, key, val, cb); + cb(key, val); + }); } -function ct(t) { - return Array.isArray(t) ? t.filter(Boolean).join(" ") : t; +function attrArrToStr(attr) { + return Array.isArray(attr) ? attr.filter(Boolean).join(" ") : attr; } -function mt(t, e, n, r) { - return t[(S(r) ? "remove" : "set") + e](n, ct(r)); +function setRemove(obj, prop, key, val) { + return obj[(isUndef(val) ? "remove" : "set") + prop](key, attrArrToStr(val)); } -function xt(t, e, n, r, o = null) { - return t[(S(r) ? "remove" : "set") + e + "NS"](o, n, ct(r)); +function setRemoveNS(obj, prop, key, val, ns = null) { + return obj[(isUndef(val) ? "remove" : "set") + prop + "NS"](ns, key, attrArrToStr(val)); } -function et(t, e, n) { - if (Reflect.set(t, e, n), !!S(n)) - return Reflect.deleteProperty(t, e); +function setDelete(obj, key, val) { + Reflect.set(obj, key, val); + if (!isUndef(val)) return; + return Reflect.deleteProperty(obj, key); } // src/events-observer.js -var D = d.M ? wt() : new Proxy({}, { +var c_ch_o = enviroment.M ? connectionsChangesObserverConstructor() : new Proxy({}, { get() { return () => { }; } }); -function wt() { - let t = /* @__PURE__ */ new Map(), e = !1, n = (s) => function(u) { - for (let f of u) - if (f.type === "childList") { - if (l(f.addedNodes, !0)) { - s(); - continue; - } - x(f.removedNodes, !0) && s(); +function connectionsChangesObserverConstructor() { + const store = /* @__PURE__ */ new Map(); + let is_observing = false; + const observerListener = (stop2) => function(mutations) { + for (const mutation of mutations) { + if (mutation.type !== "childList") continue; + if (observerAdded(mutation.addedNodes, true)) { + stop2(); + continue; } - }, r = new d.M(n(a)); - return { - observe(s) { - let u = new d.M(n(() => { - })); - return u.observe(s, { childList: !0, subtree: !0 }), () => u.disconnect(); - }, - onConnected(s, u) { - i(); - let f = c(s); - f.connected.has(u) || (f.connected.add(u), f.length_c += 1); - }, - offConnected(s, u) { - if (!t.has(s)) - return; - let f = t.get(s); - f.connected.has(u) && (f.connected.delete(u), f.length_c -= 1, o(s, f)); - }, - onDisconnected(s, u) { - i(); - let f = c(s); - f.disconnected.has(u) || (f.disconnected.add(u), f.length_d += 1); - }, - offDisconnected(s, u) { - if (!t.has(s)) - return; - let f = t.get(s); - f.disconnected.has(u) && (f.disconnected.delete(u), f.length_d -= 1, o(s, f)); + if (observerRemoved(mutation.removedNodes, true)) + stop2(); } }; - function o(s, u) { - u.length_c || u.length_d || (t.delete(s), a()); + const observer = new enviroment.M(observerListener(stop)); + return { + observe(element) { + const o = new enviroment.M(observerListener(() => { + })); + o.observe(element, { childList: true, subtree: true }); + return () => o.disconnect(); + }, + onConnected(element, listener) { + start(); + const listeners = getElementStore(element); + if (listeners.connected.has(listener)) return; + listeners.connected.add(listener); + listeners.length_c += 1; + }, + offConnected(element, listener) { + if (!store.has(element)) return; + const ls = store.get(element); + if (!ls.connected.has(listener)) return; + ls.connected.delete(listener); + ls.length_c -= 1; + cleanWhenOff(element, ls); + }, + onDisconnected(element, listener) { + start(); + const listeners = getElementStore(element); + if (listeners.disconnected.has(listener)) return; + listeners.disconnected.add(listener); + listeners.length_d += 1; + }, + offDisconnected(element, listener) { + if (!store.has(element)) return; + const ls = store.get(element); + if (!ls.disconnected.has(listener)) return; + ls.disconnected.delete(listener); + ls.length_d -= 1; + cleanWhenOff(element, ls); + } + }; + function cleanWhenOff(element, ls) { + if (ls.length_c || ls.length_d) + return; + store.delete(element); + stop(); } - function c(s) { - if (t.has(s)) - return t.get(s); - let u = { + function getElementStore(element) { + if (store.has(element)) return store.get(element); + const out = { connected: /* @__PURE__ */ new WeakSet(), length_c: 0, disconnected: /* @__PURE__ */ new WeakSet(), length_d: 0 }; - return t.set(s, u), u; + store.set(element, out); + return out; } - function i() { - e || (e = !0, r.observe(d.D.body, { childList: !0, subtree: !0 })); + function start() { + if (is_observing) return; + is_observing = true; + observer.observe(enviroment.D.body, { childList: true, subtree: true }); } - function a() { - !e || t.size || (e = !1, r.disconnect()); + function stop() { + if (!is_observing || store.size) return; + is_observing = false; + observer.disconnect(); } - function h() { - return new Promise(function(s) { - (requestIdleCallback || requestAnimationFrame)(s); + function requestIdle() { + return new Promise(function(resolve) { + (requestIdleCallback || requestAnimationFrame)(resolve); }); } - async function v(s) { - t.size > 30 && await h(); - let u = []; - if (!(s instanceof Node)) - return u; - for (let f of t.keys()) - f === s || !(f instanceof Node) || s.contains(f) && u.push(f); - return u; - } - function l(s, u) { - let f = !1; - for (let b of s) { - if (u && v(b).then(l), !t.has(b)) - continue; - let N = t.get(b); - N.length_c && (b.dispatchEvent(new Event(_)), N.connected = /* @__PURE__ */ new WeakSet(), N.length_c = 0, N.length_d || t.delete(b), f = !0); + async function collectChildren(element) { + if (store.size > 30) + await requestIdle(); + const out = []; + if (!(element instanceof Node)) return out; + for (const el of store.keys()) { + if (el === element || !(el instanceof Node)) continue; + if (element.contains(el)) + out.push(el); } - return f; + return out; } - function x(s, u) { - let f = !1; - for (let b of s) - u && v(b).then(x), !(!t.has(b) || !t.get(b).length_d) && ((globalThis.queueMicrotask || setTimeout)(y(b)), f = !0); - return f; + function observerAdded(addedNodes, is_root) { + let out = false; + for (const element of addedNodes) { + if (is_root) collectChildren(element).then(observerAdded); + if (!store.has(element)) continue; + const ls = store.get(element); + if (!ls.length_c) continue; + element.dispatchEvent(new Event(evc)); + ls.connected = /* @__PURE__ */ new WeakSet(); + ls.length_c = 0; + if (!ls.length_d) store.delete(element); + out = true; + } + return out; } - function y(s) { + function observerRemoved(removedNodes, is_root) { + let out = false; + for (const element of removedNodes) { + if (is_root) collectChildren(element).then(observerRemoved); + if (!store.has(element)) continue; + const ls = store.get(element); + if (!ls.length_d) continue; + (globalThis.queueMicrotask || setTimeout)(dispatchRemove(element)); + out = true; + } + return out; + } + function dispatchRemove(element) { return () => { - s.isConnected || (s.dispatchEvent(new Event(O)), t.delete(s)); + if (element.isConnected) return; + element.dispatchEvent(new Event(evd)); + store.delete(element); }; } } // src/customElement.js -function Zt(t, e, n, r = _t) { - m.push({ - scope: t, - host: (...i) => i.length ? i.forEach((a) => a(t)) : t - }), typeof r == "function" && (r = r.call(t, t)); - let o = t[C]; - o || yt(t); - let c = n.call(t, r); - return o || t.dispatchEvent(new Event(_)), e.nodeType === 11 && typeof e.mode == "string" && t.addEventListener(O, D.observe(e), { once: !0 }), m.pop(), e.append(c); +function customElementRender(custom_element, target, render, props = observedAttributes2) { + scope.push({ + scope: custom_element, + host: (...c) => c.length ? c.forEach((c2) => c2(custom_element)) : custom_element + }); + if (typeof props === "function") props = props.call(custom_element, custom_element); + const is_lte = custom_element[keyLTE]; + if (!is_lte) lifecyclesToEvents(custom_element); + const out = render.call(custom_element, props); + if (!is_lte) custom_element.dispatchEvent(new Event(evc)); + if (target.nodeType === 11 && typeof target.mode === "string") + custom_element.addEventListener(evd, c_ch_o.observe(target), { once: true }); + scope.pop(); + return target.append(out); } -function yt(t) { - return J(t.prototype, "connectedCallback", function(e, n, r) { - e.apply(n, r), n.dispatchEvent(new Event(_)); - }), J(t.prototype, "disconnectedCallback", function(e, n, r) { - e.apply(n, r), (globalThis.queueMicrotask || setTimeout)( - () => !n.isConnected && n.dispatchEvent(new Event(O)) +function lifecyclesToEvents(class_declaration) { + wrapMethod(class_declaration.prototype, "connectedCallback", function(target, thisArg, detail) { + target.apply(thisArg, detail); + thisArg.dispatchEvent(new Event(evc)); + }); + wrapMethod(class_declaration.prototype, "disconnectedCallback", function(target, thisArg, detail) { + target.apply(thisArg, detail); + (globalThis.queueMicrotask || setTimeout)( + () => !thisArg.isConnected && thisArg.dispatchEvent(new Event(evd)) ); - }), J(t.prototype, "attributeChangedCallback", function(e, n, r) { - let [o, , c] = r; - n.dispatchEvent(new CustomEvent(M, { - detail: [o, c] - })), e.apply(n, r); - }), t.prototype[C] = !0, t; + }); + wrapMethod(class_declaration.prototype, "attributeChangedCallback", function(target, thisArg, detail) { + const [attribute, , value] = detail; + thisArg.dispatchEvent(new CustomEvent(eva, { + detail: [attribute, value] + })); + target.apply(thisArg, detail); + }); + class_declaration.prototype[keyLTE] = true; + return class_declaration; } -function J(t, e, n) { - t[e] = new Proxy(t[e] || (() => { - }), { apply: n }); +function wrapMethod(obj, method, apply) { + obj[method] = new Proxy(obj[method] || (() => { + }), { apply }); } -function _t(t) { - return F(t, (e, n) => e.getAttribute(n)); +function observedAttributes2(instance) { + return observedAttributes(instance, (i, n) => i.getAttribute(n)); } // src/events.js -function Qt(t, e, n) { - return e || (e = {}), function(o, ...c) { - n && (c.unshift(o), o = typeof n == "function" ? n() : n); - let i = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e); - return o.dispatchEvent(i); +function dispatchEvent(name, options, host) { + if (!options) options = {}; + return function dispatch(element, ...d) { + if (host) { + d.unshift(element); + element = typeof host === "function" ? host() : host; + } + const event = d.length ? new CustomEvent(name, Object.assign({ detail: d[0] }, options)) : new Event(name, options); + return element.dispatchEvent(event); }; } -function w(t, e, n) { - return function(o) { - return o.addEventListener(t, e, n), o; +function on(event, listener, options) { + return function registerElement(element) { + element.addEventListener(event, listener, options); + return element; }; } -var it = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 }); -w.connected = function(t, e) { - return e = it(e), function(r) { - return r.addEventListener(_, t, e), r[C] ? r : r.isConnected ? (r.dispatchEvent(new Event(_)), r) : (q(e.signal, () => D.offConnected(r, t)) && D.onConnected(r, t), r); +var lifeOptions = (obj) => Object.assign({}, typeof obj === "object" ? obj : null, { once: true }); +on.connected = function(listener, options) { + options = lifeOptions(options); + return function registerElement(element) { + element.addEventListener(evc, listener, options); + if (element[keyLTE]) return element; + if (element.isConnected) return element.dispatchEvent(new Event(evc)), element; + const c = onAbort(options.signal, () => c_ch_o.offConnected(element, listener)); + if (c) c_ch_o.onConnected(element, listener); + return element; }; }; -w.disconnected = function(t, e) { - return e = it(e), function(r) { - return r.addEventListener(O, t, e), r[C] || q(e.signal, () => D.offDisconnected(r, t)) && D.onDisconnected(r, t), r; +on.disconnected = function(listener, options) { + options = lifeOptions(options); + return function registerElement(element) { + element.addEventListener(evd, listener, options); + if (element[keyLTE]) return element; + const c = onAbort(options.signal, () => c_ch_o.offDisconnected(element, listener)); + if (c) c_ch_o.onDisconnected(element, listener); + return element; }; }; -var Z = /* @__PURE__ */ new WeakMap(); -w.disconnectedAsAbort = function(t) { - if (Z.has(t)) - return Z.get(t); - let e = new AbortController(); - return Z.set(t, e), t(w.disconnected(() => e.abort())), e; +var store_abort = /* @__PURE__ */ new WeakMap(); +on.disconnectedAsAbort = function(host) { + if (store_abort.has(host)) return store_abort.get(host); + const a = new AbortController(); + store_abort.set(host, a); + host(on.disconnected(() => a.abort())); + return a; }; -var At = /* @__PURE__ */ new WeakSet(); -w.attributeChanged = function(t, e) { - return typeof e != "object" && (e = {}), function(r) { - if (r.addEventListener(M, t, e), r[C] || At.has(r) || !d.M) - return r; - let o = new d.M(function(i) { - for (let { attributeName: a, target: h } of i) - h.dispatchEvent( - new CustomEvent(M, { detail: [a, h.getAttribute(a)] }) +var els_attribute_store = /* @__PURE__ */ new WeakSet(); +on.attributeChanged = function(listener, options) { + if (typeof options !== "object") + options = {}; + return function registerElement(element) { + element.addEventListener(eva, listener, options); + if (element[keyLTE] || els_attribute_store.has(element)) + return element; + if (!enviroment.M) return element; + const observer = new enviroment.M(function(mutations) { + for (const { attributeName, target } of mutations) + target.dispatchEvent( + new CustomEvent(eva, { detail: [attributeName, target.getAttribute(attributeName)] }) ); }); - return q(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r; + const c = onAbort(options.signal, () => observer.disconnect()); + if (c) observer.observe(element, { attributes: true }); + return element; }; }; // src/signals-lib.js -var p = "__dde_signal"; -function z(t) { +var mark = "__dde_signal"; +function isSignal(candidate) { try { - return T(t, p); - } catch { - return !1; + return hasOwn(candidate, mark); + } catch (e) { + return false; } } -var H = [], g = /* @__PURE__ */ new WeakMap(); -function E(t, e) { - if (typeof t != "function") - return st(!1, t, e); - if (z(t)) - return t; - let n = st(!0), r = function() { - let [o, ...c] = g.get(r); - if (g.set(r, /* @__PURE__ */ new Set([o])), H.push(r), dt(n, t()), H.pop(), !c.length) - return; - let i = g.get(r); - for (let a of c) - i.has(a) || L(a, r); +var stack_watch = []; +var deps = /* @__PURE__ */ new WeakMap(); +function signal(value, actions) { + if (typeof value !== "function") + return create(false, value, actions); + if (isSignal(value)) return value; + const out = create(true); + const contextReWatch = function() { + const [origin, ...deps_old] = deps.get(contextReWatch); + deps.set(contextReWatch, /* @__PURE__ */ new Set([origin])); + stack_watch.push(contextReWatch); + write(out, value()); + stack_watch.pop(); + if (!deps_old.length) return; + const deps_curr = deps.get(contextReWatch); + for (const dep_signal of deps_old) { + if (deps_curr.has(dep_signal)) continue; + removeSignalListener(dep_signal, contextReWatch); + } }; - return g.set(n[p], r), g.set(r, /* @__PURE__ */ new Set([n])), r(), n; + deps.set(out[mark], contextReWatch); + deps.set(contextReWatch, /* @__PURE__ */ new Set([out])); + contextReWatch(); + return out; } -E.action = function(t, e, ...n) { - let r = t[p], { actions: o } = r; - if (!o || !(e in o)) - throw new Error(`'${t}' has no action with name '${e}'!`); - if (o[e].apply(r, n), r.skip) - return delete r.skip; - r.listeners.forEach((c) => c(r.value)); +signal.action = function(s, name, ...a) { + const M = s[mark], { actions } = M; + if (!actions || !(name in actions)) + throw new Error(`'${s}' has no action with name '${name}'!`); + actions[name].apply(M, a); + if (M.skip) return delete M.skip; + M.listeners.forEach((l) => l(M.value)); }; -E.on = function t(e, n, r = {}) { - let { signal: o } = r; - if (!(o && o.aborted)) { - if (Array.isArray(e)) - return e.forEach((c) => t(c, n, r)); - Q(e, n), o && o.addEventListener("abort", () => L(e, n)); - } +signal.on = function on2(s, listener, options = {}) { + const { signal: as } = options; + if (as && as.aborted) return; + if (Array.isArray(s)) return s.forEach((s2) => on2(s2, listener, options)); + addSignalListener(s, listener); + if (as) as.addEventListener("abort", () => removeSignalListener(s, listener)); }; -E.symbols = { +signal.symbols = { //signal: mark, onclear: Symbol.for("Signal.onclear") }; -E.clear = function(...t) { - for (let n of t) { - let r = n[p]; - r && (delete n.toJSON, r.onclear.forEach((o) => o.call(r)), e(n, r), delete n[p]); +signal.clear = function(...signals2) { + for (const s of signals2) { + const M = s[mark]; + if (!M) continue; + delete s.toJSON; + M.onclear.forEach((f) => f.call(M)); + clearListDeps(s, M); + delete s[mark]; } - function e(n, r) { - r.listeners.forEach((o) => { - if (r.listeners.delete(o), !g.has(o)) - return; - let c = g.get(o); - c.delete(n), !(c.size > 1) && (n.clear(...c), g.delete(o)); + function clearListDeps(s, o) { + o.listeners.forEach((l) => { + o.listeners.delete(l); + if (!deps.has(l)) return; + const ls = deps.get(l); + ls.delete(s); + if (ls.size > 1) return; + s.clear(...ls); + deps.delete(l); }); } }; -var R = "__dde_reactive"; -E.el = function(t, e) { - let n = j.mark({ type: "reactive" }, !0), r = n.end, o = d.D.createDocumentFragment(); - o.append(n, r); - let { current: c } = m, i = {}, a = (h) => { - if (!n.parentNode || !r.parentNode) - return L(t, a); - let v = i; - i = {}, m.push(c); - let l = e(h, function(u, f) { - let b; - return T(v, u) ? (b = v[u], delete v[u]) : b = f(), i[u] = b, b; +var key_reactive = "__dde_reactive"; +signal.el = function(s, map) { + const mark_start = createElement.mark({ type: "reactive" }, true); + const mark_end = mark_start.end; + const out = enviroment.D.createDocumentFragment(); + out.append(mark_start, mark_end); + const { current } = scope; + let cache = {}; + const reRenderReactiveElement = (v) => { + if (!mark_start.parentNode || !mark_end.parentNode) + return removeSignalListener(s, reRenderReactiveElement); + const cache_tmp = cache; + cache = {}; + scope.push(current); + let els = map(v, function useCache(key, fun) { + let value; + if (hasOwn(cache_tmp, key)) { + value = cache_tmp[key]; + delete cache_tmp[key]; + } else + value = fun(); + cache[key] = value; + return value; }); - m.pop(), Array.isArray(l) || (l = [l]); - let x = document.createComment(""); - l.push(x), n.after(...l); - let y; - for (; (y = x.nextSibling) && y !== r; ) - y.remove(); - x.remove(), n.isConnected && St(c.host()); + scope.pop(); + if (!Array.isArray(els)) + els = [els]; + const el_start_rm = document.createComment(""); + els.push(el_start_rm); + mark_start.after(...els); + let el_r; + while ((el_r = el_start_rm.nextSibling) && el_r !== mark_end) + el_r.remove(); + el_start_rm.remove(); + if (mark_start.isConnected) + requestCleanUpReactives(current.host()); }; - return Q(t, a), ft(t, a, n, e), a(t()), o; + addSignalListener(s, reRenderReactiveElement); + removeSignalsFromElements(s, reRenderReactiveElement, mark_start, map); + reRenderReactiveElement(s()); + return out; }; -function St(t) { - !t || !t[R] || (requestIdleCallback || setTimeout)(function() { - t[R] = t[R].filter(([e, n]) => n.isConnected ? !0 : (L(...e), !1)); +function requestCleanUpReactives(host) { + if (!host || !host[key_reactive]) return; + (requestIdleCallback || setTimeout)(function() { + host[key_reactive] = host[key_reactive].filter(([s, el]) => el.isConnected ? true : (removeSignalListener(...s), false)); }); } -var Ct = { - _set(t) { - this.value = t; +var observedAttributeActions = { + _set(value) { + this.value = value; } }; -function Ot(t) { - return function(e, n) { - let r = (...c) => c.length ? e.setAttribute(n, ...c) : K(r), o = at(r, e.getAttribute(n), Ct); - return t[n] = o, o; +function observedAttribute(store) { + return function(instance, name) { + const varS = (...args) => !args.length ? read(varS) : instance.setAttribute(name, ...args); + const out = toSignal(varS, instance.getAttribute(name), observedAttributeActions); + store[name] = out; + return out; }; } -var G = "__dde_attributes"; -E.observedAttributes = function(t) { - let e = t[G] = {}, n = F(t, Ot(e)); - return w.attributeChanged(function({ detail: o }) { +var key_attributes = "__dde_attributes"; +signal.observedAttributes = function(element) { + const store = element[key_attributes] = {}; + const attrs = observedAttributes(element, observedAttribute(store)); + on.attributeChanged(function attributeChangeToSignal({ detail }) { /*! This maps attributes to signals (`S.observedAttributes`). * Investigate `__dde_attributes` key of the element.*/ - let [c, i] = o, a = this[G][c]; - if (a) - return E.action(a, "_set", i); - })(t), w.disconnected(function() { + const [name, value] = detail; + const curr = this[key_attributes][name]; + if (curr) return signal.action(curr, "_set", value); + })(element); + on.disconnected(function() { /*! This removes all signals mapped to attributes (`S.observedAttributes`). * Investigate `__dde_attributes` key of the element.*/ - E.clear(...Object.values(this[G])); - })(t), n; + signal.clear(...Object.values(this[key_attributes])); + })(element); + return attrs; }; -var ut = { - isSignal: z, - processReactiveAttribute(t, e, n, r) { - if (!z(n)) - return n; - let o = (c) => { - if (!t.isConnected) - return L(n, o); - r(e, c); +var signals_config = { + isSignal, + processReactiveAttribute(element, key, attrs, set) { + if (!isSignal(attrs)) return attrs; + const l = (attr) => { + if (!element.isConnected) + return removeSignalListener(attrs, l); + set(key, attr); }; - return Q(n, o), ft(n, o, t, e), n(); + addSignalListener(attrs, l); + removeSignalsFromElements(attrs, l, element, key); + return attrs(); } }; -function ft(t, e, ...n) { - let { current: r } = m; - r.prevent || r.host(function(o) { - o[R] || (o[R] = [], w.disconnected( - () => ( - /*! - * Clears all Signals listeners added in the current scope/host (`S.el`, `assign`, …?). - * You can investigate the `__dde_reactive` key of the element. - * */ - o[R].forEach(([[c, i]]) => L(c, i, c[p] && c[p].host && c[p].host() === o)) - ) - )(o)), o[R].push([[t, e], ...n]); +function removeSignalsFromElements(s, listener, ...notes) { + const { current } = scope; + if (current.prevent) return; + current.host(function(element) { + if (!element[key_reactive]) { + element[key_reactive] = []; + on.disconnected( + () => ( + /*! + * Clears all Signals listeners added in the current scope/host (`S.el`, `assign`, …?). + * You can investigate the `__dde_reactive` key of the element. + * */ + element[key_reactive].forEach(([[s2, listener2]]) => removeSignalListener(s2, listener2, s2[mark] && s2[mark].host && s2[mark].host() === element)) + ) + )(element); + } + element[key_reactive].push([[s, listener], ...notes]); }); } -function st(t, e, n) { - let r = t ? () => K(r) : (...o) => o.length ? dt(r, ...o) : K(r); - return at(r, e, n, t); +function create(is_readonly, value, actions) { + const varS = is_readonly ? () => read(varS) : (...value2) => value2.length ? write(varS, ...value2) : read(varS); + return toSignal(varS, value, actions, is_readonly); } -var Dt = Object.assign(/* @__PURE__ */ Object.create(null), { +var protoSigal = Object.assign(/* @__PURE__ */ Object.create(null), { stopPropagation() { - this.skip = !0; + this.skip = true; } -}), V = class extends Error { +}); +var SignalDefined = class extends Error { constructor() { super(); - let [e, ...n] = this.stack.split(` -`), r = e.slice(e.indexOf("@"), e.indexOf(".js:") + 4); - this.stack = n.find((o) => !o.includes(r)); + const [curr, ...rest] = this.stack.split("\n"); + const curr_file = curr.slice(curr.indexOf("@"), curr.indexOf(".js:") + 4); + this.stack = rest.find((l) => !l.includes(curr_file)); } }; -function at(t, e, n, r = !1) { - let o = []; - X(n) !== "[object Object]" && (n = {}); - let { onclear: c } = E.symbols; - n[c] && (o.push(n[c]), delete n[c]); - let { host: i } = m; - return Reflect.defineProperty(t, p, { - value: { - value: e, - actions: n, - onclear: o, - host: i, - listeners: /* @__PURE__ */ new Set(), - defined: new V().stack, - readonly: r - }, - enumerable: !1, - writable: !1, - configurable: !0 - }), t.toJSON = () => t(), t.valueOf = () => t[p] && t[p].value, Object.setPrototypeOf(t[p], Dt), t; -} -function Rt() { - return H[H.length - 1]; -} -function K(t) { - if (!t[p]) - return; - let { value: e, listeners: n } = t[p], r = Rt(); - return r && n.add(r), g.has(r) && g.get(r).add(t), e; -} -function dt(t, e, n) { - if (!t[p]) - return; - let r = t[p]; - if (!(!n && r.value === e)) - return r.value = e, r.listeners.forEach((o) => o(e)), e; -} -function Q(t, e) { - if (t[p]) - return t[p].listeners.add(e); -} -function L(t, e, n) { - let r = t[p]; - if (!r) - return; - let o = r.listeners.delete(e); - if (n && !r.listeners.size) { - if (E.clear(t), !g.has(r)) - return o; - let c = g.get(r); - if (!g.has(c)) - return o; - g.get(c).forEach((i) => L(i, c, !0)); +function toSignal(s, value, actions, readonly = false) { + const onclear = []; + if (typeOf(actions) !== "[object Object]") + actions = {}; + const { onclear: ocs } = signal.symbols; + if (actions[ocs]) { + onclear.push(actions[ocs]); + delete actions[ocs]; } - return o; + const { host } = scope; + Reflect.defineProperty(s, mark, { + value: { + value, + actions, + onclear, + host, + listeners: /* @__PURE__ */ new Set(), + defined: new SignalDefined().stack, + readonly + }, + enumerable: false, + writable: false, + configurable: true + }); + s.toJSON = () => s(); + s.valueOf = () => s[mark] && s[mark].value; + Object.setPrototypeOf(s[mark], protoSigal); + return s; +} +function currentContext() { + return stack_watch[stack_watch.length - 1]; +} +function read(s) { + if (!s[mark]) return; + const { value, listeners } = s[mark]; + const context = currentContext(); + if (context) listeners.add(context); + if (deps.has(context)) deps.get(context).add(s); + return value; +} +function write(s, value, force) { + if (!s[mark]) return; + const M = s[mark]; + if (!force && M.value === value) return; + M.value = value; + M.listeners.forEach((l) => l(value)); + return value; +} +function addSignalListener(s, listener) { + if (!s[mark]) return; + return s[mark].listeners.add(listener); +} +function removeSignalListener(s, listener, clear_when_empty) { + const M = s[mark]; + if (!M) return; + const out = M.listeners.delete(listener); + if (clear_when_empty && !M.listeners.size) { + signal.clear(s); + if (!deps.has(M)) return out; + const c = deps.get(M); + if (!deps.has(c)) return out; + deps.get(c).forEach((sig) => removeSignalListener(sig, c, true)); + } + return out; } // signals.js -B(ut); +registerReactivity(signals_config); globalThis.dde= { - S: E, - assign: P, - assignAttribute: nt, - chainableAppend: ht, - classListDeclarative: gt, - createElement: j, - createElementNS: qt, - customElementRender: Zt, - customElementWithDDE: yt, - dispatchEvent: Qt, - el: j, - elNS: qt, - elementAttribute: vt, - empty: Ft, - isSignal: z, - lifecyclesToEvents: yt, - observedAttributes: _t, - on: w, - registerReactivity: B, - scope: m, - signal: E, - simulateSlots: Wt + S: signal, + assign, + assignAttribute, + chainableAppend, + classListDeclarative, + createElement, + createElementNS, + customElementRender, + customElementWithDDE: lifecyclesToEvents, + dispatchEvent, + el: createElement, + elNS: createElementNS, + elementAttribute, + empty, + isSignal, + lifecyclesToEvents, + observedAttributes: observedAttributes2, + on, + registerReactivity, + scope, + signal, + simulateSlots }; })(); \ No newline at end of file diff --git a/dist/dde.js b/dist/dde.js index b9ebc55..ac1273d 100644 --- a/dist/dde.js +++ b/dist/dde.js @@ -1,47 +1,52 @@ //deka-dom-el library is available via global namespace `dde` (()=> { // src/signals-common.js -var C = { - isSignal(t) { - return !1; +var signals_global = { + isSignal(attributes) { + return false; }, - processReactiveAttribute(t, e, n, r) { - return n; + processReactiveAttribute(obj, key, attr, set) { + return attr; } }; -function V(t, e = !0) { - return e ? Object.assign(C, t) : (Object.setPrototypeOf(t, C), t); +function registerReactivity(def, global = true) { + if (global) return Object.assign(signals_global, def); + Object.setPrototypeOf(def, signals_global); + return def; } -function L(t) { - return C.isPrototypeOf(t) && t !== C ? t : C; +function signals(_this) { + return signals_global.isPrototypeOf(_this) && _this !== signals_global ? _this : signals_global; } // src/helpers.js -var q = (...t) => Object.prototype.hasOwnProperty.call(...t); -function E(t) { - return typeof t > "u"; +var hasOwn = (...a) => Object.prototype.hasOwnProperty.call(...a); +function isUndef(value) { + return typeof value === "undefined"; } -function N(t, e) { - if (!t || !(t instanceof AbortSignal)) - return !0; - if (!t.aborted) - return t.addEventListener("abort", e), function() { - t.removeEventListener("abort", e); - }; +function onAbort(signal, listener) { + if (!signal || !(signal instanceof AbortSignal)) + return true; + if (signal.aborted) + return; + signal.addEventListener("abort", listener); + return function cleanUp() { + signal.removeEventListener("abort", listener); + }; } -function F(t, e) { - let { observedAttributes: n = [] } = t.constructor; - return n.reduce(function(r, o) { - return r[J(o)] = e(t, o), r; +function observedAttributes(instance, observedAttribute) { + const { observedAttributes: observedAttributes3 = [] } = instance.constructor; + return observedAttributes3.reduce(function(out, name) { + out[kebabToCamel(name)] = observedAttribute(instance, name); + return out; }, {}); } -function J(t) { - return t.replace(/-./g, (e) => e[1].toUpperCase()); +function kebabToCamel(name) { + return name.replace(/-./g, (x) => x[1].toUpperCase()); } // src/dom-common.js -var a = { - setDeleteAttr: K, +var enviroment = { + setDeleteAttr, ssr: "", D: globalThis.document, F: globalThis.DocumentFragment, @@ -49,425 +54,548 @@ var a = { S: globalThis.SVGElement, M: globalThis.MutationObserver }; -function K(t, e, n) { - if (Reflect.set(t, e, n), !!E(n)) { - if (Reflect.deleteProperty(t, e), t instanceof a.H && t.getAttribute(e) === "undefined") - return t.removeAttribute(e); - if (Reflect.get(t, e) === "undefined") - return Reflect.set(t, e, ""); - } +function setDeleteAttr(obj, prop, val) { + Reflect.set(obj, prop, val); + if (!isUndef(val)) return; + Reflect.deleteProperty(obj, prop); + if (obj instanceof enviroment.H && obj.getAttribute(prop) === "undefined") + return obj.removeAttribute(prop); + if (Reflect.get(obj, prop) === "undefined") + return Reflect.set(obj, prop, ""); } -var x = "__dde_lifecyclesToEvents", g = "dde:connected", y = "dde:disconnected", D = "dde:attributeChanged"; +var keyLTE = "__dde_lifecyclesToEvents"; +var evc = "dde:connected"; +var evd = "dde:disconnected"; +var eva = "dde:attributeChanged"; // src/dom.js -var v = [{ +var scopes = [{ get scope() { - return a.D.body; + return enviroment.D.body; }, - host: (t) => t ? t(a.D.body) : a.D.body, - prevent: !0 -}], S = { + host: (c) => c ? c(enviroment.D.body) : enviroment.D.body, + prevent: true +}]; +var scope = { get current() { - return v[v.length - 1]; + return scopes[scopes.length - 1]; }, get host() { return this.current.host; }, preventDefault() { - let { current: t } = this; - return t.prevent = !0, t; + const { current } = this; + current.prevent = true; + return current; }, get state() { - return [...v]; + return [...scopes]; }, - push(t = {}) { - return v.push(Object.assign({}, this.current, { prevent: !1 }, t)); + push(s = {}) { + return scopes.push(Object.assign({}, this.current, { prevent: false }, s)); }, pushRoot() { - return v.push(v[0]); + return scopes.push(scopes[0]); }, pop() { - if (v.length !== 1) - return v.pop(); + if (scopes.length === 1) return; + return scopes.pop(); } }; -function $(...t) { - return this.appendOriginal(...t), this; +function append(...els) { + this.appendOriginal(...els); + return this; } -function Q(t) { - return t.append === $ || (t.appendOriginal = t.append, t.append = $), t; +function chainableAppend(el) { + if (el.append === append) return el; + el.appendOriginal = el.append; + el.append = append; + return el; } -var T; -function k(t, e, ...n) { - let r = L(this), o = 0, c, f; - switch ((Object(e) !== e || r.isSignal(e)) && (e = { textContent: e }), !0) { - case typeof t == "function": { - o = 1, S.push({ scope: t, host: (...b) => b.length ? (o === 1 ? n.unshift(...b) : b.forEach((l) => l(f)), void 0) : f }), c = t(e || void 0); - let d = c instanceof a.F; - if (c.nodeName === "#comment") - break; - let p = k.mark({ +var namespace; +function createElement(tag, attributes, ...addons) { + const s = signals(this); + let scoped = 0; + let el, el_host; + if (Object(attributes) !== attributes || s.isSignal(attributes)) + attributes = { textContent: attributes }; + switch (true) { + case typeof tag === "function": { + scoped = 1; + scope.push({ scope: tag, host: (...c) => c.length ? (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0) : el_host }); + el = tag(attributes || void 0); + const is_fragment = el instanceof enviroment.F; + if (el.nodeName === "#comment") break; + const el_mark = createElement.mark({ type: "component", - name: t.name, - host: d ? "this" : "parentElement" + name: tag.name, + host: is_fragment ? "this" : "parentElement" }); - c.prepend(p), d && (f = p); + el.prepend(el_mark); + if (is_fragment) el_host = el_mark; break; } - case t === "#text": - c = O.call(this, a.D.createTextNode(""), e); + case tag === "#text": + el = assign.call(this, enviroment.D.createTextNode(""), attributes); break; - case (t === "<>" || !t): - c = O.call(this, a.D.createDocumentFragment(), e); + case (tag === "<>" || !tag): + el = assign.call(this, enviroment.D.createDocumentFragment(), attributes); break; - case !!T: - c = O.call(this, a.D.createElementNS(T, t), e); + case Boolean(namespace): + el = assign.call(this, enviroment.D.createElementNS(namespace, tag), attributes); break; - case !c: - c = O.call(this, a.D.createElement(t), e); + case !el: + el = assign.call(this, enviroment.D.createElement(tag), attributes); } - return Q(c), f || (f = c), n.forEach((d) => d(f)), o && S.pop(), o = 2, c; + chainableAppend(el); + if (!el_host) el_host = el; + addons.forEach((c) => c(el_host)); + if (scoped) scope.pop(); + scoped = 2; + return el; } -function bt(t, e = t, n = void 0) { - let r = Symbol.for("default"), o = Array.from(e.querySelectorAll("slot")).reduce((f, d) => Reflect.set(f, d.name || r, d) && f, {}), c = q(o, r); - if (t.append = new Proxy(t.append, { - apply(f, d, p) { - if (!p.length) - return t; - let b = a.D.createDocumentFragment(); - for (let l of p) { - if (!l || !l.slot) { - c && b.appendChild(l); +function simulateSlots(element, root, mapper) { + if (typeof root !== "object") { + mapper = root; + root = element; + } + const _default = Symbol.for("default"); + const slots = Array.from(root.querySelectorAll("slot")).reduce((out, curr) => Reflect.set(out, curr.name || _default, curr) && out, {}); + const has_d = hasOwn(slots, _default); + element.append = new Proxy(element.append, { + apply(orig, _, els) { + if (els[0] === root) return orig.apply(element, els); + if (!els.length) return element; + const d = enviroment.D.createDocumentFragment(); + for (const el of els) { + if (!el || !el.slot) { + if (has_d) d.append(el); continue; } - let A = l.slot, _ = o[A]; - tt(l, "remove", "slot"), _ && (X(_, l, n), Reflect.deleteProperty(o, A)); + const name = el.slot; + const slot = slots[name]; + elementAttribute(el, "remove", "slot"); + if (!slot) continue; + simulateSlotReplace(slot, el, mapper); + Reflect.deleteProperty(slots, name); } - return c && (o[r].replaceWith(b), Reflect.deleteProperty(o, r)), t.append = f, t; + if (has_d) { + slots[_default].replaceWith(d); + Reflect.deleteProperty(slots, _default); + } + element.append = orig; + return element; } - }), t !== e) { - let f = Array.from(t.childNodes); - f.forEach((d) => d.remove()), t.append(...f); + }); + if (element !== root) { + const els = Array.from(element.childNodes); + els.forEach((el) => el.remove()); + element.append(...els); } - return e; + return root; } -function X(t, e, n) { - n && n(t, e); +function simulateSlotReplace(slot, element, mapper) { + if (mapper) mapper(slot, element); try { - t.replaceWith(O(e, { className: [e.className, t.className], dataset: { ...t.dataset } })); - } catch { - t.replaceWith(e); + slot.replaceWith(assign(element, { className: [element.className, slot.className], dataset: { ...slot.dataset } })); + } catch (_) { + slot.replaceWith(element); } } -k.mark = function(t, e = !1) { - t = Object.entries(t).map(([o, c]) => o + `="${c}"`).join(" "); - let n = e ? "" : "/", r = a.D.createComment(``); - return e && (r.end = a.D.createComment("")), r; +createElement.mark = function(attrs, is_open = false) { + attrs = Object.entries(attrs).map(([n, v]) => n + `="${v}"`).join(" "); + const end = is_open ? "" : "/"; + const out = enviroment.D.createComment(``); + if (is_open) out.end = enviroment.D.createComment(""); + return out; }; -function gt(t) { - let e = this; - return function(...r) { - T = t; - let o = k.call(e, ...r); - return T = void 0, o; +function createElementNS(ns) { + const _this = this; + return function createElementNSCurried(...rest) { + namespace = ns; + const el = createElement.call(_this, ...rest); + namespace = void 0; + return el; }; } -var P = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: U } = a; -function O(t, ...e) { - if (!e.length) - return t; - P.set(t, B(t, this)); - for (let [n, r] of Object.entries(Object.assign({}, ...e))) - z.call(this, t, n, r); - return P.delete(t), t; +var assign_context = /* @__PURE__ */ new WeakMap(); +var { setDeleteAttr: setDeleteAttr2 } = enviroment; +function assign(element, ...attributes) { + if (!attributes.length) return element; + assign_context.set(element, assignContext(element, this)); + for (const [key, value] of Object.entries(Object.assign({}, ...attributes))) + assignAttribute.call(this, element, key, value); + assign_context.delete(element); + return element; } -function z(t, e, n) { - let { setRemoveAttr: r, s: o } = B(t, this), c = this; - n = o.processReactiveAttribute( - t, - e, - n, - (d, p) => z.call(c, t, d, p) +function assignAttribute(element, key, value) { + const { setRemoveAttr, s } = assignContext(element, this); + const _this = this; + value = s.processReactiveAttribute( + element, + key, + value, + (key2, value2) => assignAttribute.call(_this, element, key2, value2) ); - let [f] = e; - if (f === "=") - return r(e.slice(1), n); - if (f === ".") - return H(t, e.slice(1), n); - if (/(aria|data)([A-Z])/.test(e)) - return e = e.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(), r(e, n); - switch (e === "className" && (e = "class"), e) { - case "xlink:href": - return r(e, n, "http://www.w3.org/1999/xlink"); - case "textContent": - return U(t, e, n); - case "style": - if (typeof n != "object") - break; - case "dataset": - return M(o, n, H.bind(null, t[e])); - case "ariaset": - return M(o, n, (d, p) => r("aria-" + d, p)); - case "classList": - return Y.call(c, t, n); + const [k] = key; + if ("=" === k) return setRemoveAttr(key.slice(1), value); + if ("." === k) return setDelete(element, key.slice(1), value); + if (/(aria|data)([A-Z])/.test(key)) { + key = key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(); + return setRemoveAttr(key, value); } - return et(t, e) ? U(t, e, n) : r(e, n); + if ("className" === key) key = "class"; + switch (key) { + case "xlink:href": + return setRemoveAttr(key, value, "http://www.w3.org/1999/xlink"); + case "textContent": + return setDeleteAttr2(element, key, value); + case "style": + if (typeof value !== "object") break; + /* falls through */ + case "dataset": + return forEachEntries(s, value, setDelete.bind(null, element[key])); + case "ariaset": + return forEachEntries(s, value, (key2, val) => setRemoveAttr("aria-" + key2, val)); + case "classList": + return classListDeclarative.call(_this, element, value); + } + return isPropSetter(element, key) ? setDeleteAttr2(element, key, value) : setRemoveAttr(key, value); } -function B(t, e) { - if (P.has(t)) - return P.get(t); - let r = (t instanceof a.S ? rt : nt).bind(null, t, "Attribute"), o = L(e); - return { setRemoveAttr: r, s: o }; +function assignContext(element, _this) { + if (assign_context.has(element)) return assign_context.get(element); + const is_svg = element instanceof enviroment.S; + const setRemoveAttr = (is_svg ? setRemoveNS : setRemove).bind(null, element, "Attribute"); + const s = signals(_this); + return { setRemoveAttr, s }; } -function Y(t, e) { - let n = L(this); - return M( - n, - e, - (r, o) => t.classList.toggle(r, o === -1 ? void 0 : !!o) - ), t; +function classListDeclarative(element, toggle) { + const s = signals(this); + forEachEntries( + s, + toggle, + (class_name, val) => element.classList.toggle(class_name, val === -1 ? void 0 : Boolean(val)) + ); + return element; } -function vt(t) { - return Array.from(t.children).forEach((e) => e.remove()), t; +function empty(el) { + Array.from(el.children).forEach((el2) => el2.remove()); + return el; } -function tt(t, e, n, r) { - return t instanceof a.H ? t[e + "Attribute"](n, r) : t[e + "AttributeNS"](null, n, r); +function elementAttribute(element, op, key, value) { + if (element instanceof enviroment.H) + return element[op + "Attribute"](key, value); + return element[op + "AttributeNS"](null, key, value); } -function et(t, e) { - if (!(e in t)) - return !1; - let n = I(t, e); - return !E(n.set); +function isPropSetter(el, key) { + if (!(key in el)) return false; + const des = getPropDescriptor(el, key); + return !isUndef(des.set); } -function I(t, e) { - if (t = Object.getPrototypeOf(t), !t) - return {}; - let n = Object.getOwnPropertyDescriptor(t, e); - return n || I(t, e); +function getPropDescriptor(p, key) { + p = Object.getPrototypeOf(p); + if (!p) return {}; + const des = Object.getOwnPropertyDescriptor(p, key); + if (!des) return getPropDescriptor(p, key); + return des; } -function M(t, e, n) { - if (!(typeof e != "object" || e === null)) - return Object.entries(e).forEach(function([o, c]) { - o && (c = t.processReactiveAttribute(e, o, c, n), n(o, c)); - }); +function forEachEntries(s, obj, cb) { + if (typeof obj !== "object" || obj === null) return; + return Object.entries(obj).forEach(function process([key, val]) { + if (!key) return; + val = s.processReactiveAttribute(obj, key, val, cb); + cb(key, val); + }); } -function Z(t) { - return Array.isArray(t) ? t.filter(Boolean).join(" ") : t; +function attrArrToStr(attr) { + return Array.isArray(attr) ? attr.filter(Boolean).join(" ") : attr; } -function nt(t, e, n, r) { - return t[(E(r) ? "remove" : "set") + e](n, Z(r)); +function setRemove(obj, prop, key, val) { + return obj[(isUndef(val) ? "remove" : "set") + prop](key, attrArrToStr(val)); } -function rt(t, e, n, r, o = null) { - return t[(E(r) ? "remove" : "set") + e + "NS"](o, n, Z(r)); +function setRemoveNS(obj, prop, key, val, ns = null) { + return obj[(isUndef(val) ? "remove" : "set") + prop + "NS"](ns, key, attrArrToStr(val)); } -function H(t, e, n) { - if (Reflect.set(t, e, n), !!E(n)) - return Reflect.deleteProperty(t, e); +function setDelete(obj, key, val) { + Reflect.set(obj, key, val); + if (!isUndef(val)) return; + return Reflect.deleteProperty(obj, key); } // src/events-observer.js -var w = a.M ? ot() : new Proxy({}, { +var c_ch_o = enviroment.M ? connectionsChangesObserverConstructor() : new Proxy({}, { get() { return () => { }; } }); -function ot() { - let t = /* @__PURE__ */ new Map(), e = !1, n = (i) => function(u) { - for (let s of u) - if (s.type === "childList") { - if (l(s.addedNodes, !0)) { - i(); - continue; - } - A(s.removedNodes, !0) && i(); +function connectionsChangesObserverConstructor() { + const store = /* @__PURE__ */ new Map(); + let is_observing = false; + const observerListener = (stop2) => function(mutations) { + for (const mutation of mutations) { + if (mutation.type !== "childList") continue; + if (observerAdded(mutation.addedNodes, true)) { + stop2(); + continue; } - }, r = new a.M(n(d)); - return { - observe(i) { - let u = new a.M(n(() => { - })); - return u.observe(i, { childList: !0, subtree: !0 }), () => u.disconnect(); - }, - onConnected(i, u) { - f(); - let s = c(i); - s.connected.has(u) || (s.connected.add(u), s.length_c += 1); - }, - offConnected(i, u) { - if (!t.has(i)) - return; - let s = t.get(i); - s.connected.has(u) && (s.connected.delete(u), s.length_c -= 1, o(i, s)); - }, - onDisconnected(i, u) { - f(); - let s = c(i); - s.disconnected.has(u) || (s.disconnected.add(u), s.length_d += 1); - }, - offDisconnected(i, u) { - if (!t.has(i)) - return; - let s = t.get(i); - s.disconnected.has(u) && (s.disconnected.delete(u), s.length_d -= 1, o(i, s)); + if (observerRemoved(mutation.removedNodes, true)) + stop2(); } }; - function o(i, u) { - u.length_c || u.length_d || (t.delete(i), d()); + const observer = new enviroment.M(observerListener(stop)); + return { + observe(element) { + const o = new enviroment.M(observerListener(() => { + })); + o.observe(element, { childList: true, subtree: true }); + return () => o.disconnect(); + }, + onConnected(element, listener) { + start(); + const listeners = getElementStore(element); + if (listeners.connected.has(listener)) return; + listeners.connected.add(listener); + listeners.length_c += 1; + }, + offConnected(element, listener) { + if (!store.has(element)) return; + const ls = store.get(element); + if (!ls.connected.has(listener)) return; + ls.connected.delete(listener); + ls.length_c -= 1; + cleanWhenOff(element, ls); + }, + onDisconnected(element, listener) { + start(); + const listeners = getElementStore(element); + if (listeners.disconnected.has(listener)) return; + listeners.disconnected.add(listener); + listeners.length_d += 1; + }, + offDisconnected(element, listener) { + if (!store.has(element)) return; + const ls = store.get(element); + if (!ls.disconnected.has(listener)) return; + ls.disconnected.delete(listener); + ls.length_d -= 1; + cleanWhenOff(element, ls); + } + }; + function cleanWhenOff(element, ls) { + if (ls.length_c || ls.length_d) + return; + store.delete(element); + stop(); } - function c(i) { - if (t.has(i)) - return t.get(i); - let u = { + function getElementStore(element) { + if (store.has(element)) return store.get(element); + const out = { connected: /* @__PURE__ */ new WeakSet(), length_c: 0, disconnected: /* @__PURE__ */ new WeakSet(), length_d: 0 }; - return t.set(i, u), u; + store.set(element, out); + return out; } - function f() { - e || (e = !0, r.observe(a.D.body, { childList: !0, subtree: !0 })); + function start() { + if (is_observing) return; + is_observing = true; + observer.observe(enviroment.D.body, { childList: true, subtree: true }); } - function d() { - !e || t.size || (e = !1, r.disconnect()); + function stop() { + if (!is_observing || store.size) return; + is_observing = false; + observer.disconnect(); } - function p() { - return new Promise(function(i) { - (requestIdleCallback || requestAnimationFrame)(i); + function requestIdle() { + return new Promise(function(resolve) { + (requestIdleCallback || requestAnimationFrame)(resolve); }); } - async function b(i) { - t.size > 30 && await p(); - let u = []; - if (!(i instanceof Node)) - return u; - for (let s of t.keys()) - s === i || !(s instanceof Node) || i.contains(s) && u.push(s); - return u; - } - function l(i, u) { - let s = !1; - for (let h of i) { - if (u && b(h).then(l), !t.has(h)) - continue; - let m = t.get(h); - m.length_c && (h.dispatchEvent(new Event(g)), m.connected = /* @__PURE__ */ new WeakSet(), m.length_c = 0, m.length_d || t.delete(h), s = !0); + async function collectChildren(element) { + if (store.size > 30) + await requestIdle(); + const out = []; + if (!(element instanceof Node)) return out; + for (const el of store.keys()) { + if (el === element || !(el instanceof Node)) continue; + if (element.contains(el)) + out.push(el); } - return s; + return out; } - function A(i, u) { - let s = !1; - for (let h of i) - u && b(h).then(A), !(!t.has(h) || !t.get(h).length_d) && ((globalThis.queueMicrotask || setTimeout)(_(h)), s = !0); - return s; + function observerAdded(addedNodes, is_root) { + let out = false; + for (const element of addedNodes) { + if (is_root) collectChildren(element).then(observerAdded); + if (!store.has(element)) continue; + const ls = store.get(element); + if (!ls.length_c) continue; + element.dispatchEvent(new Event(evc)); + ls.connected = /* @__PURE__ */ new WeakSet(); + ls.length_c = 0; + if (!ls.length_d) store.delete(element); + out = true; + } + return out; } - function _(i) { + function observerRemoved(removedNodes, is_root) { + let out = false; + for (const element of removedNodes) { + if (is_root) collectChildren(element).then(observerRemoved); + if (!store.has(element)) continue; + const ls = store.get(element); + if (!ls.length_d) continue; + (globalThis.queueMicrotask || setTimeout)(dispatchRemove(element)); + out = true; + } + return out; + } + function dispatchRemove(element) { return () => { - i.isConnected || (i.dispatchEvent(new Event(y)), t.delete(i)); + if (element.isConnected) return; + element.dispatchEvent(new Event(evd)); + store.delete(element); }; } } // src/customElement.js -function Dt(t, e, n, r = it) { - S.push({ - scope: t, - host: (...f) => f.length ? f.forEach((d) => d(t)) : t - }), typeof r == "function" && (r = r.call(t, t)); - let o = t[x]; - o || ct(t); - let c = n.call(t, r); - return o || t.dispatchEvent(new Event(g)), e.nodeType === 11 && typeof e.mode == "string" && t.addEventListener(y, w.observe(e), { once: !0 }), S.pop(), e.append(c); +function customElementRender(custom_element, target, render, props = observedAttributes2) { + scope.push({ + scope: custom_element, + host: (...c) => c.length ? c.forEach((c2) => c2(custom_element)) : custom_element + }); + if (typeof props === "function") props = props.call(custom_element, custom_element); + const is_lte = custom_element[keyLTE]; + if (!is_lte) lifecyclesToEvents(custom_element); + const out = render.call(custom_element, props); + if (!is_lte) custom_element.dispatchEvent(new Event(evc)); + if (target.nodeType === 11 && typeof target.mode === "string") + custom_element.addEventListener(evd, c_ch_o.observe(target), { once: true }); + scope.pop(); + return target.append(out); } -function ct(t) { - return W(t.prototype, "connectedCallback", function(e, n, r) { - e.apply(n, r), n.dispatchEvent(new Event(g)); - }), W(t.prototype, "disconnectedCallback", function(e, n, r) { - e.apply(n, r), (globalThis.queueMicrotask || setTimeout)( - () => !n.isConnected && n.dispatchEvent(new Event(y)) +function lifecyclesToEvents(class_declaration) { + wrapMethod(class_declaration.prototype, "connectedCallback", function(target, thisArg, detail) { + target.apply(thisArg, detail); + thisArg.dispatchEvent(new Event(evc)); + }); + wrapMethod(class_declaration.prototype, "disconnectedCallback", function(target, thisArg, detail) { + target.apply(thisArg, detail); + (globalThis.queueMicrotask || setTimeout)( + () => !thisArg.isConnected && thisArg.dispatchEvent(new Event(evd)) ); - }), W(t.prototype, "attributeChangedCallback", function(e, n, r) { - let [o, , c] = r; - n.dispatchEvent(new CustomEvent(D, { - detail: [o, c] - })), e.apply(n, r); - }), t.prototype[x] = !0, t; + }); + wrapMethod(class_declaration.prototype, "attributeChangedCallback", function(target, thisArg, detail) { + const [attribute, , value] = detail; + thisArg.dispatchEvent(new CustomEvent(eva, { + detail: [attribute, value] + })); + target.apply(thisArg, detail); + }); + class_declaration.prototype[keyLTE] = true; + return class_declaration; } -function W(t, e, n) { - t[e] = new Proxy(t[e] || (() => { - }), { apply: n }); +function wrapMethod(obj, method, apply) { + obj[method] = new Proxy(obj[method] || (() => { + }), { apply }); } -function it(t) { - return F(t, (e, n) => e.getAttribute(n)); +function observedAttributes2(instance) { + return observedAttributes(instance, (i, n) => i.getAttribute(n)); } // src/events.js -function _t(t, e, n) { - return e || (e = {}), function(o, ...c) { - n && (c.unshift(o), o = typeof n == "function" ? n() : n); - let f = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e); - return o.dispatchEvent(f); +function dispatchEvent(name, options, host) { + if (!options) options = {}; + return function dispatch(element, ...d) { + if (host) { + d.unshift(element); + element = typeof host === "function" ? host() : host; + } + const event = d.length ? new CustomEvent(name, Object.assign({ detail: d[0] }, options)) : new Event(name, options); + return element.dispatchEvent(event); }; } -function R(t, e, n) { - return function(o) { - return o.addEventListener(t, e, n), o; +function on(event, listener, options) { + return function registerElement(element) { + element.addEventListener(event, listener, options); + return element; }; } -var G = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 }); -R.connected = function(t, e) { - return e = G(e), function(r) { - return r.addEventListener(g, t, e), r[x] ? r : r.isConnected ? (r.dispatchEvent(new Event(g)), r) : (N(e.signal, () => w.offConnected(r, t)) && w.onConnected(r, t), r); +var lifeOptions = (obj) => Object.assign({}, typeof obj === "object" ? obj : null, { once: true }); +on.connected = function(listener, options) { + options = lifeOptions(options); + return function registerElement(element) { + element.addEventListener(evc, listener, options); + if (element[keyLTE]) return element; + if (element.isConnected) return element.dispatchEvent(new Event(evc)), element; + const c = onAbort(options.signal, () => c_ch_o.offConnected(element, listener)); + if (c) c_ch_o.onConnected(element, listener); + return element; }; }; -R.disconnected = function(t, e) { - return e = G(e), function(r) { - return r.addEventListener(y, t, e), r[x] || N(e.signal, () => w.offDisconnected(r, t)) && w.onDisconnected(r, t), r; +on.disconnected = function(listener, options) { + options = lifeOptions(options); + return function registerElement(element) { + element.addEventListener(evd, listener, options); + if (element[keyLTE]) return element; + const c = onAbort(options.signal, () => c_ch_o.offDisconnected(element, listener)); + if (c) c_ch_o.onDisconnected(element, listener); + return element; }; }; -var j = /* @__PURE__ */ new WeakMap(); -R.disconnectedAsAbort = function(t) { - if (j.has(t)) - return j.get(t); - let e = new AbortController(); - return j.set(t, e), t(R.disconnected(() => e.abort())), e; +var store_abort = /* @__PURE__ */ new WeakMap(); +on.disconnectedAsAbort = function(host) { + if (store_abort.has(host)) return store_abort.get(host); + const a = new AbortController(); + store_abort.set(host, a); + host(on.disconnected(() => a.abort())); + return a; }; -var st = /* @__PURE__ */ new WeakSet(); -R.attributeChanged = function(t, e) { - return typeof e != "object" && (e = {}), function(r) { - if (r.addEventListener(D, t, e), r[x] || st.has(r) || !a.M) - return r; - let o = new a.M(function(f) { - for (let { attributeName: d, target: p } of f) - p.dispatchEvent( - new CustomEvent(D, { detail: [d, p.getAttribute(d)] }) +var els_attribute_store = /* @__PURE__ */ new WeakSet(); +on.attributeChanged = function(listener, options) { + if (typeof options !== "object") + options = {}; + return function registerElement(element) { + element.addEventListener(eva, listener, options); + if (element[keyLTE] || els_attribute_store.has(element)) + return element; + if (!enviroment.M) return element; + const observer = new enviroment.M(function(mutations) { + for (const { attributeName, target } of mutations) + target.dispatchEvent( + new CustomEvent(eva, { detail: [attributeName, target.getAttribute(attributeName)] }) ); }); - return N(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r; + const c = onAbort(options.signal, () => observer.disconnect()); + if (c) observer.observe(element, { attributes: true }); + return element; }; }; globalThis.dde= { - assign: O, - assignAttribute: z, - chainableAppend: Q, - classListDeclarative: Y, - createElement: k, - createElementNS: gt, - customElementRender: Dt, - customElementWithDDE: ct, - dispatchEvent: _t, - el: k, - elNS: gt, - elementAttribute: tt, - empty: vt, - lifecyclesToEvents: ct, - observedAttributes: it, - on: R, - registerReactivity: V, - scope: S, - simulateSlots: bt + assign, + assignAttribute, + chainableAppend, + classListDeclarative, + createElement, + createElementNS, + customElementRender, + customElementWithDDE: lifecyclesToEvents, + dispatchEvent, + el: createElement, + elNS: createElementNS, + elementAttribute, + empty, + lifecyclesToEvents, + observedAttributes: observedAttributes2, + on, + registerReactivity, + scope, + simulateSlots }; })(); \ No newline at end of file diff --git a/dist/esm-with-signals.d.ts b/dist/esm-with-signals.d.ts index 0e26b90..18a5868 100644 --- a/dist/esm-with-signals.d.ts +++ b/dist/esm-with-signals.d.ts @@ -47,7 +47,7 @@ type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagName type textContent= string | ddeSignal; export function el< TAG extends keyof ExtendedHTMLElementTagNameMap, - EL extends (TAG extends keyof ExtendedHTMLElementTagNameMap ? ExtendedHTMLElementTagNameMap[TAG] : HTMLElement) + EL extends ExtendedHTMLElementTagNameMap[TAG] >( tag_name: TAG, attrs?: ElementAttributes | textContent, @@ -58,7 +58,7 @@ export function el( ): ddeDocumentFragment export function el( tag_name: string, - attrs?: ElementAttributes, + attrs?: ElementAttributes | textContent, ...addons: ddeElementAddon[] ): ddeHTMLElement @@ -101,7 +101,18 @@ export function elNS( export { elNS as createElementNS } export function chainableAppend(el: EL): EL; -export function simulateSlots(el: EL): EL +/** + * Mapper function (optional). Pass for coppying attributes, this is NOT implemented by {@link simulateSlots} itself! + * */ +type simulateSlotsMapper= (body: HTMLSlotElement, el: HTMLElement)=> void; +/** Simulate slots for ddeComponents */ +export function simulateSlots(root: EL, mapper?: simulateSlotsMapper): EL +/** + * Simulate slots in Custom Elements without using `shadowRoot`. + * @param el Custom Element root element + * @param body Body of the custom element + * */ +export function simulateSlots(el: HTMLElement, body: EL, mapper?: simulateSlotsMapper): EL export function dispatchEvent(name: keyof DocumentEventMap | string, options?: EventInit): (element: SupportedElement, data?: any)=> void; diff --git a/dist/esm-with-signals.js b/dist/esm-with-signals.js index e441a79..0e67e17 100644 --- a/dist/esm-with-signals.js +++ b/dist/esm-with-signals.js @@ -1,49 +1,56 @@ // src/signals-common.js -var k = { - isSignal(t) { - return !1; +var signals_global = { + isSignal(attributes) { + return false; }, - processReactiveAttribute(t, e, n, r) { - return n; + processReactiveAttribute(obj, key, attr, set) { + return attr; } }; -function B(t, e = !0) { - return e ? Object.assign(k, t) : (Object.setPrototypeOf(t, k), t); +function registerReactivity(def, global = true) { + if (global) return Object.assign(signals_global, def); + Object.setPrototypeOf(def, signals_global); + return def; } -function W(t) { - return k.isPrototypeOf(t) && t !== k ? t : k; +function signals(_this) { + return signals_global.isPrototypeOf(_this) && _this !== signals_global ? _this : signals_global; } // src/helpers.js -var T = (...t) => Object.prototype.hasOwnProperty.call(...t); -function S(t) { - return typeof t > "u"; +var hasOwn = (...a) => Object.prototype.hasOwnProperty.call(...a); +function isUndef(value) { + return typeof value === "undefined"; } -function X(t) { - let e = typeof t; - return e !== "object" ? e : t === null ? "null" : Object.prototype.toString.call(t); +function typeOf(v) { + const t = typeof v; + if (t !== "object") return t; + if (v === null) return "null"; + return Object.prototype.toString.call(v); } -function q(t, e) { - if (!t || !(t instanceof AbortSignal)) - return !0; - if (!t.aborted) - return t.addEventListener("abort", e), function() { - t.removeEventListener("abort", e); - }; +function onAbort(signal2, listener) { + if (!signal2 || !(signal2 instanceof AbortSignal)) + return true; + if (signal2.aborted) + return; + signal2.addEventListener("abort", listener); + return function cleanUp() { + signal2.removeEventListener("abort", listener); + }; } -function F(t, e) { - let { observedAttributes: n = [] } = t.constructor; - return n.reduce(function(r, o) { - return r[pt(o)] = e(t, o), r; +function observedAttributes(instance, observedAttribute2) { + const { observedAttributes: observedAttributes3 = [] } = instance.constructor; + return observedAttributes3.reduce(function(out, name) { + out[kebabToCamel(name)] = observedAttribute2(instance, name); + return out; }, {}); } -function pt(t) { - return t.replace(/-./g, (e) => e[1].toUpperCase()); +function kebabToCamel(name) { + return name.replace(/-./g, (x) => x[1].toUpperCase()); } // src/dom-common.js -var d = { - setDeleteAttr: lt, +var enviroment = { + setDeleteAttr, ssr: "", D: globalThis.document, F: globalThis.DocumentFragment, @@ -51,641 +58,820 @@ var d = { S: globalThis.SVGElement, M: globalThis.MutationObserver }; -function lt(t, e, n) { - if (Reflect.set(t, e, n), !!S(n)) { - if (Reflect.deleteProperty(t, e), t instanceof d.H && t.getAttribute(e) === "undefined") - return t.removeAttribute(e); - if (Reflect.get(t, e) === "undefined") - return Reflect.set(t, e, ""); - } +function setDeleteAttr(obj, prop, val) { + Reflect.set(obj, prop, val); + if (!isUndef(val)) return; + Reflect.deleteProperty(obj, prop); + if (obj instanceof enviroment.H && obj.getAttribute(prop) === "undefined") + return obj.removeAttribute(prop); + if (Reflect.get(obj, prop) === "undefined") + return Reflect.set(obj, prop, ""); } -var C = "__dde_lifecyclesToEvents", _ = "dde:connected", O = "dde:disconnected", M = "dde:attributeChanged"; +var keyLTE = "__dde_lifecyclesToEvents"; +var evc = "dde:connected"; +var evd = "dde:disconnected"; +var eva = "dde:attributeChanged"; // src/dom.js -var A = [{ +var scopes = [{ get scope() { - return d.D.body; + return enviroment.D.body; }, - host: (t) => t ? t(d.D.body) : d.D.body, - prevent: !0 -}], m = { + host: (c) => c ? c(enviroment.D.body) : enviroment.D.body, + prevent: true +}]; +var scope = { get current() { - return A[A.length - 1]; + return scopes[scopes.length - 1]; }, get host() { return this.current.host; }, preventDefault() { - let { current: t } = this; - return t.prevent = !0, t; + const { current } = this; + current.prevent = true; + return current; }, get state() { - return [...A]; + return [...scopes]; }, - push(t = {}) { - return A.push(Object.assign({}, this.current, { prevent: !1 }, t)); + push(s = {}) { + return scopes.push(Object.assign({}, this.current, { prevent: false }, s)); }, pushRoot() { - return A.push(A[0]); + return scopes.push(scopes[0]); }, pop() { - if (A.length !== 1) - return A.pop(); + if (scopes.length === 1) return; + return scopes.pop(); } }; -function Y(...t) { - return this.appendOriginal(...t), this; +function append(...els) { + this.appendOriginal(...els); + return this; } -function ht(t) { - return t.append === Y || (t.appendOriginal = t.append, t.append = Y), t; +function chainableAppend(el) { + if (el.append === append) return el; + el.appendOriginal = el.append; + el.append = append; + return el; } -var $; -function j(t, e, ...n) { - let r = W(this), o = 0, c, i; - switch ((Object(e) !== e || r.isSignal(e)) && (e = { textContent: e }), !0) { - case typeof t == "function": { - o = 1, m.push({ scope: t, host: (...v) => v.length ? (o === 1 ? n.unshift(...v) : v.forEach((l) => l(i)), void 0) : i }), c = t(e || void 0); - let a = c instanceof d.F; - if (c.nodeName === "#comment") - break; - let h = j.mark({ +var namespace; +function createElement(tag, attributes, ...addons) { + const s = signals(this); + let scoped = 0; + let el, el_host; + if (Object(attributes) !== attributes || s.isSignal(attributes)) + attributes = { textContent: attributes }; + switch (true) { + case typeof tag === "function": { + scoped = 1; + scope.push({ scope: tag, host: (...c) => c.length ? (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0) : el_host }); + el = tag(attributes || void 0); + const is_fragment = el instanceof enviroment.F; + if (el.nodeName === "#comment") break; + const el_mark = createElement.mark({ type: "component", - name: t.name, - host: a ? "this" : "parentElement" + name: tag.name, + host: is_fragment ? "this" : "parentElement" }); - c.prepend(h), a && (i = h); + el.prepend(el_mark); + if (is_fragment) el_host = el_mark; break; } - case t === "#text": - c = P.call(this, d.D.createTextNode(""), e); + case tag === "#text": + el = assign.call(this, enviroment.D.createTextNode(""), attributes); break; - case (t === "<>" || !t): - c = P.call(this, d.D.createDocumentFragment(), e); + case (tag === "<>" || !tag): + el = assign.call(this, enviroment.D.createDocumentFragment(), attributes); break; - case !!$: - c = P.call(this, d.D.createElementNS($, t), e); + case Boolean(namespace): + el = assign.call(this, enviroment.D.createElementNS(namespace, tag), attributes); break; - case !c: - c = P.call(this, d.D.createElement(t), e); + case !el: + el = assign.call(this, enviroment.D.createElement(tag), attributes); } - return ht(c), i || (i = c), n.forEach((a) => a(i)), o && m.pop(), o = 2, c; + chainableAppend(el); + if (!el_host) el_host = el; + addons.forEach((c) => c(el_host)); + if (scoped) scope.pop(); + scoped = 2; + return el; } -function Wt(t, e = t, n = void 0) { - let r = Symbol.for("default"), o = Array.from(e.querySelectorAll("slot")).reduce((i, a) => Reflect.set(i, a.name || r, a) && i, {}), c = T(o, r); - if (t.append = new Proxy(t.append, { - apply(i, a, h) { - if (!h.length) - return t; - let v = d.D.createDocumentFragment(); - for (let l of h) { - if (!l || !l.slot) { - c && v.appendChild(l); +function simulateSlots(element, root, mapper) { + if (typeof root !== "object") { + mapper = root; + root = element; + } + const _default = Symbol.for("default"); + const slots = Array.from(root.querySelectorAll("slot")).reduce((out, curr) => Reflect.set(out, curr.name || _default, curr) && out, {}); + const has_d = hasOwn(slots, _default); + element.append = new Proxy(element.append, { + apply(orig, _, els) { + if (els[0] === root) return orig.apply(element, els); + if (!els.length) return element; + const d = enviroment.D.createDocumentFragment(); + for (const el of els) { + if (!el || !el.slot) { + if (has_d) d.append(el); continue; } - let x = l.slot, y = o[x]; - vt(l, "remove", "slot"), y && (bt(y, l, n), Reflect.deleteProperty(o, x)); + const name = el.slot; + const slot = slots[name]; + elementAttribute(el, "remove", "slot"); + if (!slot) continue; + simulateSlotReplace(slot, el, mapper); + Reflect.deleteProperty(slots, name); } - return c && (o[r].replaceWith(v), Reflect.deleteProperty(o, r)), t.append = i, t; + if (has_d) { + slots[_default].replaceWith(d); + Reflect.deleteProperty(slots, _default); + } + element.append = orig; + return element; } - }), t !== e) { - let i = Array.from(t.childNodes); - i.forEach((a) => a.remove()), t.append(...i); + }); + if (element !== root) { + const els = Array.from(element.childNodes); + els.forEach((el) => el.remove()); + element.append(...els); } - return e; + return root; } -function bt(t, e, n) { - n && n(t, e); +function simulateSlotReplace(slot, element, mapper) { + if (mapper) mapper(slot, element); try { - t.replaceWith(P(e, { className: [e.className, t.className], dataset: { ...t.dataset } })); - } catch { - t.replaceWith(e); + slot.replaceWith(assign(element, { className: [element.className, slot.className], dataset: { ...slot.dataset } })); + } catch (_) { + slot.replaceWith(element); } } -j.mark = function(t, e = !1) { - t = Object.entries(t).map(([o, c]) => o + `="${c}"`).join(" "); - let n = e ? "" : "/", r = d.D.createComment(``); - return e && (r.end = d.D.createComment("")), r; +createElement.mark = function(attrs, is_open = false) { + attrs = Object.entries(attrs).map(([n, v]) => n + `="${v}"`).join(" "); + const end = is_open ? "" : "/"; + const out = enviroment.D.createComment(``); + if (is_open) out.end = enviroment.D.createComment(""); + return out; }; -function qt(t) { - let e = this; - return function(...r) { - $ = t; - let o = j.call(e, ...r); - return $ = void 0, o; +function createElementNS(ns) { + const _this = this; + return function createElementNSCurried(...rest) { + namespace = ns; + const el = createElement.call(_this, ...rest); + namespace = void 0; + return el; }; } -var U = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: tt } = d; -function P(t, ...e) { - if (!e.length) - return t; - U.set(t, rt(t, this)); - for (let [n, r] of Object.entries(Object.assign({}, ...e))) - nt.call(this, t, n, r); - return U.delete(t), t; +var assign_context = /* @__PURE__ */ new WeakMap(); +var { setDeleteAttr: setDeleteAttr2 } = enviroment; +function assign(element, ...attributes) { + if (!attributes.length) return element; + assign_context.set(element, assignContext(element, this)); + for (const [key, value] of Object.entries(Object.assign({}, ...attributes))) + assignAttribute.call(this, element, key, value); + assign_context.delete(element); + return element; } -function nt(t, e, n) { - let { setRemoveAttr: r, s: o } = rt(t, this), c = this; - n = o.processReactiveAttribute( - t, - e, - n, - (a, h) => nt.call(c, t, a, h) +function assignAttribute(element, key, value) { + const { setRemoveAttr, s } = assignContext(element, this); + const _this = this; + value = s.processReactiveAttribute( + element, + key, + value, + (key2, value2) => assignAttribute.call(_this, element, key2, value2) ); - let [i] = e; - if (i === "=") - return r(e.slice(1), n); - if (i === ".") - return et(t, e.slice(1), n); - if (/(aria|data)([A-Z])/.test(e)) - return e = e.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(), r(e, n); - switch (e === "className" && (e = "class"), e) { - case "xlink:href": - return r(e, n, "http://www.w3.org/1999/xlink"); - case "textContent": - return tt(t, e, n); - case "style": - if (typeof n != "object") - break; - case "dataset": - return I(o, n, et.bind(null, t[e])); - case "ariaset": - return I(o, n, (a, h) => r("aria-" + a, h)); - case "classList": - return gt.call(c, t, n); + const [k] = key; + if ("=" === k) return setRemoveAttr(key.slice(1), value); + if ("." === k) return setDelete(element, key.slice(1), value); + if (/(aria|data)([A-Z])/.test(key)) { + key = key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(); + return setRemoveAttr(key, value); } - return Et(t, e) ? tt(t, e, n) : r(e, n); + if ("className" === key) key = "class"; + switch (key) { + case "xlink:href": + return setRemoveAttr(key, value, "http://www.w3.org/1999/xlink"); + case "textContent": + return setDeleteAttr2(element, key, value); + case "style": + if (typeof value !== "object") break; + /* falls through */ + case "dataset": + return forEachEntries(s, value, setDelete.bind(null, element[key])); + case "ariaset": + return forEachEntries(s, value, (key2, val) => setRemoveAttr("aria-" + key2, val)); + case "classList": + return classListDeclarative.call(_this, element, value); + } + return isPropSetter(element, key) ? setDeleteAttr2(element, key, value) : setRemoveAttr(key, value); } -function rt(t, e) { - if (U.has(t)) - return U.get(t); - let r = (t instanceof d.S ? xt : mt).bind(null, t, "Attribute"), o = W(e); - return { setRemoveAttr: r, s: o }; +function assignContext(element, _this) { + if (assign_context.has(element)) return assign_context.get(element); + const is_svg = element instanceof enviroment.S; + const setRemoveAttr = (is_svg ? setRemoveNS : setRemove).bind(null, element, "Attribute"); + const s = signals(_this); + return { setRemoveAttr, s }; } -function gt(t, e) { - let n = W(this); - return I( - n, - e, - (r, o) => t.classList.toggle(r, o === -1 ? void 0 : !!o) - ), t; +function classListDeclarative(element, toggle) { + const s = signals(this); + forEachEntries( + s, + toggle, + (class_name, val) => element.classList.toggle(class_name, val === -1 ? void 0 : Boolean(val)) + ); + return element; } -function Ft(t) { - return Array.from(t.children).forEach((e) => e.remove()), t; +function empty(el) { + Array.from(el.children).forEach((el2) => el2.remove()); + return el; } -function vt(t, e, n, r) { - return t instanceof d.H ? t[e + "Attribute"](n, r) : t[e + "AttributeNS"](null, n, r); +function elementAttribute(element, op, key, value) { + if (element instanceof enviroment.H) + return element[op + "Attribute"](key, value); + return element[op + "AttributeNS"](null, key, value); } -function Et(t, e) { - if (!(e in t)) - return !1; - let n = ot(t, e); - return !S(n.set); +function isPropSetter(el, key) { + if (!(key in el)) return false; + const des = getPropDescriptor(el, key); + return !isUndef(des.set); } -function ot(t, e) { - if (t = Object.getPrototypeOf(t), !t) - return {}; - let n = Object.getOwnPropertyDescriptor(t, e); - return n || ot(t, e); +function getPropDescriptor(p, key) { + p = Object.getPrototypeOf(p); + if (!p) return {}; + const des = Object.getOwnPropertyDescriptor(p, key); + if (!des) return getPropDescriptor(p, key); + return des; } -function I(t, e, n) { - if (!(typeof e != "object" || e === null)) - return Object.entries(e).forEach(function([o, c]) { - o && (c = t.processReactiveAttribute(e, o, c, n), n(o, c)); - }); +function forEachEntries(s, obj, cb) { + if (typeof obj !== "object" || obj === null) return; + return Object.entries(obj).forEach(function process([key, val]) { + if (!key) return; + val = s.processReactiveAttribute(obj, key, val, cb); + cb(key, val); + }); } -function ct(t) { - return Array.isArray(t) ? t.filter(Boolean).join(" ") : t; +function attrArrToStr(attr) { + return Array.isArray(attr) ? attr.filter(Boolean).join(" ") : attr; } -function mt(t, e, n, r) { - return t[(S(r) ? "remove" : "set") + e](n, ct(r)); +function setRemove(obj, prop, key, val) { + return obj[(isUndef(val) ? "remove" : "set") + prop](key, attrArrToStr(val)); } -function xt(t, e, n, r, o = null) { - return t[(S(r) ? "remove" : "set") + e + "NS"](o, n, ct(r)); +function setRemoveNS(obj, prop, key, val, ns = null) { + return obj[(isUndef(val) ? "remove" : "set") + prop + "NS"](ns, key, attrArrToStr(val)); } -function et(t, e, n) { - if (Reflect.set(t, e, n), !!S(n)) - return Reflect.deleteProperty(t, e); +function setDelete(obj, key, val) { + Reflect.set(obj, key, val); + if (!isUndef(val)) return; + return Reflect.deleteProperty(obj, key); } // src/events-observer.js -var D = d.M ? wt() : new Proxy({}, { +var c_ch_o = enviroment.M ? connectionsChangesObserverConstructor() : new Proxy({}, { get() { return () => { }; } }); -function wt() { - let t = /* @__PURE__ */ new Map(), e = !1, n = (s) => function(u) { - for (let f of u) - if (f.type === "childList") { - if (l(f.addedNodes, !0)) { - s(); - continue; - } - x(f.removedNodes, !0) && s(); +function connectionsChangesObserverConstructor() { + const store = /* @__PURE__ */ new Map(); + let is_observing = false; + const observerListener = (stop2) => function(mutations) { + for (const mutation of mutations) { + if (mutation.type !== "childList") continue; + if (observerAdded(mutation.addedNodes, true)) { + stop2(); + continue; } - }, r = new d.M(n(a)); - return { - observe(s) { - let u = new d.M(n(() => { - })); - return u.observe(s, { childList: !0, subtree: !0 }), () => u.disconnect(); - }, - onConnected(s, u) { - i(); - let f = c(s); - f.connected.has(u) || (f.connected.add(u), f.length_c += 1); - }, - offConnected(s, u) { - if (!t.has(s)) - return; - let f = t.get(s); - f.connected.has(u) && (f.connected.delete(u), f.length_c -= 1, o(s, f)); - }, - onDisconnected(s, u) { - i(); - let f = c(s); - f.disconnected.has(u) || (f.disconnected.add(u), f.length_d += 1); - }, - offDisconnected(s, u) { - if (!t.has(s)) - return; - let f = t.get(s); - f.disconnected.has(u) && (f.disconnected.delete(u), f.length_d -= 1, o(s, f)); + if (observerRemoved(mutation.removedNodes, true)) + stop2(); } }; - function o(s, u) { - u.length_c || u.length_d || (t.delete(s), a()); + const observer = new enviroment.M(observerListener(stop)); + return { + observe(element) { + const o = new enviroment.M(observerListener(() => { + })); + o.observe(element, { childList: true, subtree: true }); + return () => o.disconnect(); + }, + onConnected(element, listener) { + start(); + const listeners = getElementStore(element); + if (listeners.connected.has(listener)) return; + listeners.connected.add(listener); + listeners.length_c += 1; + }, + offConnected(element, listener) { + if (!store.has(element)) return; + const ls = store.get(element); + if (!ls.connected.has(listener)) return; + ls.connected.delete(listener); + ls.length_c -= 1; + cleanWhenOff(element, ls); + }, + onDisconnected(element, listener) { + start(); + const listeners = getElementStore(element); + if (listeners.disconnected.has(listener)) return; + listeners.disconnected.add(listener); + listeners.length_d += 1; + }, + offDisconnected(element, listener) { + if (!store.has(element)) return; + const ls = store.get(element); + if (!ls.disconnected.has(listener)) return; + ls.disconnected.delete(listener); + ls.length_d -= 1; + cleanWhenOff(element, ls); + } + }; + function cleanWhenOff(element, ls) { + if (ls.length_c || ls.length_d) + return; + store.delete(element); + stop(); } - function c(s) { - if (t.has(s)) - return t.get(s); - let u = { + function getElementStore(element) { + if (store.has(element)) return store.get(element); + const out = { connected: /* @__PURE__ */ new WeakSet(), length_c: 0, disconnected: /* @__PURE__ */ new WeakSet(), length_d: 0 }; - return t.set(s, u), u; + store.set(element, out); + return out; } - function i() { - e || (e = !0, r.observe(d.D.body, { childList: !0, subtree: !0 })); + function start() { + if (is_observing) return; + is_observing = true; + observer.observe(enviroment.D.body, { childList: true, subtree: true }); } - function a() { - !e || t.size || (e = !1, r.disconnect()); + function stop() { + if (!is_observing || store.size) return; + is_observing = false; + observer.disconnect(); } - function h() { - return new Promise(function(s) { - (requestIdleCallback || requestAnimationFrame)(s); + function requestIdle() { + return new Promise(function(resolve) { + (requestIdleCallback || requestAnimationFrame)(resolve); }); } - async function v(s) { - t.size > 30 && await h(); - let u = []; - if (!(s instanceof Node)) - return u; - for (let f of t.keys()) - f === s || !(f instanceof Node) || s.contains(f) && u.push(f); - return u; - } - function l(s, u) { - let f = !1; - for (let b of s) { - if (u && v(b).then(l), !t.has(b)) - continue; - let N = t.get(b); - N.length_c && (b.dispatchEvent(new Event(_)), N.connected = /* @__PURE__ */ new WeakSet(), N.length_c = 0, N.length_d || t.delete(b), f = !0); + async function collectChildren(element) { + if (store.size > 30) + await requestIdle(); + const out = []; + if (!(element instanceof Node)) return out; + for (const el of store.keys()) { + if (el === element || !(el instanceof Node)) continue; + if (element.contains(el)) + out.push(el); } - return f; + return out; } - function x(s, u) { - let f = !1; - for (let b of s) - u && v(b).then(x), !(!t.has(b) || !t.get(b).length_d) && ((globalThis.queueMicrotask || setTimeout)(y(b)), f = !0); - return f; + function observerAdded(addedNodes, is_root) { + let out = false; + for (const element of addedNodes) { + if (is_root) collectChildren(element).then(observerAdded); + if (!store.has(element)) continue; + const ls = store.get(element); + if (!ls.length_c) continue; + element.dispatchEvent(new Event(evc)); + ls.connected = /* @__PURE__ */ new WeakSet(); + ls.length_c = 0; + if (!ls.length_d) store.delete(element); + out = true; + } + return out; } - function y(s) { + function observerRemoved(removedNodes, is_root) { + let out = false; + for (const element of removedNodes) { + if (is_root) collectChildren(element).then(observerRemoved); + if (!store.has(element)) continue; + const ls = store.get(element); + if (!ls.length_d) continue; + (globalThis.queueMicrotask || setTimeout)(dispatchRemove(element)); + out = true; + } + return out; + } + function dispatchRemove(element) { return () => { - s.isConnected || (s.dispatchEvent(new Event(O)), t.delete(s)); + if (element.isConnected) return; + element.dispatchEvent(new Event(evd)); + store.delete(element); }; } } // src/customElement.js -function Zt(t, e, n, r = _t) { - m.push({ - scope: t, - host: (...i) => i.length ? i.forEach((a) => a(t)) : t - }), typeof r == "function" && (r = r.call(t, t)); - let o = t[C]; - o || yt(t); - let c = n.call(t, r); - return o || t.dispatchEvent(new Event(_)), e.nodeType === 11 && typeof e.mode == "string" && t.addEventListener(O, D.observe(e), { once: !0 }), m.pop(), e.append(c); +function customElementRender(custom_element, target, render, props = observedAttributes2) { + scope.push({ + scope: custom_element, + host: (...c) => c.length ? c.forEach((c2) => c2(custom_element)) : custom_element + }); + if (typeof props === "function") props = props.call(custom_element, custom_element); + const is_lte = custom_element[keyLTE]; + if (!is_lte) lifecyclesToEvents(custom_element); + const out = render.call(custom_element, props); + if (!is_lte) custom_element.dispatchEvent(new Event(evc)); + if (target.nodeType === 11 && typeof target.mode === "string") + custom_element.addEventListener(evd, c_ch_o.observe(target), { once: true }); + scope.pop(); + return target.append(out); } -function yt(t) { - return J(t.prototype, "connectedCallback", function(e, n, r) { - e.apply(n, r), n.dispatchEvent(new Event(_)); - }), J(t.prototype, "disconnectedCallback", function(e, n, r) { - e.apply(n, r), (globalThis.queueMicrotask || setTimeout)( - () => !n.isConnected && n.dispatchEvent(new Event(O)) +function lifecyclesToEvents(class_declaration) { + wrapMethod(class_declaration.prototype, "connectedCallback", function(target, thisArg, detail) { + target.apply(thisArg, detail); + thisArg.dispatchEvent(new Event(evc)); + }); + wrapMethod(class_declaration.prototype, "disconnectedCallback", function(target, thisArg, detail) { + target.apply(thisArg, detail); + (globalThis.queueMicrotask || setTimeout)( + () => !thisArg.isConnected && thisArg.dispatchEvent(new Event(evd)) ); - }), J(t.prototype, "attributeChangedCallback", function(e, n, r) { - let [o, , c] = r; - n.dispatchEvent(new CustomEvent(M, { - detail: [o, c] - })), e.apply(n, r); - }), t.prototype[C] = !0, t; + }); + wrapMethod(class_declaration.prototype, "attributeChangedCallback", function(target, thisArg, detail) { + const [attribute, , value] = detail; + thisArg.dispatchEvent(new CustomEvent(eva, { + detail: [attribute, value] + })); + target.apply(thisArg, detail); + }); + class_declaration.prototype[keyLTE] = true; + return class_declaration; } -function J(t, e, n) { - t[e] = new Proxy(t[e] || (() => { - }), { apply: n }); +function wrapMethod(obj, method, apply) { + obj[method] = new Proxy(obj[method] || (() => { + }), { apply }); } -function _t(t) { - return F(t, (e, n) => e.getAttribute(n)); +function observedAttributes2(instance) { + return observedAttributes(instance, (i, n) => i.getAttribute(n)); } // src/events.js -function Qt(t, e, n) { - return e || (e = {}), function(o, ...c) { - n && (c.unshift(o), o = typeof n == "function" ? n() : n); - let i = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e); - return o.dispatchEvent(i); +function dispatchEvent(name, options, host) { + if (!options) options = {}; + return function dispatch(element, ...d) { + if (host) { + d.unshift(element); + element = typeof host === "function" ? host() : host; + } + const event = d.length ? new CustomEvent(name, Object.assign({ detail: d[0] }, options)) : new Event(name, options); + return element.dispatchEvent(event); }; } -function w(t, e, n) { - return function(o) { - return o.addEventListener(t, e, n), o; +function on(event, listener, options) { + return function registerElement(element) { + element.addEventListener(event, listener, options); + return element; }; } -var it = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 }); -w.connected = function(t, e) { - return e = it(e), function(r) { - return r.addEventListener(_, t, e), r[C] ? r : r.isConnected ? (r.dispatchEvent(new Event(_)), r) : (q(e.signal, () => D.offConnected(r, t)) && D.onConnected(r, t), r); +var lifeOptions = (obj) => Object.assign({}, typeof obj === "object" ? obj : null, { once: true }); +on.connected = function(listener, options) { + options = lifeOptions(options); + return function registerElement(element) { + element.addEventListener(evc, listener, options); + if (element[keyLTE]) return element; + if (element.isConnected) return element.dispatchEvent(new Event(evc)), element; + const c = onAbort(options.signal, () => c_ch_o.offConnected(element, listener)); + if (c) c_ch_o.onConnected(element, listener); + return element; }; }; -w.disconnected = function(t, e) { - return e = it(e), function(r) { - return r.addEventListener(O, t, e), r[C] || q(e.signal, () => D.offDisconnected(r, t)) && D.onDisconnected(r, t), r; +on.disconnected = function(listener, options) { + options = lifeOptions(options); + return function registerElement(element) { + element.addEventListener(evd, listener, options); + if (element[keyLTE]) return element; + const c = onAbort(options.signal, () => c_ch_o.offDisconnected(element, listener)); + if (c) c_ch_o.onDisconnected(element, listener); + return element; }; }; -var Z = /* @__PURE__ */ new WeakMap(); -w.disconnectedAsAbort = function(t) { - if (Z.has(t)) - return Z.get(t); - let e = new AbortController(); - return Z.set(t, e), t(w.disconnected(() => e.abort())), e; +var store_abort = /* @__PURE__ */ new WeakMap(); +on.disconnectedAsAbort = function(host) { + if (store_abort.has(host)) return store_abort.get(host); + const a = new AbortController(); + store_abort.set(host, a); + host(on.disconnected(() => a.abort())); + return a; }; -var At = /* @__PURE__ */ new WeakSet(); -w.attributeChanged = function(t, e) { - return typeof e != "object" && (e = {}), function(r) { - if (r.addEventListener(M, t, e), r[C] || At.has(r) || !d.M) - return r; - let o = new d.M(function(i) { - for (let { attributeName: a, target: h } of i) - h.dispatchEvent( - new CustomEvent(M, { detail: [a, h.getAttribute(a)] }) +var els_attribute_store = /* @__PURE__ */ new WeakSet(); +on.attributeChanged = function(listener, options) { + if (typeof options !== "object") + options = {}; + return function registerElement(element) { + element.addEventListener(eva, listener, options); + if (element[keyLTE] || els_attribute_store.has(element)) + return element; + if (!enviroment.M) return element; + const observer = new enviroment.M(function(mutations) { + for (const { attributeName, target } of mutations) + target.dispatchEvent( + new CustomEvent(eva, { detail: [attributeName, target.getAttribute(attributeName)] }) ); }); - return q(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r; + const c = onAbort(options.signal, () => observer.disconnect()); + if (c) observer.observe(element, { attributes: true }); + return element; }; }; // src/signals-lib.js -var p = "__dde_signal"; -function z(t) { +var mark = "__dde_signal"; +function isSignal(candidate) { try { - return T(t, p); - } catch { - return !1; + return hasOwn(candidate, mark); + } catch (e) { + return false; } } -var H = [], g = /* @__PURE__ */ new WeakMap(); -function E(t, e) { - if (typeof t != "function") - return st(!1, t, e); - if (z(t)) - return t; - let n = st(!0), r = function() { - let [o, ...c] = g.get(r); - if (g.set(r, /* @__PURE__ */ new Set([o])), H.push(r), dt(n, t()), H.pop(), !c.length) - return; - let i = g.get(r); - for (let a of c) - i.has(a) || L(a, r); +var stack_watch = []; +var deps = /* @__PURE__ */ new WeakMap(); +function signal(value, actions) { + if (typeof value !== "function") + return create(false, value, actions); + if (isSignal(value)) return value; + const out = create(true); + const contextReWatch = function() { + const [origin, ...deps_old] = deps.get(contextReWatch); + deps.set(contextReWatch, /* @__PURE__ */ new Set([origin])); + stack_watch.push(contextReWatch); + write(out, value()); + stack_watch.pop(); + if (!deps_old.length) return; + const deps_curr = deps.get(contextReWatch); + for (const dep_signal of deps_old) { + if (deps_curr.has(dep_signal)) continue; + removeSignalListener(dep_signal, contextReWatch); + } }; - return g.set(n[p], r), g.set(r, /* @__PURE__ */ new Set([n])), r(), n; + deps.set(out[mark], contextReWatch); + deps.set(contextReWatch, /* @__PURE__ */ new Set([out])); + contextReWatch(); + return out; } -E.action = function(t, e, ...n) { - let r = t[p], { actions: o } = r; - if (!o || !(e in o)) - throw new Error(`'${t}' has no action with name '${e}'!`); - if (o[e].apply(r, n), r.skip) - return delete r.skip; - r.listeners.forEach((c) => c(r.value)); +signal.action = function(s, name, ...a) { + const M = s[mark], { actions } = M; + if (!actions || !(name in actions)) + throw new Error(`'${s}' has no action with name '${name}'!`); + actions[name].apply(M, a); + if (M.skip) return delete M.skip; + M.listeners.forEach((l) => l(M.value)); }; -E.on = function t(e, n, r = {}) { - let { signal: o } = r; - if (!(o && o.aborted)) { - if (Array.isArray(e)) - return e.forEach((c) => t(c, n, r)); - Q(e, n), o && o.addEventListener("abort", () => L(e, n)); - } +signal.on = function on2(s, listener, options = {}) { + const { signal: as } = options; + if (as && as.aborted) return; + if (Array.isArray(s)) return s.forEach((s2) => on2(s2, listener, options)); + addSignalListener(s, listener); + if (as) as.addEventListener("abort", () => removeSignalListener(s, listener)); }; -E.symbols = { +signal.symbols = { //signal: mark, onclear: Symbol.for("Signal.onclear") }; -E.clear = function(...t) { - for (let n of t) { - let r = n[p]; - r && (delete n.toJSON, r.onclear.forEach((o) => o.call(r)), e(n, r), delete n[p]); +signal.clear = function(...signals2) { + for (const s of signals2) { + const M = s[mark]; + if (!M) continue; + delete s.toJSON; + M.onclear.forEach((f) => f.call(M)); + clearListDeps(s, M); + delete s[mark]; } - function e(n, r) { - r.listeners.forEach((o) => { - if (r.listeners.delete(o), !g.has(o)) - return; - let c = g.get(o); - c.delete(n), !(c.size > 1) && (n.clear(...c), g.delete(o)); + function clearListDeps(s, o) { + o.listeners.forEach((l) => { + o.listeners.delete(l); + if (!deps.has(l)) return; + const ls = deps.get(l); + ls.delete(s); + if (ls.size > 1) return; + s.clear(...ls); + deps.delete(l); }); } }; -var R = "__dde_reactive"; -E.el = function(t, e) { - let n = j.mark({ type: "reactive" }, !0), r = n.end, o = d.D.createDocumentFragment(); - o.append(n, r); - let { current: c } = m, i = {}, a = (h) => { - if (!n.parentNode || !r.parentNode) - return L(t, a); - let v = i; - i = {}, m.push(c); - let l = e(h, function(u, f) { - let b; - return T(v, u) ? (b = v[u], delete v[u]) : b = f(), i[u] = b, b; +var key_reactive = "__dde_reactive"; +signal.el = function(s, map) { + const mark_start = createElement.mark({ type: "reactive" }, true); + const mark_end = mark_start.end; + const out = enviroment.D.createDocumentFragment(); + out.append(mark_start, mark_end); + const { current } = scope; + let cache = {}; + const reRenderReactiveElement = (v) => { + if (!mark_start.parentNode || !mark_end.parentNode) + return removeSignalListener(s, reRenderReactiveElement); + const cache_tmp = cache; + cache = {}; + scope.push(current); + let els = map(v, function useCache(key, fun) { + let value; + if (hasOwn(cache_tmp, key)) { + value = cache_tmp[key]; + delete cache_tmp[key]; + } else + value = fun(); + cache[key] = value; + return value; }); - m.pop(), Array.isArray(l) || (l = [l]); - let x = document.createComment(""); - l.push(x), n.after(...l); - let y; - for (; (y = x.nextSibling) && y !== r; ) - y.remove(); - x.remove(), n.isConnected && St(c.host()); + scope.pop(); + if (!Array.isArray(els)) + els = [els]; + const el_start_rm = document.createComment(""); + els.push(el_start_rm); + mark_start.after(...els); + let el_r; + while ((el_r = el_start_rm.nextSibling) && el_r !== mark_end) + el_r.remove(); + el_start_rm.remove(); + if (mark_start.isConnected) + requestCleanUpReactives(current.host()); }; - return Q(t, a), ft(t, a, n, e), a(t()), o; + addSignalListener(s, reRenderReactiveElement); + removeSignalsFromElements(s, reRenderReactiveElement, mark_start, map); + reRenderReactiveElement(s()); + return out; }; -function St(t) { - !t || !t[R] || (requestIdleCallback || setTimeout)(function() { - t[R] = t[R].filter(([e, n]) => n.isConnected ? !0 : (L(...e), !1)); +function requestCleanUpReactives(host) { + if (!host || !host[key_reactive]) return; + (requestIdleCallback || setTimeout)(function() { + host[key_reactive] = host[key_reactive].filter(([s, el]) => el.isConnected ? true : (removeSignalListener(...s), false)); }); } -var Ct = { - _set(t) { - this.value = t; +var observedAttributeActions = { + _set(value) { + this.value = value; } }; -function Ot(t) { - return function(e, n) { - let r = (...c) => c.length ? e.setAttribute(n, ...c) : K(r), o = at(r, e.getAttribute(n), Ct); - return t[n] = o, o; +function observedAttribute(store) { + return function(instance, name) { + const varS = (...args) => !args.length ? read(varS) : instance.setAttribute(name, ...args); + const out = toSignal(varS, instance.getAttribute(name), observedAttributeActions); + store[name] = out; + return out; }; } -var G = "__dde_attributes"; -E.observedAttributes = function(t) { - let e = t[G] = {}, n = F(t, Ot(e)); - return w.attributeChanged(function({ detail: o }) { +var key_attributes = "__dde_attributes"; +signal.observedAttributes = function(element) { + const store = element[key_attributes] = {}; + const attrs = observedAttributes(element, observedAttribute(store)); + on.attributeChanged(function attributeChangeToSignal({ detail }) { /*! This maps attributes to signals (`S.observedAttributes`). * Investigate `__dde_attributes` key of the element.*/ - let [c, i] = o, a = this[G][c]; - if (a) - return E.action(a, "_set", i); - })(t), w.disconnected(function() { + const [name, value] = detail; + const curr = this[key_attributes][name]; + if (curr) return signal.action(curr, "_set", value); + })(element); + on.disconnected(function() { /*! This removes all signals mapped to attributes (`S.observedAttributes`). * Investigate `__dde_attributes` key of the element.*/ - E.clear(...Object.values(this[G])); - })(t), n; + signal.clear(...Object.values(this[key_attributes])); + })(element); + return attrs; }; -var ut = { - isSignal: z, - processReactiveAttribute(t, e, n, r) { - if (!z(n)) - return n; - let o = (c) => { - if (!t.isConnected) - return L(n, o); - r(e, c); +var signals_config = { + isSignal, + processReactiveAttribute(element, key, attrs, set) { + if (!isSignal(attrs)) return attrs; + const l = (attr) => { + if (!element.isConnected) + return removeSignalListener(attrs, l); + set(key, attr); }; - return Q(n, o), ft(n, o, t, e), n(); + addSignalListener(attrs, l); + removeSignalsFromElements(attrs, l, element, key); + return attrs(); } }; -function ft(t, e, ...n) { - let { current: r } = m; - r.prevent || r.host(function(o) { - o[R] || (o[R] = [], w.disconnected( - () => ( - /*! - * Clears all Signals listeners added in the current scope/host (`S.el`, `assign`, …?). - * You can investigate the `__dde_reactive` key of the element. - * */ - o[R].forEach(([[c, i]]) => L(c, i, c[p] && c[p].host && c[p].host() === o)) - ) - )(o)), o[R].push([[t, e], ...n]); +function removeSignalsFromElements(s, listener, ...notes) { + const { current } = scope; + if (current.prevent) return; + current.host(function(element) { + if (!element[key_reactive]) { + element[key_reactive] = []; + on.disconnected( + () => ( + /*! + * Clears all Signals listeners added in the current scope/host (`S.el`, `assign`, …?). + * You can investigate the `__dde_reactive` key of the element. + * */ + element[key_reactive].forEach(([[s2, listener2]]) => removeSignalListener(s2, listener2, s2[mark] && s2[mark].host && s2[mark].host() === element)) + ) + )(element); + } + element[key_reactive].push([[s, listener], ...notes]); }); } -function st(t, e, n) { - let r = t ? () => K(r) : (...o) => o.length ? dt(r, ...o) : K(r); - return at(r, e, n, t); +function create(is_readonly, value, actions) { + const varS = is_readonly ? () => read(varS) : (...value2) => value2.length ? write(varS, ...value2) : read(varS); + return toSignal(varS, value, actions, is_readonly); } -var Dt = Object.assign(/* @__PURE__ */ Object.create(null), { +var protoSigal = Object.assign(/* @__PURE__ */ Object.create(null), { stopPropagation() { - this.skip = !0; + this.skip = true; } -}), V = class extends Error { +}); +var SignalDefined = class extends Error { constructor() { super(); - let [e, ...n] = this.stack.split(` -`), r = e.slice(e.indexOf("@"), e.indexOf(".js:") + 4); - this.stack = n.find((o) => !o.includes(r)); + const [curr, ...rest] = this.stack.split("\n"); + const curr_file = curr.slice(curr.indexOf("@"), curr.indexOf(".js:") + 4); + this.stack = rest.find((l) => !l.includes(curr_file)); } }; -function at(t, e, n, r = !1) { - let o = []; - X(n) !== "[object Object]" && (n = {}); - let { onclear: c } = E.symbols; - n[c] && (o.push(n[c]), delete n[c]); - let { host: i } = m; - return Reflect.defineProperty(t, p, { - value: { - value: e, - actions: n, - onclear: o, - host: i, - listeners: /* @__PURE__ */ new Set(), - defined: new V().stack, - readonly: r - }, - enumerable: !1, - writable: !1, - configurable: !0 - }), t.toJSON = () => t(), t.valueOf = () => t[p] && t[p].value, Object.setPrototypeOf(t[p], Dt), t; -} -function Rt() { - return H[H.length - 1]; -} -function K(t) { - if (!t[p]) - return; - let { value: e, listeners: n } = t[p], r = Rt(); - return r && n.add(r), g.has(r) && g.get(r).add(t), e; -} -function dt(t, e, n) { - if (!t[p]) - return; - let r = t[p]; - if (!(!n && r.value === e)) - return r.value = e, r.listeners.forEach((o) => o(e)), e; -} -function Q(t, e) { - if (t[p]) - return t[p].listeners.add(e); -} -function L(t, e, n) { - let r = t[p]; - if (!r) - return; - let o = r.listeners.delete(e); - if (n && !r.listeners.size) { - if (E.clear(t), !g.has(r)) - return o; - let c = g.get(r); - if (!g.has(c)) - return o; - g.get(c).forEach((i) => L(i, c, !0)); +function toSignal(s, value, actions, readonly = false) { + const onclear = []; + if (typeOf(actions) !== "[object Object]") + actions = {}; + const { onclear: ocs } = signal.symbols; + if (actions[ocs]) { + onclear.push(actions[ocs]); + delete actions[ocs]; } - return o; + const { host } = scope; + Reflect.defineProperty(s, mark, { + value: { + value, + actions, + onclear, + host, + listeners: /* @__PURE__ */ new Set(), + defined: new SignalDefined().stack, + readonly + }, + enumerable: false, + writable: false, + configurable: true + }); + s.toJSON = () => s(); + s.valueOf = () => s[mark] && s[mark].value; + Object.setPrototypeOf(s[mark], protoSigal); + return s; +} +function currentContext() { + return stack_watch[stack_watch.length - 1]; +} +function read(s) { + if (!s[mark]) return; + const { value, listeners } = s[mark]; + const context = currentContext(); + if (context) listeners.add(context); + if (deps.has(context)) deps.get(context).add(s); + return value; +} +function write(s, value, force) { + if (!s[mark]) return; + const M = s[mark]; + if (!force && M.value === value) return; + M.value = value; + M.listeners.forEach((l) => l(value)); + return value; +} +function addSignalListener(s, listener) { + if (!s[mark]) return; + return s[mark].listeners.add(listener); +} +function removeSignalListener(s, listener, clear_when_empty) { + const M = s[mark]; + if (!M) return; + const out = M.listeners.delete(listener); + if (clear_when_empty && !M.listeners.size) { + signal.clear(s); + if (!deps.has(M)) return out; + const c = deps.get(M); + if (!deps.has(c)) return out; + deps.get(c).forEach((sig) => removeSignalListener(sig, c, true)); + } + return out; } // signals.js -B(ut); +registerReactivity(signals_config); export { - E as S, - P as assign, - nt as assignAttribute, - ht as chainableAppend, - gt as classListDeclarative, - j as createElement, - qt as createElementNS, - Zt as customElementRender, - yt as customElementWithDDE, - Qt as dispatchEvent, - j as el, - qt as elNS, - vt as elementAttribute, - Ft as empty, - z as isSignal, - yt as lifecyclesToEvents, - _t as observedAttributes, - w as on, - B as registerReactivity, - m as scope, - E as signal, - Wt as simulateSlots + signal as S, + assign, + assignAttribute, + chainableAppend, + classListDeclarative, + createElement, + createElementNS, + customElementRender, + lifecyclesToEvents as customElementWithDDE, + dispatchEvent, + createElement as el, + createElementNS as elNS, + elementAttribute, + empty, + isSignal, + lifecyclesToEvents, + observedAttributes2 as observedAttributes, + on, + registerReactivity, + scope, + signal, + simulateSlots }; diff --git a/dist/esm.d.ts b/dist/esm.d.ts index 3cc6e74..4656d84 100644 --- a/dist/esm.d.ts +++ b/dist/esm.d.ts @@ -47,7 +47,7 @@ type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagName type textContent= string | ddeSignal; export function el< TAG extends keyof ExtendedHTMLElementTagNameMap, - EL extends (TAG extends keyof ExtendedHTMLElementTagNameMap ? ExtendedHTMLElementTagNameMap[TAG] : HTMLElement) + EL extends ExtendedHTMLElementTagNameMap[TAG] >( tag_name: TAG, attrs?: ElementAttributes | textContent, @@ -58,7 +58,7 @@ export function el( ): ddeDocumentFragment export function el( tag_name: string, - attrs?: ElementAttributes, + attrs?: ElementAttributes | textContent, ...addons: ddeElementAddon[] ): ddeHTMLElement @@ -101,7 +101,18 @@ export function elNS( export { elNS as createElementNS } export function chainableAppend(el: EL): EL; -export function simulateSlots(el: EL): EL +/** + * Mapper function (optional). Pass for coppying attributes, this is NOT implemented by {@link simulateSlots} itself! + * */ +type simulateSlotsMapper= (body: HTMLSlotElement, el: HTMLElement)=> void; +/** Simulate slots for ddeComponents */ +export function simulateSlots(root: EL, mapper?: simulateSlotsMapper): EL +/** + * Simulate slots in Custom Elements without using `shadowRoot`. + * @param el Custom Element root element + * @param body Body of the custom element + * */ +export function simulateSlots(el: HTMLElement, body: EL, mapper?: simulateSlotsMapper): EL export function dispatchEvent(name: keyof DocumentEventMap | string, options?: EventInit): (element: SupportedElement, data?: any)=> void; diff --git a/dist/esm.js b/dist/esm.js index 2df90a5..23654bc 100644 --- a/dist/esm.js +++ b/dist/esm.js @@ -1,45 +1,50 @@ // src/signals-common.js -var C = { - isSignal(t) { - return !1; +var signals_global = { + isSignal(attributes) { + return false; }, - processReactiveAttribute(t, e, n, r) { - return n; + processReactiveAttribute(obj, key, attr, set) { + return attr; } }; -function V(t, e = !0) { - return e ? Object.assign(C, t) : (Object.setPrototypeOf(t, C), t); +function registerReactivity(def, global = true) { + if (global) return Object.assign(signals_global, def); + Object.setPrototypeOf(def, signals_global); + return def; } -function L(t) { - return C.isPrototypeOf(t) && t !== C ? t : C; +function signals(_this) { + return signals_global.isPrototypeOf(_this) && _this !== signals_global ? _this : signals_global; } // src/helpers.js -var q = (...t) => Object.prototype.hasOwnProperty.call(...t); -function E(t) { - return typeof t > "u"; +var hasOwn = (...a) => Object.prototype.hasOwnProperty.call(...a); +function isUndef(value) { + return typeof value === "undefined"; } -function N(t, e) { - if (!t || !(t instanceof AbortSignal)) - return !0; - if (!t.aborted) - return t.addEventListener("abort", e), function() { - t.removeEventListener("abort", e); - }; +function onAbort(signal, listener) { + if (!signal || !(signal instanceof AbortSignal)) + return true; + if (signal.aborted) + return; + signal.addEventListener("abort", listener); + return function cleanUp() { + signal.removeEventListener("abort", listener); + }; } -function F(t, e) { - let { observedAttributes: n = [] } = t.constructor; - return n.reduce(function(r, o) { - return r[J(o)] = e(t, o), r; +function observedAttributes(instance, observedAttribute) { + const { observedAttributes: observedAttributes3 = [] } = instance.constructor; + return observedAttributes3.reduce(function(out, name) { + out[kebabToCamel(name)] = observedAttribute(instance, name); + return out; }, {}); } -function J(t) { - return t.replace(/-./g, (e) => e[1].toUpperCase()); +function kebabToCamel(name) { + return name.replace(/-./g, (x) => x[1].toUpperCase()); } // src/dom-common.js -var a = { - setDeleteAttr: K, +var enviroment = { + setDeleteAttr, ssr: "", D: globalThis.document, F: globalThis.DocumentFragment, @@ -47,422 +52,545 @@ var a = { S: globalThis.SVGElement, M: globalThis.MutationObserver }; -function K(t, e, n) { - if (Reflect.set(t, e, n), !!E(n)) { - if (Reflect.deleteProperty(t, e), t instanceof a.H && t.getAttribute(e) === "undefined") - return t.removeAttribute(e); - if (Reflect.get(t, e) === "undefined") - return Reflect.set(t, e, ""); - } +function setDeleteAttr(obj, prop, val) { + Reflect.set(obj, prop, val); + if (!isUndef(val)) return; + Reflect.deleteProperty(obj, prop); + if (obj instanceof enviroment.H && obj.getAttribute(prop) === "undefined") + return obj.removeAttribute(prop); + if (Reflect.get(obj, prop) === "undefined") + return Reflect.set(obj, prop, ""); } -var x = "__dde_lifecyclesToEvents", g = "dde:connected", y = "dde:disconnected", D = "dde:attributeChanged"; +var keyLTE = "__dde_lifecyclesToEvents"; +var evc = "dde:connected"; +var evd = "dde:disconnected"; +var eva = "dde:attributeChanged"; // src/dom.js -var v = [{ +var scopes = [{ get scope() { - return a.D.body; + return enviroment.D.body; }, - host: (t) => t ? t(a.D.body) : a.D.body, - prevent: !0 -}], S = { + host: (c) => c ? c(enviroment.D.body) : enviroment.D.body, + prevent: true +}]; +var scope = { get current() { - return v[v.length - 1]; + return scopes[scopes.length - 1]; }, get host() { return this.current.host; }, preventDefault() { - let { current: t } = this; - return t.prevent = !0, t; + const { current } = this; + current.prevent = true; + return current; }, get state() { - return [...v]; + return [...scopes]; }, - push(t = {}) { - return v.push(Object.assign({}, this.current, { prevent: !1 }, t)); + push(s = {}) { + return scopes.push(Object.assign({}, this.current, { prevent: false }, s)); }, pushRoot() { - return v.push(v[0]); + return scopes.push(scopes[0]); }, pop() { - if (v.length !== 1) - return v.pop(); + if (scopes.length === 1) return; + return scopes.pop(); } }; -function $(...t) { - return this.appendOriginal(...t), this; +function append(...els) { + this.appendOriginal(...els); + return this; } -function Q(t) { - return t.append === $ || (t.appendOriginal = t.append, t.append = $), t; +function chainableAppend(el) { + if (el.append === append) return el; + el.appendOriginal = el.append; + el.append = append; + return el; } -var T; -function k(t, e, ...n) { - let r = L(this), o = 0, c, f; - switch ((Object(e) !== e || r.isSignal(e)) && (e = { textContent: e }), !0) { - case typeof t == "function": { - o = 1, S.push({ scope: t, host: (...b) => b.length ? (o === 1 ? n.unshift(...b) : b.forEach((l) => l(f)), void 0) : f }), c = t(e || void 0); - let d = c instanceof a.F; - if (c.nodeName === "#comment") - break; - let p = k.mark({ +var namespace; +function createElement(tag, attributes, ...addons) { + const s = signals(this); + let scoped = 0; + let el, el_host; + if (Object(attributes) !== attributes || s.isSignal(attributes)) + attributes = { textContent: attributes }; + switch (true) { + case typeof tag === "function": { + scoped = 1; + scope.push({ scope: tag, host: (...c) => c.length ? (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0) : el_host }); + el = tag(attributes || void 0); + const is_fragment = el instanceof enviroment.F; + if (el.nodeName === "#comment") break; + const el_mark = createElement.mark({ type: "component", - name: t.name, - host: d ? "this" : "parentElement" + name: tag.name, + host: is_fragment ? "this" : "parentElement" }); - c.prepend(p), d && (f = p); + el.prepend(el_mark); + if (is_fragment) el_host = el_mark; break; } - case t === "#text": - c = O.call(this, a.D.createTextNode(""), e); + case tag === "#text": + el = assign.call(this, enviroment.D.createTextNode(""), attributes); break; - case (t === "<>" || !t): - c = O.call(this, a.D.createDocumentFragment(), e); + case (tag === "<>" || !tag): + el = assign.call(this, enviroment.D.createDocumentFragment(), attributes); break; - case !!T: - c = O.call(this, a.D.createElementNS(T, t), e); + case Boolean(namespace): + el = assign.call(this, enviroment.D.createElementNS(namespace, tag), attributes); break; - case !c: - c = O.call(this, a.D.createElement(t), e); + case !el: + el = assign.call(this, enviroment.D.createElement(tag), attributes); } - return Q(c), f || (f = c), n.forEach((d) => d(f)), o && S.pop(), o = 2, c; + chainableAppend(el); + if (!el_host) el_host = el; + addons.forEach((c) => c(el_host)); + if (scoped) scope.pop(); + scoped = 2; + return el; } -function bt(t, e = t, n = void 0) { - let r = Symbol.for("default"), o = Array.from(e.querySelectorAll("slot")).reduce((f, d) => Reflect.set(f, d.name || r, d) && f, {}), c = q(o, r); - if (t.append = new Proxy(t.append, { - apply(f, d, p) { - if (!p.length) - return t; - let b = a.D.createDocumentFragment(); - for (let l of p) { - if (!l || !l.slot) { - c && b.appendChild(l); +function simulateSlots(element, root, mapper) { + if (typeof root !== "object") { + mapper = root; + root = element; + } + const _default = Symbol.for("default"); + const slots = Array.from(root.querySelectorAll("slot")).reduce((out, curr) => Reflect.set(out, curr.name || _default, curr) && out, {}); + const has_d = hasOwn(slots, _default); + element.append = new Proxy(element.append, { + apply(orig, _, els) { + if (els[0] === root) return orig.apply(element, els); + if (!els.length) return element; + const d = enviroment.D.createDocumentFragment(); + for (const el of els) { + if (!el || !el.slot) { + if (has_d) d.append(el); continue; } - let A = l.slot, _ = o[A]; - tt(l, "remove", "slot"), _ && (X(_, l, n), Reflect.deleteProperty(o, A)); + const name = el.slot; + const slot = slots[name]; + elementAttribute(el, "remove", "slot"); + if (!slot) continue; + simulateSlotReplace(slot, el, mapper); + Reflect.deleteProperty(slots, name); } - return c && (o[r].replaceWith(b), Reflect.deleteProperty(o, r)), t.append = f, t; + if (has_d) { + slots[_default].replaceWith(d); + Reflect.deleteProperty(slots, _default); + } + element.append = orig; + return element; } - }), t !== e) { - let f = Array.from(t.childNodes); - f.forEach((d) => d.remove()), t.append(...f); + }); + if (element !== root) { + const els = Array.from(element.childNodes); + els.forEach((el) => el.remove()); + element.append(...els); } - return e; + return root; } -function X(t, e, n) { - n && n(t, e); +function simulateSlotReplace(slot, element, mapper) { + if (mapper) mapper(slot, element); try { - t.replaceWith(O(e, { className: [e.className, t.className], dataset: { ...t.dataset } })); - } catch { - t.replaceWith(e); + slot.replaceWith(assign(element, { className: [element.className, slot.className], dataset: { ...slot.dataset } })); + } catch (_) { + slot.replaceWith(element); } } -k.mark = function(t, e = !1) { - t = Object.entries(t).map(([o, c]) => o + `="${c}"`).join(" "); - let n = e ? "" : "/", r = a.D.createComment(``); - return e && (r.end = a.D.createComment("")), r; +createElement.mark = function(attrs, is_open = false) { + attrs = Object.entries(attrs).map(([n, v]) => n + `="${v}"`).join(" "); + const end = is_open ? "" : "/"; + const out = enviroment.D.createComment(``); + if (is_open) out.end = enviroment.D.createComment(""); + return out; }; -function gt(t) { - let e = this; - return function(...r) { - T = t; - let o = k.call(e, ...r); - return T = void 0, o; +function createElementNS(ns) { + const _this = this; + return function createElementNSCurried(...rest) { + namespace = ns; + const el = createElement.call(_this, ...rest); + namespace = void 0; + return el; }; } -var P = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: U } = a; -function O(t, ...e) { - if (!e.length) - return t; - P.set(t, B(t, this)); - for (let [n, r] of Object.entries(Object.assign({}, ...e))) - z.call(this, t, n, r); - return P.delete(t), t; +var assign_context = /* @__PURE__ */ new WeakMap(); +var { setDeleteAttr: setDeleteAttr2 } = enviroment; +function assign(element, ...attributes) { + if (!attributes.length) return element; + assign_context.set(element, assignContext(element, this)); + for (const [key, value] of Object.entries(Object.assign({}, ...attributes))) + assignAttribute.call(this, element, key, value); + assign_context.delete(element); + return element; } -function z(t, e, n) { - let { setRemoveAttr: r, s: o } = B(t, this), c = this; - n = o.processReactiveAttribute( - t, - e, - n, - (d, p) => z.call(c, t, d, p) +function assignAttribute(element, key, value) { + const { setRemoveAttr, s } = assignContext(element, this); + const _this = this; + value = s.processReactiveAttribute( + element, + key, + value, + (key2, value2) => assignAttribute.call(_this, element, key2, value2) ); - let [f] = e; - if (f === "=") - return r(e.slice(1), n); - if (f === ".") - return H(t, e.slice(1), n); - if (/(aria|data)([A-Z])/.test(e)) - return e = e.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(), r(e, n); - switch (e === "className" && (e = "class"), e) { - case "xlink:href": - return r(e, n, "http://www.w3.org/1999/xlink"); - case "textContent": - return U(t, e, n); - case "style": - if (typeof n != "object") - break; - case "dataset": - return M(o, n, H.bind(null, t[e])); - case "ariaset": - return M(o, n, (d, p) => r("aria-" + d, p)); - case "classList": - return Y.call(c, t, n); + const [k] = key; + if ("=" === k) return setRemoveAttr(key.slice(1), value); + if ("." === k) return setDelete(element, key.slice(1), value); + if (/(aria|data)([A-Z])/.test(key)) { + key = key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(); + return setRemoveAttr(key, value); } - return et(t, e) ? U(t, e, n) : r(e, n); + if ("className" === key) key = "class"; + switch (key) { + case "xlink:href": + return setRemoveAttr(key, value, "http://www.w3.org/1999/xlink"); + case "textContent": + return setDeleteAttr2(element, key, value); + case "style": + if (typeof value !== "object") break; + /* falls through */ + case "dataset": + return forEachEntries(s, value, setDelete.bind(null, element[key])); + case "ariaset": + return forEachEntries(s, value, (key2, val) => setRemoveAttr("aria-" + key2, val)); + case "classList": + return classListDeclarative.call(_this, element, value); + } + return isPropSetter(element, key) ? setDeleteAttr2(element, key, value) : setRemoveAttr(key, value); } -function B(t, e) { - if (P.has(t)) - return P.get(t); - let r = (t instanceof a.S ? rt : nt).bind(null, t, "Attribute"), o = L(e); - return { setRemoveAttr: r, s: o }; +function assignContext(element, _this) { + if (assign_context.has(element)) return assign_context.get(element); + const is_svg = element instanceof enviroment.S; + const setRemoveAttr = (is_svg ? setRemoveNS : setRemove).bind(null, element, "Attribute"); + const s = signals(_this); + return { setRemoveAttr, s }; } -function Y(t, e) { - let n = L(this); - return M( - n, - e, - (r, o) => t.classList.toggle(r, o === -1 ? void 0 : !!o) - ), t; +function classListDeclarative(element, toggle) { + const s = signals(this); + forEachEntries( + s, + toggle, + (class_name, val) => element.classList.toggle(class_name, val === -1 ? void 0 : Boolean(val)) + ); + return element; } -function vt(t) { - return Array.from(t.children).forEach((e) => e.remove()), t; +function empty(el) { + Array.from(el.children).forEach((el2) => el2.remove()); + return el; } -function tt(t, e, n, r) { - return t instanceof a.H ? t[e + "Attribute"](n, r) : t[e + "AttributeNS"](null, n, r); +function elementAttribute(element, op, key, value) { + if (element instanceof enviroment.H) + return element[op + "Attribute"](key, value); + return element[op + "AttributeNS"](null, key, value); } -function et(t, e) { - if (!(e in t)) - return !1; - let n = I(t, e); - return !E(n.set); +function isPropSetter(el, key) { + if (!(key in el)) return false; + const des = getPropDescriptor(el, key); + return !isUndef(des.set); } -function I(t, e) { - if (t = Object.getPrototypeOf(t), !t) - return {}; - let n = Object.getOwnPropertyDescriptor(t, e); - return n || I(t, e); +function getPropDescriptor(p, key) { + p = Object.getPrototypeOf(p); + if (!p) return {}; + const des = Object.getOwnPropertyDescriptor(p, key); + if (!des) return getPropDescriptor(p, key); + return des; } -function M(t, e, n) { - if (!(typeof e != "object" || e === null)) - return Object.entries(e).forEach(function([o, c]) { - o && (c = t.processReactiveAttribute(e, o, c, n), n(o, c)); - }); +function forEachEntries(s, obj, cb) { + if (typeof obj !== "object" || obj === null) return; + return Object.entries(obj).forEach(function process([key, val]) { + if (!key) return; + val = s.processReactiveAttribute(obj, key, val, cb); + cb(key, val); + }); } -function Z(t) { - return Array.isArray(t) ? t.filter(Boolean).join(" ") : t; +function attrArrToStr(attr) { + return Array.isArray(attr) ? attr.filter(Boolean).join(" ") : attr; } -function nt(t, e, n, r) { - return t[(E(r) ? "remove" : "set") + e](n, Z(r)); +function setRemove(obj, prop, key, val) { + return obj[(isUndef(val) ? "remove" : "set") + prop](key, attrArrToStr(val)); } -function rt(t, e, n, r, o = null) { - return t[(E(r) ? "remove" : "set") + e + "NS"](o, n, Z(r)); +function setRemoveNS(obj, prop, key, val, ns = null) { + return obj[(isUndef(val) ? "remove" : "set") + prop + "NS"](ns, key, attrArrToStr(val)); } -function H(t, e, n) { - if (Reflect.set(t, e, n), !!E(n)) - return Reflect.deleteProperty(t, e); +function setDelete(obj, key, val) { + Reflect.set(obj, key, val); + if (!isUndef(val)) return; + return Reflect.deleteProperty(obj, key); } // src/events-observer.js -var w = a.M ? ot() : new Proxy({}, { +var c_ch_o = enviroment.M ? connectionsChangesObserverConstructor() : new Proxy({}, { get() { return () => { }; } }); -function ot() { - let t = /* @__PURE__ */ new Map(), e = !1, n = (i) => function(u) { - for (let s of u) - if (s.type === "childList") { - if (l(s.addedNodes, !0)) { - i(); - continue; - } - A(s.removedNodes, !0) && i(); +function connectionsChangesObserverConstructor() { + const store = /* @__PURE__ */ new Map(); + let is_observing = false; + const observerListener = (stop2) => function(mutations) { + for (const mutation of mutations) { + if (mutation.type !== "childList") continue; + if (observerAdded(mutation.addedNodes, true)) { + stop2(); + continue; } - }, r = new a.M(n(d)); - return { - observe(i) { - let u = new a.M(n(() => { - })); - return u.observe(i, { childList: !0, subtree: !0 }), () => u.disconnect(); - }, - onConnected(i, u) { - f(); - let s = c(i); - s.connected.has(u) || (s.connected.add(u), s.length_c += 1); - }, - offConnected(i, u) { - if (!t.has(i)) - return; - let s = t.get(i); - s.connected.has(u) && (s.connected.delete(u), s.length_c -= 1, o(i, s)); - }, - onDisconnected(i, u) { - f(); - let s = c(i); - s.disconnected.has(u) || (s.disconnected.add(u), s.length_d += 1); - }, - offDisconnected(i, u) { - if (!t.has(i)) - return; - let s = t.get(i); - s.disconnected.has(u) && (s.disconnected.delete(u), s.length_d -= 1, o(i, s)); + if (observerRemoved(mutation.removedNodes, true)) + stop2(); } }; - function o(i, u) { - u.length_c || u.length_d || (t.delete(i), d()); + const observer = new enviroment.M(observerListener(stop)); + return { + observe(element) { + const o = new enviroment.M(observerListener(() => { + })); + o.observe(element, { childList: true, subtree: true }); + return () => o.disconnect(); + }, + onConnected(element, listener) { + start(); + const listeners = getElementStore(element); + if (listeners.connected.has(listener)) return; + listeners.connected.add(listener); + listeners.length_c += 1; + }, + offConnected(element, listener) { + if (!store.has(element)) return; + const ls = store.get(element); + if (!ls.connected.has(listener)) return; + ls.connected.delete(listener); + ls.length_c -= 1; + cleanWhenOff(element, ls); + }, + onDisconnected(element, listener) { + start(); + const listeners = getElementStore(element); + if (listeners.disconnected.has(listener)) return; + listeners.disconnected.add(listener); + listeners.length_d += 1; + }, + offDisconnected(element, listener) { + if (!store.has(element)) return; + const ls = store.get(element); + if (!ls.disconnected.has(listener)) return; + ls.disconnected.delete(listener); + ls.length_d -= 1; + cleanWhenOff(element, ls); + } + }; + function cleanWhenOff(element, ls) { + if (ls.length_c || ls.length_d) + return; + store.delete(element); + stop(); } - function c(i) { - if (t.has(i)) - return t.get(i); - let u = { + function getElementStore(element) { + if (store.has(element)) return store.get(element); + const out = { connected: /* @__PURE__ */ new WeakSet(), length_c: 0, disconnected: /* @__PURE__ */ new WeakSet(), length_d: 0 }; - return t.set(i, u), u; + store.set(element, out); + return out; } - function f() { - e || (e = !0, r.observe(a.D.body, { childList: !0, subtree: !0 })); + function start() { + if (is_observing) return; + is_observing = true; + observer.observe(enviroment.D.body, { childList: true, subtree: true }); } - function d() { - !e || t.size || (e = !1, r.disconnect()); + function stop() { + if (!is_observing || store.size) return; + is_observing = false; + observer.disconnect(); } - function p() { - return new Promise(function(i) { - (requestIdleCallback || requestAnimationFrame)(i); + function requestIdle() { + return new Promise(function(resolve) { + (requestIdleCallback || requestAnimationFrame)(resolve); }); } - async function b(i) { - t.size > 30 && await p(); - let u = []; - if (!(i instanceof Node)) - return u; - for (let s of t.keys()) - s === i || !(s instanceof Node) || i.contains(s) && u.push(s); - return u; - } - function l(i, u) { - let s = !1; - for (let h of i) { - if (u && b(h).then(l), !t.has(h)) - continue; - let m = t.get(h); - m.length_c && (h.dispatchEvent(new Event(g)), m.connected = /* @__PURE__ */ new WeakSet(), m.length_c = 0, m.length_d || t.delete(h), s = !0); + async function collectChildren(element) { + if (store.size > 30) + await requestIdle(); + const out = []; + if (!(element instanceof Node)) return out; + for (const el of store.keys()) { + if (el === element || !(el instanceof Node)) continue; + if (element.contains(el)) + out.push(el); } - return s; + return out; } - function A(i, u) { - let s = !1; - for (let h of i) - u && b(h).then(A), !(!t.has(h) || !t.get(h).length_d) && ((globalThis.queueMicrotask || setTimeout)(_(h)), s = !0); - return s; + function observerAdded(addedNodes, is_root) { + let out = false; + for (const element of addedNodes) { + if (is_root) collectChildren(element).then(observerAdded); + if (!store.has(element)) continue; + const ls = store.get(element); + if (!ls.length_c) continue; + element.dispatchEvent(new Event(evc)); + ls.connected = /* @__PURE__ */ new WeakSet(); + ls.length_c = 0; + if (!ls.length_d) store.delete(element); + out = true; + } + return out; } - function _(i) { + function observerRemoved(removedNodes, is_root) { + let out = false; + for (const element of removedNodes) { + if (is_root) collectChildren(element).then(observerRemoved); + if (!store.has(element)) continue; + const ls = store.get(element); + if (!ls.length_d) continue; + (globalThis.queueMicrotask || setTimeout)(dispatchRemove(element)); + out = true; + } + return out; + } + function dispatchRemove(element) { return () => { - i.isConnected || (i.dispatchEvent(new Event(y)), t.delete(i)); + if (element.isConnected) return; + element.dispatchEvent(new Event(evd)); + store.delete(element); }; } } // src/customElement.js -function Dt(t, e, n, r = it) { - S.push({ - scope: t, - host: (...f) => f.length ? f.forEach((d) => d(t)) : t - }), typeof r == "function" && (r = r.call(t, t)); - let o = t[x]; - o || ct(t); - let c = n.call(t, r); - return o || t.dispatchEvent(new Event(g)), e.nodeType === 11 && typeof e.mode == "string" && t.addEventListener(y, w.observe(e), { once: !0 }), S.pop(), e.append(c); +function customElementRender(custom_element, target, render, props = observedAttributes2) { + scope.push({ + scope: custom_element, + host: (...c) => c.length ? c.forEach((c2) => c2(custom_element)) : custom_element + }); + if (typeof props === "function") props = props.call(custom_element, custom_element); + const is_lte = custom_element[keyLTE]; + if (!is_lte) lifecyclesToEvents(custom_element); + const out = render.call(custom_element, props); + if (!is_lte) custom_element.dispatchEvent(new Event(evc)); + if (target.nodeType === 11 && typeof target.mode === "string") + custom_element.addEventListener(evd, c_ch_o.observe(target), { once: true }); + scope.pop(); + return target.append(out); } -function ct(t) { - return W(t.prototype, "connectedCallback", function(e, n, r) { - e.apply(n, r), n.dispatchEvent(new Event(g)); - }), W(t.prototype, "disconnectedCallback", function(e, n, r) { - e.apply(n, r), (globalThis.queueMicrotask || setTimeout)( - () => !n.isConnected && n.dispatchEvent(new Event(y)) +function lifecyclesToEvents(class_declaration) { + wrapMethod(class_declaration.prototype, "connectedCallback", function(target, thisArg, detail) { + target.apply(thisArg, detail); + thisArg.dispatchEvent(new Event(evc)); + }); + wrapMethod(class_declaration.prototype, "disconnectedCallback", function(target, thisArg, detail) { + target.apply(thisArg, detail); + (globalThis.queueMicrotask || setTimeout)( + () => !thisArg.isConnected && thisArg.dispatchEvent(new Event(evd)) ); - }), W(t.prototype, "attributeChangedCallback", function(e, n, r) { - let [o, , c] = r; - n.dispatchEvent(new CustomEvent(D, { - detail: [o, c] - })), e.apply(n, r); - }), t.prototype[x] = !0, t; + }); + wrapMethod(class_declaration.prototype, "attributeChangedCallback", function(target, thisArg, detail) { + const [attribute, , value] = detail; + thisArg.dispatchEvent(new CustomEvent(eva, { + detail: [attribute, value] + })); + target.apply(thisArg, detail); + }); + class_declaration.prototype[keyLTE] = true; + return class_declaration; } -function W(t, e, n) { - t[e] = new Proxy(t[e] || (() => { - }), { apply: n }); +function wrapMethod(obj, method, apply) { + obj[method] = new Proxy(obj[method] || (() => { + }), { apply }); } -function it(t) { - return F(t, (e, n) => e.getAttribute(n)); +function observedAttributes2(instance) { + return observedAttributes(instance, (i, n) => i.getAttribute(n)); } // src/events.js -function _t(t, e, n) { - return e || (e = {}), function(o, ...c) { - n && (c.unshift(o), o = typeof n == "function" ? n() : n); - let f = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e); - return o.dispatchEvent(f); +function dispatchEvent(name, options, host) { + if (!options) options = {}; + return function dispatch(element, ...d) { + if (host) { + d.unshift(element); + element = typeof host === "function" ? host() : host; + } + const event = d.length ? new CustomEvent(name, Object.assign({ detail: d[0] }, options)) : new Event(name, options); + return element.dispatchEvent(event); }; } -function R(t, e, n) { - return function(o) { - return o.addEventListener(t, e, n), o; +function on(event, listener, options) { + return function registerElement(element) { + element.addEventListener(event, listener, options); + return element; }; } -var G = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 }); -R.connected = function(t, e) { - return e = G(e), function(r) { - return r.addEventListener(g, t, e), r[x] ? r : r.isConnected ? (r.dispatchEvent(new Event(g)), r) : (N(e.signal, () => w.offConnected(r, t)) && w.onConnected(r, t), r); +var lifeOptions = (obj) => Object.assign({}, typeof obj === "object" ? obj : null, { once: true }); +on.connected = function(listener, options) { + options = lifeOptions(options); + return function registerElement(element) { + element.addEventListener(evc, listener, options); + if (element[keyLTE]) return element; + if (element.isConnected) return element.dispatchEvent(new Event(evc)), element; + const c = onAbort(options.signal, () => c_ch_o.offConnected(element, listener)); + if (c) c_ch_o.onConnected(element, listener); + return element; }; }; -R.disconnected = function(t, e) { - return e = G(e), function(r) { - return r.addEventListener(y, t, e), r[x] || N(e.signal, () => w.offDisconnected(r, t)) && w.onDisconnected(r, t), r; +on.disconnected = function(listener, options) { + options = lifeOptions(options); + return function registerElement(element) { + element.addEventListener(evd, listener, options); + if (element[keyLTE]) return element; + const c = onAbort(options.signal, () => c_ch_o.offDisconnected(element, listener)); + if (c) c_ch_o.onDisconnected(element, listener); + return element; }; }; -var j = /* @__PURE__ */ new WeakMap(); -R.disconnectedAsAbort = function(t) { - if (j.has(t)) - return j.get(t); - let e = new AbortController(); - return j.set(t, e), t(R.disconnected(() => e.abort())), e; +var store_abort = /* @__PURE__ */ new WeakMap(); +on.disconnectedAsAbort = function(host) { + if (store_abort.has(host)) return store_abort.get(host); + const a = new AbortController(); + store_abort.set(host, a); + host(on.disconnected(() => a.abort())); + return a; }; -var st = /* @__PURE__ */ new WeakSet(); -R.attributeChanged = function(t, e) { - return typeof e != "object" && (e = {}), function(r) { - if (r.addEventListener(D, t, e), r[x] || st.has(r) || !a.M) - return r; - let o = new a.M(function(f) { - for (let { attributeName: d, target: p } of f) - p.dispatchEvent( - new CustomEvent(D, { detail: [d, p.getAttribute(d)] }) +var els_attribute_store = /* @__PURE__ */ new WeakSet(); +on.attributeChanged = function(listener, options) { + if (typeof options !== "object") + options = {}; + return function registerElement(element) { + element.addEventListener(eva, listener, options); + if (element[keyLTE] || els_attribute_store.has(element)) + return element; + if (!enviroment.M) return element; + const observer = new enviroment.M(function(mutations) { + for (const { attributeName, target } of mutations) + target.dispatchEvent( + new CustomEvent(eva, { detail: [attributeName, target.getAttribute(attributeName)] }) ); }); - return N(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r; + const c = onAbort(options.signal, () => observer.disconnect()); + if (c) observer.observe(element, { attributes: true }); + return element; }; }; export { - O as assign, - z as assignAttribute, - Q as chainableAppend, - Y as classListDeclarative, - k as createElement, - gt as createElementNS, - Dt as customElementRender, - ct as customElementWithDDE, - _t as dispatchEvent, - k as el, - gt as elNS, - tt as elementAttribute, - vt as empty, - ct as lifecyclesToEvents, - it as observedAttributes, - R as on, - V as registerReactivity, - S as scope, - bt as simulateSlots + assign, + assignAttribute, + chainableAppend, + classListDeclarative, + createElement, + createElementNS, + customElementRender, + lifecyclesToEvents as customElementWithDDE, + dispatchEvent, + createElement as el, + createElementNS as elNS, + elementAttribute, + empty, + lifecyclesToEvents, + observedAttributes2 as observedAttributes, + on, + registerReactivity, + scope, + simulateSlots }; diff --git a/docs/global.css b/docs/global.css index 58ba305..7842668 100644 --- a/docs/global.css +++ b/docs/global.css @@ -145,6 +145,7 @@ main > *{ --shiki-token-punctuation: var(--code); --shiki-token-link: #EE0000; white-space: pre; + tab-size: 2; overflow: scroll; } diff --git a/docs/index.html b/docs/index.html index a463c02..e50d02b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -19,4 +19,4 @@ document.body.append( }) ) ); -

The library introduces a new “type” of variable/constant called signal allowing us to to use introduced 3PS pattern in our applications. As you can see it in the example above.

Also please notice that there is very similar 3PS pattern used for separate creation of UI and business logic.

The 3PS is very simplified definition of the pattern. There are more deep/academic definitions more precisely describe usage in specific situations, see for example MVVM or MVC.

# Organization of the documentation

\ No newline at end of file +

The library introduces a new “type” of variable/constant called signal allowing us to to use introduced 3PS pattern in our applications. As you can see it in the example above.

Also please notice that there is very similar 3PS pattern used for separate creation of UI and business logic.

The 3PS is very simplified definition of the pattern. There are more deep/academic definitions more precisely describe usage in specific situations, see for example MVVM or MVC.

# Organization of the documentation

\ No newline at end of file diff --git a/docs/p02-elements.html b/docs/p02-elements.html index 0109f67..62b07d5 100644 --- a/docs/p02-elements.html +++ b/docs/p02-elements.html @@ -26,7 +26,7 @@ document.body.append( { textContent: "Element’s text content.", style: "color: coral;" } ) ); -

To make this easier, you can use the el function. Internally in basic examples, it is wrapper around assign(document.createElement(…), { … }).

import { el, assign } from "./esm-with-signals.js"; +

To make this easier, you can use the el function. Internally in basic examples, it is wrapper around assign(document.createElement(…), { … }).

import { el, assign } from "./esm-with-signals.js"; const color= "lightcoral"; document.body.append( el("p", { textContent: "Hello (first time)", style: { color } }) @@ -37,7 +37,7 @@ document.body.append( { textContent: "Hello (second time)", style: { color } } ) ); -

The assign function provides improved behaviour of Object.assign(). You can declaratively sets any IDL and attribute of the given element. Function prefers IDL and fallback to the element.setAttribute if there is no writable property in the element prototype.

You can study all JavaScript elements interfaces to the corresponding HTML elements. All HTML elements inherits from HTMLElement. To see all available IDLs for example for paragraphs, see HTMLParagraphElement. Moreover, the assign provides a way to sets declaratively some convenient properties:

  • It is possible to sets data-*/aria-* attributes using object notation.
  • In opposite, it is also possible to sets data-*/aria-* attribute using camelCase notation.
  • You can use string or object as a value for style property.
  • className (IDL – preffered)/class are ways to add CSS classes to the element. You can use string (similarly to class="…" syntax in HTML) or array of strings. This is handy to concat conditional classes.
  • Use classList to toggle specific classes. This will be handy later when the reactivity via signals is beeing introduced.
  • The assign also accepts the undefined as a value for any property to remove it from the element declaratively. Also for some IDL the corresponding attribute is removed as it can be confusing. For example, natievly the element’s id is removed by setting the IDL to an empty string.
  • You can use = or . to force processing given key as attribute/property of the element.

For processing, the assign internally uses assignAttribute and classListDeclarative.

import { assign, assignAttribute, classListDeclarative } from "./esm-with-signals.js"; +

The assign function provides improved behaviour of Object.assign(). You can declaratively sets any IDL and attribute of the given element. Function prefers IDL and fallback to the element.setAttribute if there is no writable property in the element prototype.

You can study all JavaScript elements interfaces to the corresponding HTML elements. All HTML elements inherits from HTMLElement. To see all available IDLs for example for paragraphs, see HTMLParagraphElement. Moreover, the assign provides a way to sets declaratively some convenient properties:

  • It is possible to sets data-*/aria-* attributes using object notation.
  • In opposite, it is also possible to sets data-*/aria-* attribute using camelCase notation.
  • You can use string or object as a value for style property.
  • className (IDL – preffered)/class are ways to add CSS classes to the element. You can use string (similarly to class="…" syntax in HTML) or array of strings. This is handy to concat conditional classes.
  • Use classList to toggle specific classes. This will be handy later when the reactivity via signals is beeing introduced.
  • The assign also accepts the undefined as a value for any property to remove it from the element declaratively. Also for some IDL the corresponding attribute is removed as it can be confusing. For example, natievly the element’s id is removed by setting the IDL to an empty string.
  • You can use = or . to force processing given key as attribute/property of the element.

For processing, the assign internally uses assignAttribute and classListDeclarative.

import { assign, assignAttribute, classListDeclarative } from "./esm-with-signals.js"; const paragraph= document.createElement("p"); assignAttribute(paragraph, "textContent", "Hello, world!"); @@ -70,7 +70,7 @@ console.log("paragraph.something=", paragraph.something); document.body.append( paragraph ); -

# Native JavaScript templating

By default, the native JS has no good way to define HTML template using DOM API:

document.body.append( +

# Native JavaScript templating

By default, the native JS has no good way to define HTML template using DOM API:

document.body.append( document.createElement("div"), document.createElement("span"), document.createElement("main") @@ -81,7 +81,7 @@ const template= document.createElement("main").append( document.createElement("span"), ); console.log(typeof template==="undefined"); -

This library therefore overwrites the append method of created elements to always return parent element.

import { el } from "./esm-with-signals.js"; +

This library therefore overwrites the append method of created elements to always return parent element.

import { el } from "./esm-with-signals.js"; document.head.append( el("style").append( "tr, td{ border: 1px solid red; padding: 1em; }", @@ -116,7 +116,7 @@ document.body.append( ) ) ); -

# Basic (state-less) components

You can use functions for encapsulation (repeating) logic. The el accepts function as first argument. In that case, the function should return dom elements and the second argument for el is argument for given element.

import { el } from "./esm-with-signals.js"; +

# Basic (state-less) components

You can use functions for encapsulation (repeating) logic. The el accepts function as first argument. In that case, the function should return dom elements and the second argument for el is argument for given element.

import { el } from "./esm-with-signals.js"; document.head.append( el("style").append( ".class1{ font-weight: bold; }", @@ -133,7 +133,7 @@ function component({ className, textContent }){ el("p", textContent) ); } -

As you can see, in case of state-less/basic components there is no difference between calling component function directly or using el function.

It is nice to use similar naming convention as native DOM API. This allows us to use the destructuring assignment syntax and keep track of the native API (things are best remembered through regular use).

# Creating non-HTML elements

Similarly to the native DOM API (document.createElementNS) for non-HTML elements we need to tell JavaScript which kind of the element to create. We can use the elNS function:

import { elNS, assign } from "./esm-with-signals.js"; +

As you can see, in case of state-less/basic components there is no difference between calling component function directly or using el function.

It is nice to use similar naming convention as native DOM API. This allows us to use the destructuring assignment syntax and keep track of the native API (things are best remembered through regular use).

# Creating non-HTML elements

Similarly to the native DOM API (document.createElementNS) for non-HTML elements we need to tell JavaScript which kind of the element to create. We can use the elNS function:

import { elNS, assign } from "./esm-with-signals.js"; const elSVG= elNS("http://www.w3.org/2000/svg"); const elMath= elNS("http://www.w3.org/1998/Math/MathML"); document.body.append( @@ -144,4 +144,4 @@ document.body.append( console.log( document.body.innerHTML.includes("<svg></svg><math></math>") ) -

# Mnemonic

  • assign(<element>, ...<idl-objects>): <element> — assign properties to the element
  • el(<tag-name>, <primitive>)[.append(...)]: <element-from-tag-name> — simple element containing only text
  • el(<tag-name>, <idl-object>)[.append(...)]: <element-from-tag-name> — element with more properties
  • el(<function>, <function-argument(s)>)[.append(...)]: <element-returned-by-function> — using component represented by function
  • el(<...>, <...>, ...<addons>) — see following page
  • elNS(<namespace>)(<as-el-see-above>)[.append(...)]: <element-based-on-arguments> — typically SVG elements
\ No newline at end of file +

# Mnemonic

  • assign(<element>, ...<idl-objects>): <element> — assign properties to the element
  • el(<tag-name>, <primitive>)[.append(...)]: <element-from-tag-name> — simple element containing only text
  • el(<tag-name>, <idl-object>)[.append(...)]: <element-from-tag-name> — element with more properties
  • el(<function>, <function-argument(s)>)[.append(...)]: <element-returned-by-function> — using component represented by function
  • el(<...>, <...>, ...<addons>) — see following page
  • elNS(<namespace>)(<as-el-see-above>)[.append(...)]: <element-based-on-arguments> — typically SVG elements
\ No newline at end of file diff --git a/docs/p03-events.html b/docs/p03-events.html index 3d13d08..244b215 100644 --- a/docs/p03-events.html +++ b/docs/p03-events.html @@ -12,7 +12,7 @@ on("click", log("`on`"), { once: true })(button); document.body.append( button ); -

…this is actually one of the two differences. The another one is that on accepts only object as the options (but it is still optional).

The other difference is that there is no off function. You can remove listener declaratively using AbortSignal:

import { el, on } from "./esm-with-signals.js"; +

…this is actually one of the two differences. The another one is that on accepts only object as the options (but it is still optional).

The other difference is that there is no off function. You can remove listener declaratively using AbortSignal:

import { el, on } from "./esm-with-signals.js"; const log= mark=> console.log.bind(console, mark); const abort_controller= new AbortController(); @@ -25,7 +25,7 @@ on("click", log("`on`"), { signal })(button); document.body.append( button, " ", el("button", { textContent: "Off", onclick: ()=> abort_controller.abort() }) ); -

So, there are (typically) three ways to handle events. You can use:

  • el("button", { textContent: "click me", "=onclick": "console.log(event)" })
  • el("button", { textContent: "click me", onclick: console.log })
  • el("button", { textContent: "click me" }, on("click", console.log))

In the first example we force to use HTML attribute (it corresponds to <button onclick="console.log(event)">click me</button>). Side note: this can be useful in case of SSR. To study difference, you can read a nice summary here: GIST @WebReflection/web_events.md.

# Addons

From practical point of view, Addons are just functions that accept any HTML element as their first parameter. You can see that the on(…) fullfills this requirement.

You can use Addons as ≥3rd argument of el function. This way is possible to extends your templates by additional (3rd party) functionalities. But for now mainly, you can add events listeners:

import { el, on } from "./esm-with-signals.js"; +

So, there are (typically) three ways to handle events. You can use:

  • el("button", { textContent: "click me", "=onclick": "console.log(event)" })
  • el("button", { textContent: "click me", onclick: console.log })
  • el("button", { textContent: "click me" }, on("click", console.log))

In the first example we force to use HTML attribute (it corresponds to <button onclick="console.log(event)">click me</button>). Side note: this can be useful in case of SSR. To study difference, you can read a nice summary here: GIST @WebReflection/web_events.md.

# Addons

From practical point of view, Addons are just functions that accept any HTML element as their first parameter. You can see that the on(…) fullfills this requirement.

You can use Addons as ≥3rd argument of el function. This way is possible to extends your templates by additional (3rd party) functionalities. But for now mainly, you can add events listeners:

import { el, on } from "./esm-with-signals.js"; const abort_controller= new AbortController(); const { signal }= abort_controller; /** @type {ddeElementAddon<HTMLButtonElement>} */ @@ -49,7 +49,7 @@ function update(event){ "\n" ); } -

As the example shows, you can also provide types in JSDoc+TypeScript by using global type ddeElementAddon. Also notice, you can use Addons to get element reference.

# Life-cycle events

Addons are called immediately when the element is created, even it is not connected to live DOM yet. Therefore, you can understand the Addon to be “oncreate” event.

The library provide three additional live-cycle events corresponding to how they are named in a case of custom elements: on.connected, on.disconnected and on.attributeChanged.

import { el, on } from "./esm-with-signals.js"; +

As the example shows, you can also provide types in JSDoc+TypeScript by using global type ddeElementAddon. Also notice, you can use Addons to get element reference.

# Life-cycle events

Addons are called immediately when the element is created, even it is not connected to live DOM yet. Therefore, you can understand the Addon to be “oncreate” event.

The library provide three additional live-cycle events corresponding to how they are named in a case of custom elements: on.connected, on.disconnected and on.attributeChanged.

import { el, on } from "./esm-with-signals.js"; const paragraph= el("p", "See live-cycle events in console.", el=> log({ type: "dde:created", detail: el }), on.connected(log), @@ -67,7 +67,7 @@ document.body.append( function log({ type, detail }){ console.log({ _this: this, type, detail }); } -

For Custom elements, we will later introduce a way to replace *Callback syntax with dde:* events. The on.* functions then listen to the appropriate Custom Elements events (see Custom element lifecycle callbacks | MDN).

But, in case of regular elemnets the MutationObserver | MDN is internaly used to track these events. Therefore, there are some drawbacks:

  • To proper listener registration, you need to use on.* not `on("dde:*", …)`!
  • Use sparingly! Internally, library must loop of all registered events and fires event properly. It is good practice to use the fact that if an element is removed, its children are also removed! In this spirit, we will introduce later the host syntax to register, clean up procedures when the component is removed from the app.

To provide intuitive behaviour, similar also to how the life-cycle events works in other frameworks/libraries, deka-dom-el library ensures that on.connected and on.disconnected are called only once and only when the element, is (dis)connected to live DOM. The solution is inspired by Vue. For using native behaviour re-(dis)connecting element, use:

  • custom MutationObserver or logic in (dis)connectedCallback or…
  • re-add on.connected or on.disconnected listeners.

# Final notes

The library also provides a method to dispatch the events.

import { el, on, dispatchEvent } from "./esm-with-signals.js"; +

For Custom elements, we will later introduce a way to replace *Callback syntax with dde:* events. The on.* functions then listen to the appropriate Custom Elements events (see Custom element lifecycle callbacks | MDN).

But, in case of regular elemnets the MutationObserver | MDN is internaly used to track these events. Therefore, there are some drawbacks:

  • To proper listener registration, you need to use on.* not `on("dde:*", …)`!
  • Use sparingly! Internally, library must loop of all registered events and fires event properly. It is good practice to use the fact that if an element is removed, its children are also removed! In this spirit, we will introduce later the host syntax to register, clean up procedures when the component is removed from the app.

To provide intuitive behaviour, similar also to how the life-cycle events works in other frameworks/libraries, deka-dom-el library ensures that on.connected and on.disconnected are called only once and only when the element, is (dis)connected to live DOM. The solution is inspired by Vue. For using native behaviour re-(dis)connecting element, use:

  • custom MutationObserver or logic in (dis)connectedCallback or…
  • re-add on.connected or on.disconnected listeners.

# Final notes

The library also provides a method to dispatch the events.

import { el, on, dispatchEvent } from "./esm-with-signals.js"; document.body.append( el("p", "Listenning to `test` event.", on("test", console.log)).append( el("br"), @@ -91,4 +91,4 @@ function dde(){ function ddeOptions(){ dispatchEvent("test", { bubbles: true })(this, "hi"); } -

# Mnemonic

  • on(<event>, <listener>[, <options>])(<element>) — just <element>.addEventListener(<event>, <listener>[, <options>])
  • on.<live-cycle>(<event>, <listener>[, <options>])(<element>) — corresponds to custom elemnets callbacks <live-cycle>Callback(...){...}. To connect to custom element see following page, else it is simulated by MutationObserver.
  • dispatchEvent(<event>[, <options>])(element) — just <element>.dispatchEvent(new Event(<event>[, <options>]))
  • dispatchEvent(<event>[, <options>])(element, detail) — just <element>.dispatchEvent(new CustomEvent(<event>, { detail, ...<options> }))
\ No newline at end of file +

# Mnemonic

  • on(<event>, <listener>[, <options>])(<element>) — just <element>.addEventListener(<event>, <listener>[, <options>])
  • on.<live-cycle>(<event>, <listener>[, <options>])(<element>) — corresponds to custom elemnets callbacks <live-cycle>Callback(...){...}. To connect to custom element see following page, else it is simulated by MutationObserver.
  • dispatchEvent(<event>[, <options>])(element) — just <element>.dispatchEvent(new Event(<event>[, <options>]))
  • dispatchEvent(<event>[, <options>])(element, detail) — just <element>.dispatchEvent(new CustomEvent(<event>, { detail, ...<options> }))
\ No newline at end of file diff --git a/docs/p04-signals.html b/docs/p04-signals.html index 8548737..710914d 100644 --- a/docs/p04-signals.html +++ b/docs/p04-signals.html @@ -16,7 +16,7 @@ update(); const interval= 5*1000; setTimeout(clearInterval, 10*interval, setInterval(update, interval)); -

All this is just an example of Event-driven programming and Publish–subscribe pattern (compare for example with fpubsub library). All three parts can be in some manner independent and still connected to the same reactive entity.

Signals are implemented in the library as functions. To see current value of signal, just call it without any arguments console.log(signal()). To update the signal value, pass any argument signal('a new value'). For listenning the signal value changes, use S.on(signal, console.log).

Similarly to the on function to register DOM events listener. You can use AbortController/AbortSignal to off/stop listenning. In example, you also found the way for representing “live” piece of code computation pattern (derived signal):

import { S } from "./esm-with-signals.js"; +

All this is just an example of Event-driven programming and Publish–subscribe pattern (compare for example with fpubsub library). All three parts can be in some manner independent and still connected to the same reactive entity.

Signals are implemented in the library as functions. To see current value of signal, just call it without any arguments console.log(signal()). To update the signal value, pass any argument signal('a new value'). For listenning the signal value changes, use S.on(signal, console.log).

Similarly to the on function to register DOM events listener. You can use AbortController/AbortSignal to off/stop listenning. In example, you also found the way for representing “live” piece of code computation pattern (derived signal):

import { S } from "./esm-with-signals.js"; const signal= S(0); // computation pattern const double= S(()=> 2*signal()); @@ -32,7 +32,7 @@ ac.signal.addEventListener("abort", ()=> setTimeout(()=> clearInterval(id), 2*interval)); setTimeout(()=> ac.abort(), 3*interval) -

# Signals and actions

S(/* primitive */) allows you to declare simple reactive variables, typically, around immutable primitive types. However, it may also be necessary to use reactive arrays, objects, or other complex reactive structures.

import { S } from "./esm-with-signals.js"; +

# Signals and actions

S(/* primitive */) allows you to declare simple reactive variables, typically, around immutable primitive types. However, it may also be necessary to use reactive arrays, objects, or other complex reactive structures.

import { S } from "./esm-with-signals.js"; const signal= S(0, { increaseOnlyOdd(add){ console.info(add); @@ -50,7 +50,7 @@ setTimeout( 10*interval, setInterval(oninterval, interval) ); -

…but typical user-case is object/array (maps, sets and other mutable objects):

import { S } from "./esm-with-signals.js"; +

…but typical user-case is object/array (maps, sets and other mutable objects):

import { S } from "./esm-with-signals.js"; const todos= S([], { push(item){ this.value.push(S(item)); @@ -106,7 +106,7 @@ function radio({ textContent, checked= false }){ " ",textContent ) } -

In some way, you can compare it with useReducer hook from React. So, the S(<data>, <actions>) pattern creates a store “machine”. We can then invoke (dispatch) registered action by calling S.action(<signal>, <name>, ...<args>) after the action call the signal calls all its listeners. This can be stopped by calling this.stopPropagation() in the method representing the given action. As it can be seen in examples, the “store” value is available also in the function for given action (this.value).

# Reactive DOM attributes and elements

There are on basic level two distinc situation to mirror dynamic value into the DOM/UI

  1. to change some attribute(s) of existing element(s)
  2. to generate elements itself dynamically – this covers conditions and loops
import { S } from "./esm-with-signals.js"; +

In some way, you can compare it with useReducer hook from React. So, the S(<data>, <actions>) pattern creates a store “machine”. We can then invoke (dispatch) registered action by calling S.action(<signal>, <name>, ...<args>) after the action call the signal calls all its listeners. This can be stopped by calling this.stopPropagation() in the method representing the given action. As it can be seen in examples, the “store” value is available also in the function for given action (this.value).

# Reactive DOM attributes and elements

There are on basic level two distinc situation to mirror dynamic value into the DOM/UI

  1. to change some attribute(s) of existing element(s)
  2. to generate elements itself dynamically – this covers conditions and loops
import { S } from "./esm-with-signals.js"; const count= S(0); import { el } from "./esm-with-signals.js"; @@ -121,7 +121,7 @@ document.head.append( const interval= 5 * 1000; setTimeout(clearInterval, 10*interval, setInterval(()=> count(count()+1), interval)); -

To derived attribute based on value of signal variable just use the signal as a value of the attribute (assign(element, { attribute: S('value') })). assign/el provides ways to glue reactive attributes/classes more granularly into the DOM. Just use dedicated build-in attributes dataset, ariaset and classList.

For computation, you can use the “derived signal” (see above) like assign(element, { textContent: S(()=> 'Hello '+WorldSignal()) }). This is read-only signal its value is computed based on given function and updated when any signal used in the function changes.

To represent part of the template filled dynamically based on the signal value use S.el(signal, DOMgenerator). This was already used in the todo example above or see:

import { S } from "./esm-with-signals.js"; +

To derived attribute based on value of signal variable just use the signal as a value of the attribute (assign(element, { attribute: S('value') })). assign/el provides ways to glue reactive attributes/classes more granularly into the DOM. Just use dedicated build-in attributes dataset, ariaset and classList.

For computation, you can use the “derived signal” (see above) like assign(element, { textContent: S(()=> 'Hello '+WorldSignal()) }). This is read-only signal its value is computed based on given function and updated when any signal used in the function changes.

To represent part of the template filled dynamically based on the signal value use S.el(signal, DOMgenerator). This was already used in the todo example above or see:

import { S } from "./esm-with-signals.js"; const count= S(0, { add(){ this.value= this.value + Math.round(Math.random()*10); } }); @@ -147,4 +147,4 @@ setTimeout(clearInterval, 10*interval, setInterval(function(){ S.action(count, "add"); S.action(numbers, "push", count()); }, interval)); -

# Mnemonic

  • S(<value>) — signal: reactive value
  • S(()=> <computation>) — read-only signal: reactive value dependent on calculation using other signals
  • S.on(<signal>, <listener>[, <options>]) — listen to the signal value changes
  • S.clear(...<signals>) — off and clear signals
  • S(<value>, <actions>) — signal: pattern to create complex reactive objects/arrays
  • S.action(<signal>, <action-name>, ...<action-arguments>) — invoke an action for given signal
  • S.el(<signal>, <function-returning-dom>) — render partial dom structure (template) based on the current signal value
\ No newline at end of file +

# Mnemonic

  • S(<value>) — signal: reactive value
  • S(()=> <computation>) — read-only signal: reactive value dependent on calculation using other signals
  • S.on(<signal>, <listener>[, <options>]) — listen to the signal value changes
  • S.clear(...<signals>) — off and clear signals
  • S(<value>, <actions>) — signal: pattern to create complex reactive objects/arrays
  • S.action(<signal>, <action-name>, ...<action-arguments>) — invoke an action for given signal
  • S.el(<signal>, <function-returning-dom>) — render partial dom structure (template) based on the current signal value
\ No newline at end of file diff --git a/docs/p05-scopes.html b/docs/p05-scopes.html index 843559c..ab43f49 100644 --- a/docs/p05-scopes.html +++ b/docs/p05-scopes.html @@ -34,7 +34,7 @@ function component(){ el("strong", "Component") ); } -

To better understanding we implement function elClass helping to create component as class instances.

import { el } from "./esm-with-signals.js"; +

To better understanding we implement function elClass helping to create component as class instances.

import { el } from "./esm-with-signals.js"; class Test { constructor(params){ this._params= params; @@ -78,7 +78,7 @@ function elClass(_class, attributes, ...addons){ scope.pop(); return element; } -

As you can see, the scope.host() is stored temporarily and synchronously. Therefore, at least in the beginning of using library, it is the good practise to store host in the root of your component. As it may be changed, typically when there is asynchronous code in the component.

import { el, scope, on, dispatchEvent } from "deka-dom-el"; +

As you can see, the scope.host() is stored temporarily and synchronously. Therefore, at least in the beginning of using library, it is the good practise to store host in the root of your component. As it may be changed, typically when there is asynchronous code in the component.

import { el, scope, on, dispatchEvent } from "deka-dom-el"; document.body.append( el(component) ); @@ -114,7 +114,7 @@ function component(){ }); return el("p", textContent, onclickChange); } -

The text content of the paragraph is changing when the value of the signal textContent is changed. Internally, there is association between textContent and the paragraph, similar to using S.on(textContent, /* update the paragraph */).

This listener must be removed when the component is removed from the DOM. To do it, the library assign internally on.disconnected(/* remove the listener */)(host()) to the host element.

The library DOM API and signals works ideally when used declaratively. It means, you split your app logic into three parts as it was itroduced in Signals.

/* PSEUDO-CODE!!! */ +

The text content of the paragraph is changing when the value of the signal textContent is changed. Internally, there is association between textContent and the paragraph, similar to using S.on(textContent, /* update the paragraph */).

This listener must be removed when the component is removed from the DOM. To do it, the library assign internally on.disconnected(/* remove the listener */)(host()) to the host element.

The library DOM API and signals works ideally when used declaratively. It means, you split your app logic into three parts as it was itroduced in Signals.

/* PSEUDO-CODE!!! */ import { el } from "deka-dom-el"; import { S } from "deka-dom-el/signals"; function component(){ diff --git a/docs/p06-customElement.html b/docs/p06-customElement.html index 07d94be..f37eabe 100644 --- a/docs/p06-customElement.html +++ b/docs/p06-customElement.html @@ -10,7 +10,7 @@ S.observedAttributes; // “internal” utils import { lifecyclesToEvents } from "deka-dom-el"; -

# Custom Elements Introduction

To start with, let’s see how to use native Custom Elements. As starting point please read Using Custom Elements on MDN. To sum up and for mnemonic see following code overview:

export class HTMLCustomElement extends HTMLElement{ +

# Custom Elements Introduction

Web Components, specifically Custom Elements, are a set of web platform APIs that allow you to create new HTML tags with custom functionality encapsulated within them. This allows for the creation of reusable components that can be used across web applications.

To start with, let’s see how to use native Custom Elements. As starting point please read Using Custom Elementson MDN. To sum up and for mnemonic see following code overview:

export class HTMLCustomElement extends HTMLElement{ static tagName= "custom-element"; // just suggestion, we can use `el(HTMLCustomElement.tagName)` static observedAttributes= [ "custom-attribute" ]; constructor(){ @@ -31,7 +31,7 @@ import { lifecyclesToEvents } from "deka-dom-el"; set customAttribute(value){ this.setAttribute("custom-attribute", value); } } customElements.define(HTMLCustomElement.tagName, HTMLCustomElement); -

For more advanced use of Custom Elements, the summary Handy Custom Elements' Patterns may be useful. Especially pay attention to linking HTML attributes and defining setters/getters, this is very helpful to use in combination with the library (el(HTMLCustomElement.tagName, { customAttribute: "new-value" });).

Also see the Life Cycle Events sections, very similarly we would like to use DDE events. To do it, the library provides function customElementWithDDE

import { customElementWithDDE, el, on } from "./esm-with-signals.js"; +

For more advanced use of Custom Elements, the summary Handy Custom Elements Patterns may be useful. Especially pay attention to linking HTML attributes and defining setters/getters, this is very helpful to use in combination with the library (el(HTMLCustomElement.tagName, { customAttribute: "new-value" });).

Also see the Life Cycle Events sections, very similarly we would like to use DDE events. To do it, the library provides function customElementWithDDE

import { customElementWithDDE, el, on } from "./esm-with-signals.js"; export class HTMLCustomElement extends HTMLElement{ static tagName= "custom-element"; connectedCallback(){ @@ -54,7 +54,7 @@ instance.addEventListener( document.body.append( instance, ); -

Custom Elements with DDE

The customElementWithDDE function is only (small) part of the inregration of the library. More important for coexistence is render component function as a body of the Custom Element. For that, you can use customElementRender with arguments instance reference, target for connection, render function and optional properties (will be passed to the render function) see later…

import { +

Custom Elements with DDE

The customElementWithDDE function is only (small) part of the inregration of the library. More important for coexistence is render component function as a body of the Custom Element. For that, you can use customElementRender with arguments instance reference, target for connection, render function and optional properties (will be passed to the render function) see later…

import { customElementRender, customElementWithDDE, } from "./esm-with-signals.js"; @@ -87,7 +87,7 @@ customElements.define(HTMLCustomElement.tagName, HTMLCustomElement); document.body.append( el(HTMLCustomElement.tagName, { attr: "Attribute" }) ); -

…as you can see, you can use components created based on the documentation previously introduced. To unlock full potential, use with combination customElementWithDDE (allows to use livecycle events) and observedAttributes (converts attributes to render function arguments — default) or S.observedAttributes (converts attributes to signals).

import { +

…as you can see, you can use components created based on the documentation previously introduced. To unlock full potential, use with combination customElementWithDDE (allows to use livecycle events) and observedAttributes (converts attributes to render function arguments — default) or S.observedAttributes (converts attributes to signals).

import { customElementRender, customElementWithDDE, observedAttributes, @@ -129,13 +129,16 @@ setTimeout( ()=> document.querySelector(HTMLCustomElement.tagName).setAttribute("attr", "New Value"), 3*750 ); -

# Shadow DOM

Regarding to this.attachShadow({ mode: 'open' }) see quick overview Using Shadow DOM. An another source of information can be Shadow DOM in Depth. To sum up, there in basic three ways to render component body:

import { +

# Shadow DOM

Shadow DOM is a web platform feature that allows for the encapsulation of a component’s internal DOM tree from the rest of the document. This means that styles and scripts applied to the document will not affect the component’s internal DOM, and vice versa.

import { el, customElementRender, customElementWithDDE, } from "./esm-with-signals.js"; function ddeComponent(){ return el().append( + el("style", ` + .red{ color: firebrick; } + `), el("p", { className: "red" }).append( "Hello from ", el("slot", "Custom Element"), "!" ) @@ -180,13 +183,60 @@ customElementWithDDE(C); customElements.define(C.tagName, C); document.body.append( - el("style", ` - .red{ color: red; } - `), el(A.tagName).append("Without shadowRoot"), el("hr"), el(B.tagName).append("Open shadowRoot"), el("hr"), el(C.tagName).append("Closed shadowRoot"), + el("style", ` + .red{ color: crimson; } + `), ); -

# Mnemonic

  • customElementRender(<custom-element>, <connect-target>, <render-function>[, <properties>]) — use function to render DOM structure for given <custom-element>
  • customElementWithDDE(<custom-element>) — register <custom-element> to DDE library, see also `lifecyclesToEvents`, can be also used as decorator
  • observedAttributes(<custom-element>) — returns record of observed attributes (keys uses camelCase)
  • S.observedAttributes(<custom-element>) — returns record of observed attributes (keys uses camelCase and values are signals)
  • lifecyclesToEvents(<class-declaration>) — convert lifecycle methods to events, can be also used as decorator
\ No newline at end of file +console.log(A.tagName, "expect modifications"); +document.body.querySelector(A.tagName).querySelector("p").textContent+= " (editable with JS)"; +console.log(B.tagName, "expect modifications"); +document.body.querySelector(B.tagName).shadowRoot.querySelector("p").textContent+= " (editable with JS)"; +console.log(C.tagName, "expect error ↓"); +document.body.querySelector(C.tagName).querySelector("p").textContent+= " (editable with JS)"; +

Regarding to this.attachShadow({ mode: 'open' }) see quick overview Using Shadow DOM. An another source of information can be Shadow DOM in Depth.

Besides the encapsulation, the Shadow DOM allows for using the <slot>element(s). You can simulate this feature using simulateSlots:

import { + customElementRender, + customElementWithDDE, + el, + simulateSlots +} from "./esm-with-signals.js"; +export class HTMLCustomElement extends HTMLElement{ + static tagName= "custom-slotting"; + connectedCallback(){ + const c= ()=> simulateSlots(this, ddeComponent()); + customElementRender(this, this, c); + } +} +customElementWithDDE(HTMLCustomElement); +customElements.define(HTMLCustomElement.tagName, HTMLCustomElement); + +document.body.append( + el(HTMLCustomElement.tagName), + el(HTMLCustomElement.tagName).append( + "Slot" + ), + el(ddeComponentSlot), + el(ddeComponentSlot).append( + "Slot" + ), +); + +function ddeComponent(){ + return el().append( + el("p").append( + "Hello ", el("slot", "World") + ) + ); +} +function ddeComponentSlot(){ + return simulateSlots(el().append( + el("p").append( + "Hello ", el("slot", "World") + ) + )); +} +

To sum up:

  • The use of shadow DOM to encapsulate the internal structure of the custom element, which affects how the custom element can be styled and modified using JavaScript and CSS.
  • The ability to access and modify the internal structure of the custom element using JavaScript, which is affected by the use of shadow DOM and the mode of the shadow DOM.
  • The use of slots to allow for the insertion of content from the parent document into the custom element, which is affected by the use of shadow DOM and the mode of the shadow DOM.

# Mnemonic

  • customElementRender(<custom-element>, <connect-target>, <render-function>[, <properties>]) — use function to render DOM structure for given <custom-element>
  • customElementWithDDE(<custom-element>) — register <custom-element> to DDE library, see also `lifecyclesToEvents`, can be also used as decorator
  • observedAttributes(<custom-element>) — returns record of observed attributes (keys uses camelCase)
  • S.observedAttributes(<custom-element>) — returns record of observed attributes (keys uses camelCase and values are signals)
  • lifecyclesToEvents(<class-declaration>) — convert lifecycle methods to events, can be also used as decorator
  • simulateSlots(<class-instance>, <body>[, <mapper>]) — simulate slots for Custom Elements without shadow DOM
  • simulateSlots(<dde-component>[, <mapper>]) — simulate slots for “dde”/functional components
\ No newline at end of file diff --git a/docs_src/components/code.html.js b/docs_src/components/code.html.js index 9a81953..197f758 100644 --- a/docs_src/components/code.html.js +++ b/docs_src/components/code.html.js @@ -14,7 +14,8 @@ ${host}{ --shiki-token-punctuation: var(--code); --shiki-token-link: #EE0000; white-space: pre; - tab-size: 2;${""/* TODO: allow custom tab size?! */} + ${""/* TODO: allow custom tab size?! */} + tab-size: 2; overflow: scroll; } ${host}[data-js=todo]{ diff --git a/docs_src/components/examples/customElement/shadowRoot.js b/docs_src/components/examples/customElement/shadowRoot.js index 900333b..3aad1ae 100644 --- a/docs_src/components/examples/customElement/shadowRoot.js +++ b/docs_src/components/examples/customElement/shadowRoot.js @@ -5,6 +5,9 @@ import { } from "deka-dom-el"; function ddeComponent(){ return el().append( + el("style", ` + .red{ color: firebrick; } + `), el("p", { className: "red" }).append( "Hello from ", el("slot", "Custom Element"), "!" ) @@ -49,12 +52,18 @@ customElementWithDDE(C); customElements.define(C.tagName, C); document.body.append( - el("style", ` - .red{ color: red; } - `), el(A.tagName).append("Without shadowRoot"), el("hr"), el(B.tagName).append("Open shadowRoot"), el("hr"), el(C.tagName).append("Closed shadowRoot"), + el("style", ` + .red{ color: crimson; } + `), ); +console.log(A.tagName, "expect modifications"); +document.body.querySelector(A.tagName).querySelector("p").textContent+= " (editable with JS)"; +console.log(B.tagName, "expect modifications"); +document.body.querySelector(B.tagName).shadowRoot.querySelector("p").textContent+= " (editable with JS)"; +console.log(C.tagName, "expect error ↓"); +document.body.querySelector(C.tagName).querySelector("p").textContent+= " (editable with JS)"; diff --git a/docs_src/components/examples/customElement/simulateSlots.js b/docs_src/components/examples/customElement/simulateSlots.js new file mode 100644 index 0000000..59fc900 --- /dev/null +++ b/docs_src/components/examples/customElement/simulateSlots.js @@ -0,0 +1,41 @@ +import { + customElementRender, + customElementWithDDE, + el, + simulateSlots +} from "deka-dom-el"; +export class HTMLCustomElement extends HTMLElement{ + static tagName= "custom-slotting"; + connectedCallback(){ + const c= ()=> simulateSlots(this, ddeComponent()); + customElementRender(this, this, c); + } +} +customElementWithDDE(HTMLCustomElement); +customElements.define(HTMLCustomElement.tagName, HTMLCustomElement); + +document.body.append( + el(HTMLCustomElement.tagName), + el(HTMLCustomElement.tagName).append( + "Slot" + ), + el(ddeComponentSlot), + el(ddeComponentSlot).append( + "Slot" + ), +); + +function ddeComponent(){ + return el().append( + el("p").append( + "Hello ", el("slot", "World") + ) + ); +} +function ddeComponentSlot(){ + return simulateSlots(el().append( + el("p").append( + "Hello ", el("slot", "World") + ) + )); +} diff --git a/docs_src/components/mnemonic/customElement-init.js b/docs_src/components/mnemonic/customElement-init.js index c253215..0454726 100644 --- a/docs_src/components/mnemonic/customElement-init.js +++ b/docs_src/components/mnemonic/customElement-init.js @@ -17,6 +17,12 @@ export function mnemonic(){ ), el("li").append( el("code", "lifecyclesToEvents()"), " — convert lifecycle methods to events, can be also used as decorator", - ) + ), + el("li").append( + el("code", "simulateSlots(, [, ])"), " — simulate slots for Custom Elements without shadow DOM", + ), + el("li").append( + el("code", "simulateSlots([, ])"), " — simulate slots for “dde”/functional components", + ), ); } diff --git a/docs_src/p06-customElement.html.js b/docs_src/p06-customElement.html.js index 1b8702f..8d471f6 100644 --- a/docs_src/p06-customElement.html.js +++ b/docs_src/p06-customElement.html.js @@ -38,6 +38,11 @@ const references= { title: t`MDN documentation page for Shadow DOM`, href: "https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM", }, + /** Shallow DOM on mdn */ + mdn_shadow_dom_slot: { + title: t`MDN documentation page for `, + href: "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot", + }, /** Shallow DOM */ shadow_dom_depth: { title: t`Everything you need to know about Shadow DOM (github repo praveenpuglia/shadow-dom-in-depth)`, @@ -50,7 +55,7 @@ export function page({ pkg, info }){ return el(simplePage, { info, pkg }).append( el("h2", t`Using web components in combinantion with DDE`), el("p").append(...T` - The DDE library allows for use within ${el("a", references.mdn_web_components).append( el("strong", "Web Components") )} + The DDE library allows for use within ${el("a", references.mdn_web_components).append( el("strong", t`Web Components`) )} for dom-tree generation. However, in order to be able to use signals (possibly mapping to registered ${el("a", references.mdn_observedAttributes).append( el("code", "observedAttributes") )}) and additional functionality is (unfortunately) required to use helpers provided by the library. @@ -58,17 +63,22 @@ export function page({ pkg, info }){ el(code, { src: fileURL("./components/examples/customElement/intro.js"), page_id }), el(h3, t`Custom Elements Introduction`), + el("p").append(...T` + Web Components, specifically Custom Elements, are a set of web platform APIs that allow you to create + new HTML tags with custom functionality encapsulated within them. This allows for the creation of reusable + components that can be used across web applications. + `), el("p").append(...T` To start with, let’s see how to use native Custom Elements. As starting point please read - ${el("a", references.mdn_custom_elements).append( el("strong", "Using Custom Elements"), " on MDN" )}. + ${el("a", references.mdn_custom_elements).append( el("strong", t`Using Custom Elements`), t` on MDN` )}. To sum up and for mnemonic see following code overview: `), el(code, { src: fileURL("./components/examples/customElement/native-basic.js"), page_id }), el("p").append(...T` For more advanced use of Custom Elements, the summary ${el("a", references.custom_elements_tips) - .append( el("strong", t`Handy Custom Elements' Patterns`) )} may be useful. Especially pay attention to + .append( el("strong", t`Handy Custom Elements Patterns`) )} may be useful. Especially pay attention to linking HTML attributes and defining setters/getters, this is very helpful to use in combination with - the library (${el("code", "el(HTMLCustomElement.tagName, { customAttribute: \"new-value\" });")}). + the library (${el("code", `el(HTMLCustomElement.tagName, { customAttribute: "${t`new-value`}" });`)}). `), el("p").append(...T` Also see the Life Cycle Events sections, very similarly we would like to use @@ -96,12 +106,38 @@ export function page({ pkg, info }){ el(h3, t`Shadow DOM`), el("p").append(...T` - Regarding to ${el("code", "this.attachShadow({ mode: 'open' })")} see quick overview - ${el("a", { textContent: t`Using Shadow DOM`, ...references.mdn_shadow_dom_depth })}. An another source of - information can be ${el("a", { textContent: t`Shadow DOM in Depth`, ...references.shadow_dom_depth })}. To - sum up, there in basic three ways to render component body: + Shadow DOM is a web platform feature that allows for the encapsulation of a component’s internal DOM tree + from the rest of the document. This means that styles and scripts applied to the document will not affect + the component’s internal DOM, and vice versa. `), el(example, { src: fileURL("./components/examples/customElement/shadowRoot.js"), page_id }), + el("p").append(...T` + Regarding to ${el("code", "this.attachShadow({ mode: 'open' })")} see quick overview + ${el("a", { textContent: t`Using Shadow DOM`, ...references.mdn_shadow_dom_depth })}. An another source of + information can be ${el("a", { textContent: t`Shadow DOM in Depth`, ...references.shadow_dom_depth })}. + `), + el("p").append(...T` + Besides the encapsulation, the Shadow DOM allows for using the ${el("a", references.mdn_shadow_dom_slot).append( + el("strong", t``), t` element(s)`)}. You can simulate this feature using ${el("code", "simulateSlots")}: + `), + el(example, { src: fileURL("./components/examples/customElement/simulateSlots.js"), page_id }), + el("p").append(...T` + To sum up: + `), + el("ul").append( + el("li").append(...T` + The use of shadow DOM to encapsulate the internal structure of the custom element, which affects how + the custom element can be styled and modified using JavaScript and CSS. + `), + el("li").append(...T` + The ability to access and modify the internal structure of the custom element using JavaScript, which + is affected by the use of shadow DOM and the mode of the shadow DOM. + `), + el("li").append(...T` + The use of slots to allow for the insertion of content from the parent document into the custom + element, which is affected by the use of shadow DOM and the mode of the shadow DOM. + `), + ), el(mnemonic) ); diff --git a/examples/index.js b/examples/index.js index 1d0d24a..9fc6332 100644 --- a/examples/index.js +++ b/examples/index.js @@ -1,18 +1,22 @@ -import { style, el } from './exports.js'; +import { style, el, S } from './exports.js'; document.head.append(style.element); import { fullNameComponent } from './components/fullNameComponent.js'; import { todosComponent } from './components/todosComponent.js'; import { CustomHTMLTestElement, CustomSlottingHTMLElement } from "./components/webComponent.js"; import { thirdParty } from "./components/3rd-party.js"; +const toggle= S(false); document.body.append( el("h1", "Experiments:"), el(fullNameComponent), el(todosComponent), el(CustomHTMLTestElement.tagName, { name: "attr" }), el(thirdParty), - el(CustomSlottingHTMLElement.tagName).append( + el(CustomSlottingHTMLElement.tagName, { onclick: ()=> toggle(!toggle()) }).append( el("strong", { slot: "name", textContent: "Honzo" }), - el("span", "…default slot") + S.el(toggle, is=> is + ? el("span", "…default slot") + : el("span", "…custom slot") + ) ) ); diff --git a/index.d.ts b/index.d.ts index 60c07db..270cdf2 100644 --- a/index.d.ts +++ b/index.d.ts @@ -47,7 +47,7 @@ type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagName type textContent= string | ddeSignal; export function el< TAG extends keyof ExtendedHTMLElementTagNameMap, - EL extends (TAG extends keyof ExtendedHTMLElementTagNameMap ? ExtendedHTMLElementTagNameMap[TAG] : HTMLElement) + EL extends ExtendedHTMLElementTagNameMap[TAG] >( tag_name: TAG, attrs?: ElementAttributes | textContent, @@ -58,7 +58,7 @@ export function el( ): ddeDocumentFragment export function el( tag_name: string, - attrs?: ElementAttributes, + attrs?: ElementAttributes | textContent, ...addons: ddeElementAddon[] ): ddeHTMLElement @@ -101,7 +101,18 @@ export function elNS( export { elNS as createElementNS } export function chainableAppend(el: EL): EL; -export function simulateSlots(el: EL): EL +/** + * Mapper function (optional). Pass for coppying attributes, this is NOT implemented by {@link simulateSlots} itself! + * */ +type simulateSlotsMapper= (body: HTMLSlotElement, el: HTMLElement)=> void; +/** Simulate slots for ddeComponents */ +export function simulateSlots(root: EL, mapper?: simulateSlotsMapper): EL +/** + * Simulate slots in Custom Elements without using `shadowRoot`. + * @param el Custom Element root element + * @param body Body of the custom element + * */ +export function simulateSlots(el: HTMLElement, body: EL, mapper?: simulateSlotsMapper): EL export function dispatchEvent(name: keyof DocumentEventMap | string, options?: EventInit): (element: SupportedElement, data?: any)=> void; diff --git a/src/dom.js b/src/dom.js index fc56e1a..4c5daa5 100644 --- a/src/dom.js +++ b/src/dom.js @@ -67,18 +67,23 @@ export function createElement(tag, attributes, ...addons){ } import { hasOwn } from "./helpers.js"; /** @param {HTMLElement} element @param {HTMLElement} [root] */ -export function simulateSlots(element, root= element, mapper= undefined){ +export function simulateSlots(element, root, mapper){ + if(typeof root!=="object"){ + mapper= root; + root= element; + } const _default= Symbol.for("default"); const slots= Array.from(root.querySelectorAll("slot")) .reduce((out, curr)=> Reflect.set(out, curr.name || _default, curr) && out, {}); const has_d= hasOwn(slots, _default); element.append= new Proxy(element.append, { apply(orig, _, els){ + if(els[0]===root) return orig.apply(element, els); if(!els.length) return element; const d= env.D.createDocumentFragment(); for(const el of els){ - if(!el || !el.slot){ if(has_d) d.appendChild(el); continue; } + if(!el || !el.slot){ if(has_d) d.append(el); continue; } const name= el.slot; const slot= slots[name]; elementAttribute(el, "remove", "slot");