1
0
mirror of https://github.com/jaandrle/deka-dom-el synced 2024-11-21 07:29:37 +01:00

Replace “observable” term with “signal” (#19)

*  refact docs

to make editing (now renaming observables to signal) easier

*   use signal(s) term isntead of observable(s)

*  🔤 version + typo

* 🐛 customElement example (0→S)

* 📺 version in package-lock.json
This commit is contained in:
Jan Andrle 2024-05-22 21:43:49 +02:00 committed by GitHub
parent 4014e79740
commit cd62782c7b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
65 changed files with 1426 additions and 978 deletions

View File

@ -23,7 +23,7 @@ document.body.append(
);
function component({ textContent, className }){
const value= O("onchange");
const value= S("onchange");
return el().append(
el("p", { textContent, className }),
@ -35,8 +35,7 @@ function component({ textContent, className }){
}
```
# Deka DOM Elements
Creating reactive elements, components and Web components using [IDL](https://developer.mozilla.org/en-US/docs/Glossary/IDL)/JavaScript DOM API and signals/observables
([Signals — whats going on behind the scenes | by Ryan Hoffnan | ITNEXT](https://itnext.io/signals-whats-going-on-behind-the-scenes-ec858589ea63), [The Evolution of Signals in JavaScript - DEV Community](https://dev.to/this-is-learning/the-evolution-of-signals-in-javascript-8ob) or [Observer pattern - Wikipedia](https://en.wikipedia.org/wiki/Observer_pattern)).
Creating reactive elements, components and Web components using [IDL](https://developer.mozilla.org/en-US/docs/Glossary/IDL)/JavaScript DOM API and [**signals/observables**](#signals).
## Inspiration and suggested alternatives
- my previous library (mostly used internaly): [jaandrle/dollar_dom_component: Functional DOM components without JSX and virtual DOM.](https://github.com/jaandrle/dollar_dom_component)
@ -53,13 +52,13 @@ Another goal is to proceed in the best spirit of functional programming. This in
pure JavaScript (DOM API) and gradually adding auxiliary functions, ranging from “minor” improvements
to the capability of writing complete declarative reactive UI templates.
As a result, any “internal” function (`assign`, `classListDeclarative`, `on`, `dispatchEvent`, …, `O`, …)
As a result, any “internal” function (`assign`, `classListDeclarative`, `on`, `dispatchEvent`, …, `S`, …)
can be used independently, although they are primarily designed for use in combination. This can also,
hopefully, help in integrating the library into existing projects.
To balance these requirements, numerous compromises have been made. To summarize:
- [ ] Library size: 1015kB minified (the original goal was a maximum of 10kB)
- [x] Optional use of *observables* with the ability to register *your own signals/observables implementation*
- [x] Optional use of *signals* with the ability to register *your own signals/observables implementation*
- [x] *No build step required*
- [x] Preference for a *declarative/functional* approach
- [x] Focus on zero/minimal dependencies
@ -73,3 +72,9 @@ To balance these requirements, numerous compromises have been made. To summarize
- Installation
- npm
- [dist/](dist/) (`https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/`…)
## Signals
- [Signals — whats going on behind the scenes | by Ryan Hoffnan | ITNEXT](https://itnext.io/signals-whats-going-on-behind-the-scenes-ec858589ea63)
- [The Evolution of Signals in JavaScript - DEV Community](https://dev.to/this-is-learning/the-evolution-of-signals-in-javascript-8ob)
- there is also [tc39/proposal-signals: A proposal to add signals to JavaScript.](https://github.com/tc39/proposal-signals)
- [Observer pattern - Wikipedia](https://en.wikipedia.org/wiki/Observer_pattern)

View File

@ -1,14 +1,14 @@
#!/usr/bin/env -S npx nodejsscript
import { bundle as bundleDTS } from "dts-bundler";
const files= [ "index", "index-with-observables" ];
const files= [ "index", "index-with-signals" ];
const filesOut= (file, mark= "esm")=> "dist/"+file.replace("index", mark);
const css= echo.css`
.info{ color: gray; }
`;
$.api("", true)
.option("--minify", "Level of minification [ full (default), partial ]")
.action(async function main({ minify= "full" }){
.option("--minify", "Level of minification [ full, partial (default) ]")
.action(async function main({ minify= "partial" }){
for(const file_root of files){
const file= file_root+".js";
echo("Processing: "+ file);

View File

@ -2,20 +2,21 @@
/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */
echo("Building static documentation files…");
echo("Preparing…");
import { path_target, pages, styles, dispatchEvent } from "../docs_src/ssr.js";
import { path_target, pages as pages_registered, styles, dispatchEvent } from "../docs_src/ssr.js";
import { createHTMl } from "./docs/jsdom.js";
import { register } from "../jsdom.js";
const pkg= s.cat("package.json").xargs(JSON.parse);
for(const info of pages){
const { id }= info;
echo("Collecting list of pages…");
const pages= s.ls($.xdg.main`../docs_src/*.html.js`).map(addPage);
for(const { id, info } of pages){
echo(`Generating ${id}.html…`);
const serverDOM= createHTMl("");
serverDOM.registerGlobally(
"HTMLScriptElement"
);
const { el }= await register(serverDOM.dom);
const { page }= await import(`../docs_src/${id}.html.js`); //→ TODO: important to mention in docs!!!
const { page }= await import(`../docs_src/${id}.html.js`);
serverDOM.document.body.append(
el(page, { pkg, info }),
);
@ -27,3 +28,17 @@ for(const info of pages){
s.echo(styles.content).to(path_target.css+styles.name);
dispatchEvent("onssrend");
echo("Done");
/** @param {`${string}/${string}.html.js`} path */
function addPage(path){
const id= idFromPath(path);
const [ info_pre ]= s.cat(path).match(/(?<=\s*export\s+const\s+info\s*=\s*)\{(.|\s)*?\}(?=;)/gm);
const info= { id, href: id, ...eval(`(${info_pre})`) };
pages_registered.push(info);
return { id, info };
}
/** @param {`${string}/${string}.html.js`} path */
function idFromPath(path){
const file_start= path.lastIndexOf("/");
return path.slice(file_start+1, path.indexOf(".", file_start));
}

View File

@ -1,8 +1,8 @@
//deka-dom-el library is available via global namespace `dde`
(()=> {
// src/observables-common.js
// src/signals-common.js
var k = {
isObservable(t) {
isSignal(t) {
return !1;
},
processReactiveAttribute(t, e, n, r) {
@ -18,7 +18,7 @@ function W(t) {
// src/helpers.js
var T = (...t) => Object.prototype.hasOwnProperty.call(...t);
function A(t) {
function S(t) {
return typeof t > "u";
}
function X(t) {
@ -54,17 +54,17 @@ var d = {
M: globalThis.MutationObserver
};
function lt(t, e, n) {
if (Reflect.set(t, e, n), !!A(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, "");
}
}
var C = "__dde_lifecyclesToEvents", y = "dde:connected", S = "dde:disconnected", P = "dde:attributeChanged";
var C = "__dde_lifecyclesToEvents", _ = "dde:connected", O = "dde:disconnected", M = "dde:attributeChanged";
// src/dom.js
var _ = [{
var A = [{
get scope() {
return d.D.body;
},
@ -72,7 +72,7 @@ var _ = [{
prevent: !0
}], m = {
get current() {
return _[_.length - 1];
return A[A.length - 1];
},
get host() {
return this.current.host;
@ -82,17 +82,17 @@ var _ = [{
return t.prevent = !0, t;
},
get state() {
return [..._];
return [...A];
},
push(t = {}) {
return _.push(Object.assign({}, this.current, { prevent: !1 }, t));
return A.push(Object.assign({}, this.current, { prevent: !1 }, t));
},
pushRoot() {
return _.push(_[0]);
return A.push(A[0]);
},
pop() {
if (_.length !== 1)
return _.pop();
if (A.length !== 1)
return A.pop();
}
};
function Y(...t) {
@ -102,68 +102,68 @@ function ht(t) {
return t.append === Y || (t.appendOriginal = t.append, t.append = Y), t;
}
var $;
function M(t, e, ...n) {
let r = W(this), o = 0, c, s;
switch ((Object(e) !== e || r.isObservable(e)) && (e = { textContent: e }), !0) {
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: (...g) => g.length ? (o === 1 ? n.unshift(...g) : g.forEach((l) => l(s)), void 0) : s }), c = t(e || void 0);
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 = M.mark({
let h = j.mark({
type: "component",
name: t.name,
host: a ? "this" : "parentElement"
});
c.prepend(h), a && (s = h);
c.prepend(h), a && (i = h);
break;
}
case t === "#text":
c = j.call(this, d.D.createTextNode(""), e);
c = P.call(this, d.D.createTextNode(""), e);
break;
case (t === "<>" || !t):
c = j.call(this, d.D.createDocumentFragment(), e);
c = P.call(this, d.D.createDocumentFragment(), e);
break;
case !!$:
c = j.call(this, d.D.createElementNS($, t), e);
c = P.call(this, d.D.createElementNS($, t), e);
break;
case !c:
c = j.call(this, d.D.createElement(t), e);
c = P.call(this, d.D.createElement(t), e);
}
return ht(c), s || (s = c), n.forEach((a) => a(s)), o && m.pop(), o = 2, c;
return ht(c), i || (i = c), n.forEach((a) => a(i)), o && m.pop(), o = 2, c;
}
function Wt(t, e = t, n = void 0) {
let r = Symbol.for("default"), o = Array.from(e.querySelectorAll("slot")).reduce((s, a) => Reflect.set(s, a.name || r, a) && s, {}), c = T(o, r);
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(s, a, h) {
apply(i, a, h) {
if (!h.length)
return t;
let g = d.D.createDocumentFragment();
let v = d.D.createDocumentFragment();
for (let l of h) {
if (!l || !l.slot) {
c && g.appendChild(l);
c && v.appendChild(l);
continue;
}
let x = l.slot, w = o[x];
gt(l, "remove", "slot"), w && (bt(w, l, n), Reflect.deleteProperty(o, x));
let x = l.slot, y = o[x];
vt(l, "remove", "slot"), y && (bt(y, l, n), Reflect.deleteProperty(o, x));
}
return c && (o[r].replaceWith(g), Reflect.deleteProperty(o, r)), t.append = s, t;
return c && (o[r].replaceWith(v), Reflect.deleteProperty(o, r)), t.append = i, t;
}
}), t !== e) {
let s = Array.from(t.childNodes);
s.forEach((a) => a.remove()), t.append(...s);
let i = Array.from(t.childNodes);
i.forEach((a) => a.remove()), t.append(...i);
}
return e;
}
function bt(t, e, n) {
n && n(t, e);
try {
t.replaceWith(j(e, { className: [e.className, t.className], dataset: { ...t.dataset } }));
t.replaceWith(P(e, { className: [e.className, t.className], dataset: { ...t.dataset } }));
} catch {
t.replaceWith(e);
}
}
M.mark = function(t, e = !1) {
j.mark = function(t, e = !1) {
t = Object.entries(t).map(([o, c]) => o + `="${c}"`).join(" ");
let n = e ? "" : "/", r = d.D.createComment(`<dde:mark ${t}${d.ssr}${n}>`);
return e && (r.end = d.D.createComment("</dde:mark>")), r;
@ -172,12 +172,12 @@ function qt(t) {
let e = this;
return function(...r) {
$ = t;
let o = M.call(e, ...r);
let o = j.call(e, ...r);
return $ = void 0, o;
};
}
var U = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: tt } = d;
function j(t, ...e) {
function P(t, ...e) {
if (!e.length)
return t;
U.set(t, rt(t, this));
@ -193,10 +193,10 @@ function nt(t, e, n) {
n,
(a, h) => nt.call(c, t, a, h)
);
let [s] = e;
if (s === "=")
let [i] = e;
if (i === "=")
return r(e.slice(1), n);
if (s === ".")
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);
@ -213,7 +213,7 @@ function nt(t, e, n) {
case "ariaset":
return I(o, n, (a, h) => r("aria-" + a, h));
case "classList":
return vt.call(c, t, n);
return gt.call(c, t, n);
}
return Et(t, e) ? tt(t, e, n) : r(e, n);
}
@ -223,7 +223,7 @@ function rt(t, e) {
let r = (t instanceof d.S ? xt : mt).bind(null, t, "Attribute"), o = W(e);
return { setRemoveAttr: r, s: o };
}
function vt(t, e) {
function gt(t, e) {
let n = W(this);
return I(
n,
@ -234,14 +234,14 @@ function vt(t, e) {
function Ft(t) {
return Array.from(t.children).forEach((e) => e.remove()), t;
}
function gt(t, e, n, r) {
function vt(t, e, n, r) {
return t instanceof d.H ? t[e + "Attribute"](n, r) : t[e + "AttributeNS"](null, n, r);
}
function Et(t, e) {
if (!(e in t))
return !1;
let n = ot(t, e);
return !A(n.set);
return !S(n.set);
}
function ot(t, e) {
if (t = Object.getPrototypeOf(t), !t)
@ -259,141 +259,141 @@ function ct(t) {
return Array.isArray(t) ? t.filter(Boolean).join(" ") : t;
}
function mt(t, e, n, r) {
return t[(A(r) ? "remove" : "set") + e](n, ct(r));
return t[(S(r) ? "remove" : "set") + e](n, ct(r));
}
function xt(t, e, n, r, o = null) {
return t[(A(r) ? "remove" : "set") + e + "NS"](o, n, ct(r));
return t[(S(r) ? "remove" : "set") + e + "NS"](o, n, ct(r));
}
function et(t, e, n) {
if (Reflect.set(t, e, n), !!A(n))
if (Reflect.set(t, e, n), !!S(n))
return Reflect.deleteProperty(t, e);
}
// src/events-observer.js
var D = d.M ? Ot() : new Proxy({}, {
var D = d.M ? wt() : new Proxy({}, {
get() {
return () => {
};
}
});
function Ot() {
let t = /* @__PURE__ */ new Map(), e = !1, n = (i) => function(u) {
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)) {
i();
s();
continue;
}
x(f.removedNodes, !0) && i();
x(f.removedNodes, !0) && s();
}
}, r = new d.M(n(a));
return {
observe(i) {
observe(s) {
let u = new d.M(n(() => {
}));
return u.observe(i, { childList: !0, subtree: !0 }), () => u.disconnect();
return u.observe(s, { childList: !0, subtree: !0 }), () => u.disconnect();
},
onConnected(i, u) {
s();
let f = c(i);
onConnected(s, u) {
i();
let f = c(s);
f.connected.has(u) || (f.connected.add(u), f.length_c += 1);
},
offConnected(i, u) {
if (!t.has(i))
offConnected(s, u) {
if (!t.has(s))
return;
let f = t.get(i);
f.connected.has(u) && (f.connected.delete(u), f.length_c -= 1, o(i, f));
let f = t.get(s);
f.connected.has(u) && (f.connected.delete(u), f.length_c -= 1, o(s, f));
},
onDisconnected(i, u) {
s();
let f = c(i);
onDisconnected(s, u) {
i();
let f = c(s);
f.disconnected.has(u) || (f.disconnected.add(u), f.length_d += 1);
},
offDisconnected(i, u) {
if (!t.has(i))
offDisconnected(s, u) {
if (!t.has(s))
return;
let f = t.get(i);
f.disconnected.has(u) && (f.disconnected.delete(u), f.length_d -= 1, o(i, f));
let f = t.get(s);
f.disconnected.has(u) && (f.disconnected.delete(u), f.length_d -= 1, o(s, f));
}
};
function o(i, u) {
u.length_c || u.length_d || (t.delete(i), a());
function o(s, u) {
u.length_c || u.length_d || (t.delete(s), a());
}
function c(i) {
if (t.has(i))
return t.get(i);
function c(s) {
if (t.has(s))
return t.get(s);
let u = {
connected: /* @__PURE__ */ new WeakSet(),
length_c: 0,
disconnected: /* @__PURE__ */ new WeakSet(),
length_d: 0
};
return t.set(i, u), u;
return t.set(s, u), u;
}
function s() {
function i() {
e || (e = !0, r.observe(d.D.body, { childList: !0, subtree: !0 }));
}
function a() {
!e || t.size || (e = !1, r.disconnect());
}
function h() {
return new Promise(function(i) {
(requestIdleCallback || requestAnimationFrame)(i);
return new Promise(function(s) {
(requestIdleCallback || requestAnimationFrame)(s);
});
}
async function g(i) {
async function v(s) {
t.size > 30 && await h();
let u = [];
if (!(i instanceof Node))
if (!(s instanceof Node))
return u;
for (let f of t.keys())
f === i || !(f instanceof Node) || i.contains(f) && u.push(f);
f === s || !(f instanceof Node) || s.contains(f) && u.push(f);
return u;
}
function l(i, u) {
function l(s, u) {
let f = !1;
for (let b of i) {
if (u && g(b).then(l), !t.has(b))
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(y)), N.connected = /* @__PURE__ */ new WeakSet(), N.length_c = 0, N.length_d || t.delete(b), f = !0);
N.length_c && (b.dispatchEvent(new Event(_)), N.connected = /* @__PURE__ */ new WeakSet(), N.length_c = 0, N.length_d || t.delete(b), f = !0);
}
return f;
}
function x(i, u) {
function x(s, u) {
let f = !1;
for (let b of i)
u && g(b).then(x), !(!t.has(b) || !t.get(b).length_d) && ((globalThis.queueMicrotask || setTimeout)(w(b)), f = !0);
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 w(i) {
function y(s) {
return () => {
i.isConnected || (i.dispatchEvent(new Event(S)), t.delete(i));
s.isConnected || (s.dispatchEvent(new Event(O)), t.delete(s));
};
}
}
// src/customElement.js
function Zt(t, e, n, r = yt) {
function Zt(t, e, n, r = _t) {
m.push({
scope: t,
host: (...s) => s.length ? s.forEach((a) => a(t)) : t
host: (...i) => i.length ? i.forEach((a) => a(t)) : t
}), typeof r == "function" && (r = r.call(t, t));
let o = t[C];
o || wt(t);
o || yt(t);
let c = n.call(t, r);
return o || t.dispatchEvent(new Event(y)), e.nodeType === 11 && typeof e.mode == "string" && t.addEventListener(S, D.observe(e), { once: !0 }), m.pop(), e.append(c);
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 wt(t) {
function yt(t) {
return J(t.prototype, "connectedCallback", function(e, n, r) {
e.apply(n, r), n.dispatchEvent(new Event(y));
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(S))
() => !n.isConnected && n.dispatchEvent(new Event(O))
);
}), J(t.prototype, "attributeChangedCallback", function(e, n, r) {
let [o, , c] = r;
n.dispatchEvent(new CustomEvent(P, {
n.dispatchEvent(new CustomEvent(M, {
detail: [o, c]
})), e.apply(n, r);
}), t.prototype[C] = !0, t;
@ -402,7 +402,7 @@ function J(t, e, n) {
t[e] = new Proxy(t[e] || (() => {
}), { apply: n });
}
function yt(t) {
function _t(t) {
return F(t, (e, n) => e.getAttribute(n));
}
@ -410,50 +410,50 @@ function yt(t) {
function Qt(t, e, n) {
return e || (e = {}), function(o, ...c) {
n && (c.unshift(o), o = typeof n == "function" ? n() : n);
let s = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e);
return o.dispatchEvent(s);
let i = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e);
return o.dispatchEvent(i);
};
}
function O(t, e, n) {
function w(t, e, n) {
return function(o) {
return o.addEventListener(t, e, n), o;
};
}
var st = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 });
O.connected = function(t, e) {
return e = st(e), function(r) {
return r.addEventListener(y, t, e), r[C] ? r : r.isConnected ? (r.dispatchEvent(new Event(y)), r) : (q(e.signal, () => D.offConnected(r, t)) && D.onConnected(r, t), r);
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);
};
};
O.disconnected = function(t, e) {
return e = st(e), function(r) {
return r.addEventListener(S, t, e), r[C] || q(e.signal, () => D.offDisconnected(r, t)) && D.onDisconnected(r, t), r;
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;
};
};
var Z = /* @__PURE__ */ new WeakMap();
O.disconnectedAsAbort = function(t) {
w.disconnectedAsAbort = function(t) {
if (Z.has(t))
return Z.get(t);
let e = new AbortController();
return Z.set(t, e), t(O.disconnected(() => e.abort())), e;
return Z.set(t, e), t(w.disconnected(() => e.abort())), e;
};
var _t = /* @__PURE__ */ new WeakSet();
O.attributeChanged = function(t, e) {
var At = /* @__PURE__ */ new WeakSet();
w.attributeChanged = function(t, e) {
return typeof e != "object" && (e = {}), function(r) {
if (r.addEventListener(P, t, e), r[C] || _t.has(r) || !d.M)
if (r.addEventListener(M, t, e), r[C] || At.has(r) || !d.M)
return r;
let o = new d.M(function(s) {
for (let { attributeName: a, target: h } of s)
let o = new d.M(function(i) {
for (let { attributeName: a, target: h } of i)
h.dispatchEvent(
new CustomEvent(P, { detail: [a, h.getAttribute(a)] })
new CustomEvent(M, { detail: [a, h.getAttribute(a)] })
);
});
return q(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r;
};
};
// src/observables-lib.js
var p = "__dde_observable";
// src/signals-lib.js
var p = "__dde_signal";
function z(t) {
try {
return T(t, p);
@ -461,21 +461,21 @@ function z(t) {
return !1;
}
}
var H = [], v = /* @__PURE__ */ new WeakMap();
var H = [], g = /* @__PURE__ */ new WeakMap();
function E(t, e) {
if (typeof t != "function")
return it(!1, t, e);
return st(!1, t, e);
if (z(t))
return t;
let n = it(!0), r = function() {
let [o, ...c] = v.get(r);
if (v.set(r, /* @__PURE__ */ new Set([o])), H.push(r), dt(n, t()), H.pop(), !c.length)
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 s = v.get(r);
let i = g.get(r);
for (let a of c)
s.has(a) || L(a, r);
i.has(a) || L(a, r);
};
return v.set(n[p], r), v.set(r, /* @__PURE__ */ new Set([n])), r(), n;
return g.set(n[p], r), g.set(r, /* @__PURE__ */ new Set([n])), r(), n;
}
E.action = function(t, e, ...n) {
let r = t[p], { actions: o } = r;
@ -494,8 +494,8 @@ E.on = function t(e, n, r = {}) {
}
};
E.symbols = {
//observable: mark,
onclear: Symbol.for("Observable.onclear")
//signal: mark,
onclear: Symbol.for("Signal.onclear")
};
E.clear = function(...t) {
for (let n of t) {
@ -504,37 +504,37 @@ E.clear = function(...t) {
}
function e(n, r) {
r.listeners.forEach((o) => {
if (r.listeners.delete(o), !v.has(o))
if (r.listeners.delete(o), !g.has(o))
return;
let c = v.get(o);
c.delete(n), !(c.size > 1) && (n.clear(...c), v.delete(o));
let c = g.get(o);
c.delete(n), !(c.size > 1) && (n.clear(...c), g.delete(o));
});
}
};
var R = "__dde_reactive";
E.el = function(t, e) {
let n = M.mark({ type: "reactive" }, !0), r = n.end, o = d.D.createDocumentFragment();
let n = j.mark({ type: "reactive" }, !0), r = n.end, o = d.D.createDocumentFragment();
o.append(n, r);
let { current: c } = m, s = {}, a = (h) => {
let { current: c } = m, i = {}, a = (h) => {
if (!n.parentNode || !r.parentNode)
return L(t, a);
let g = s;
s = {}, m.push(c);
let v = i;
i = {}, m.push(c);
let l = e(h, function(u, f) {
let b;
return T(g, u) ? (b = g[u], delete g[u]) : b = f(), s[u] = b, b;
return T(v, u) ? (b = v[u], delete v[u]) : b = f(), i[u] = b, b;
});
m.pop(), Array.isArray(l) || (l = [l]);
let x = document.createComment("");
l.push(x), n.after(...l);
let w;
for (; (w = x.nextSibling) && w !== r; )
w.remove();
x.remove(), n.isConnected && At(c.host());
let y;
for (; (y = x.nextSibling) && y !== r; )
y.remove();
x.remove(), n.isConnected && St(c.host());
};
return Q(t, a), ft(t, a, n, e), a(t()), o;
};
function At(t) {
function St(t) {
!t || !t[R] || (requestIdleCallback || setTimeout)(function() {
t[R] = t[R].filter(([e, n]) => n.isConnected ? !0 : (L(...e), !1));
});
@ -544,7 +544,7 @@ var Ct = {
this.value = t;
}
};
function St(t) {
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;
@ -552,21 +552,21 @@ function St(t) {
}
var G = "__dde_attributes";
E.observedAttributes = function(t) {
let e = t[G] = {}, n = F(t, St(e));
return O.attributeChanged(function({ detail: o }) {
/*! This maps attributes to observables (`O.observedAttributes`).
let e = t[G] = {}, n = F(t, Ot(e));
return w.attributeChanged(function({ detail: o }) {
/*! This maps attributes to signals (`S.observedAttributes`).
* Investigate `__dde_attributes` key of the element.*/
let [c, s] = o, a = this[G][c];
let [c, i] = o, a = this[G][c];
if (a)
return E.action(a, "_set", s);
})(t), O.disconnected(function() {
/*! This removes all observables mapped to attributes (`O.observedAttributes`).
return E.action(a, "_set", i);
})(t), w.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;
};
var ut = {
isObservable: z,
isSignal: z,
processReactiveAttribute(t, e, n, r) {
if (!z(n))
return n;
@ -581,18 +581,18 @@ var ut = {
function ft(t, e, ...n) {
let { current: r } = m;
r.prevent || r.host(function(o) {
o[R] || (o[R] = [], O.disconnected(
o[R] || (o[R] = [], w.disconnected(
() => (
/*!
* Clears all Observables listeners added in the current scope/host (`O.el`, `assign`, ?).
* 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, s]]) => L(c, s, c[p] && c[p].host && c[p].host() === o))
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 it(t, e, n) {
function st(t, e, n) {
let r = t ? () => K(r) : (...o) => o.length ? dt(r, ...o) : K(r);
return at(r, e, n, t);
}
@ -613,13 +613,13 @@ function at(t, e, n, r = !1) {
X(n) !== "[object Object]" && (n = {});
let { onclear: c } = E.symbols;
n[c] && (o.push(n[c]), delete n[c]);
let { host: s } = m;
let { host: i } = m;
return Reflect.defineProperty(t, p, {
value: {
value: e,
actions: n,
onclear: o,
host: s,
host: i,
listeners: /* @__PURE__ */ new Set(),
defined: new V().stack,
readonly: r
@ -636,7 +636,7 @@ function K(t) {
if (!t[p])
return;
let { value: e, listeners: n } = t[p], r = Rt();
return r && n.add(r), v.has(r) && v.get(r).add(t), e;
return r && n.add(r), g.has(r) && g.get(r).add(t), e;
}
function dt(t, e, n) {
if (!t[p])
@ -655,41 +655,41 @@ function L(t, e, n) {
return;
let o = r.listeners.delete(e);
if (n && !r.listeners.size) {
if (E.clear(t), !v.has(r))
if (E.clear(t), !g.has(r))
return o;
let c = v.get(r);
if (!v.has(c))
let c = g.get(r);
if (!g.has(c))
return o;
v.get(c).forEach((s) => L(s, c, !0));
g.get(c).forEach((i) => L(i, c, !0));
}
return o;
}
// observables.js
// signals.js
B(ut);
globalThis.dde= {
O: E,
assign: j,
S: E,
assign: P,
assignAttribute: nt,
chainableAppend: ht,
classListDeclarative: vt,
createElement: M,
classListDeclarative: gt,
createElement: j,
createElementNS: qt,
customElementRender: Zt,
customElementWithDDE: wt,
customElementWithDDE: yt,
dispatchEvent: Qt,
el: M,
el: j,
elNS: qt,
elementAttribute: gt,
elementAttribute: vt,
empty: Ft,
isObservable: z,
lifecyclesToEvents: wt,
observable: E,
observedAttributes: yt,
on: O,
isSignal: z,
lifecyclesToEvents: yt,
observedAttributes: _t,
on: w,
registerReactivity: B,
scope: m,
signal: E,
simulateSlots: Wt
};

88
dist/dde.js vendored
View File

@ -1,8 +1,8 @@
//deka-dom-el library is available via global namespace `dde`
(()=> {
// src/observables-common.js
// src/signals-common.js
var C = {
isObservable(t) {
isSignal(t) {
return !1;
},
processReactiveAttribute(t, e, n, r) {
@ -57,18 +57,18 @@ function K(t, e, n) {
return Reflect.set(t, e, "");
}
}
var x = "__dde_lifecyclesToEvents", v = "dde:connected", y = "dde:disconnected", O = "dde:attributeChanged";
var x = "__dde_lifecyclesToEvents", g = "dde:connected", y = "dde:disconnected", D = "dde:attributeChanged";
// src/dom.js
var g = [{
var v = [{
get scope() {
return a.D.body;
},
host: (t) => t ? t(a.D.body) : a.D.body,
prevent: !0
}], R = {
}], S = {
get current() {
return g[g.length - 1];
return v[v.length - 1];
},
get host() {
return this.current.host;
@ -78,17 +78,17 @@ var g = [{
return t.prevent = !0, t;
},
get state() {
return [...g];
return [...v];
},
push(t = {}) {
return g.push(Object.assign({}, this.current, { prevent: !1 }, t));
return v.push(Object.assign({}, this.current, { prevent: !1 }, t));
},
pushRoot() {
return g.push(g[0]);
return v.push(v[0]);
},
pop() {
if (g.length !== 1)
return g.pop();
if (v.length !== 1)
return v.pop();
}
};
function $(...t) {
@ -100,9 +100,9 @@ function Q(t) {
var T;
function k(t, e, ...n) {
let r = L(this), o = 0, c, f;
switch ((Object(e) !== e || r.isObservable(e)) && (e = { textContent: e }), !0) {
switch ((Object(e) !== e || r.isSignal(e)) && (e = { textContent: e }), !0) {
case typeof t == "function": {
o = 1, R.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);
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;
@ -115,18 +115,18 @@ function k(t, e, ...n) {
break;
}
case t === "#text":
c = D.call(this, a.D.createTextNode(""), e);
c = O.call(this, a.D.createTextNode(""), e);
break;
case (t === "<>" || !t):
c = D.call(this, a.D.createDocumentFragment(), e);
c = O.call(this, a.D.createDocumentFragment(), e);
break;
case !!T:
c = D.call(this, a.D.createElementNS(T, t), e);
c = O.call(this, a.D.createElementNS(T, t), e);
break;
case !c:
c = D.call(this, a.D.createElement(t), e);
c = O.call(this, a.D.createElement(t), e);
}
return Q(c), f || (f = c), n.forEach((d) => d(f)), o && R.pop(), o = 2, c;
return Q(c), f || (f = c), n.forEach((d) => d(f)), o && S.pop(), o = 2, c;
}
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);
@ -154,7 +154,7 @@ function bt(t, e = t, n = void 0) {
function X(t, e, n) {
n && n(t, e);
try {
t.replaceWith(D(e, { className: [e.className, t.className], dataset: { ...t.dataset } }));
t.replaceWith(O(e, { className: [e.className, t.className], dataset: { ...t.dataset } }));
} catch {
t.replaceWith(e);
}
@ -164,7 +164,7 @@ k.mark = function(t, e = !1) {
let n = e ? "" : "/", r = a.D.createComment(`<dde:mark ${t}${a.ssr}${n}>`);
return e && (r.end = a.D.createComment("</dde:mark>")), r;
};
function vt(t) {
function gt(t) {
let e = this;
return function(...r) {
T = t;
@ -173,7 +173,7 @@ function vt(t) {
};
}
var P = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: U } = a;
function D(t, ...e) {
function O(t, ...e) {
if (!e.length)
return t;
P.set(t, B(t, this));
@ -227,7 +227,7 @@ function Y(t, e) {
(r, o) => t.classList.toggle(r, o === -1 ? void 0 : !!o)
), t;
}
function gt(t) {
function vt(t) {
return Array.from(t.children).forEach((e) => e.remove()), t;
}
function tt(t, e, n, r) {
@ -352,7 +352,7 @@ function ot() {
if (u && b(h).then(l), !t.has(h))
continue;
let m = t.get(h);
m.length_c && (h.dispatchEvent(new Event(v)), m.connected = /* @__PURE__ */ new WeakSet(), m.length_c = 0, m.length_d || t.delete(h), s = !0);
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);
}
return s;
}
@ -370,26 +370,26 @@ function ot() {
}
// src/customElement.js
function Ot(t, e, n, r = it) {
R.push({
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(v)), e.nodeType === 11 && typeof e.mode == "string" && t.addEventListener(y, w.observe(e), { once: !0 }), R.pop(), e.append(c);
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 ct(t) {
return W(t.prototype, "connectedCallback", function(e, n, r) {
e.apply(n, r), n.dispatchEvent(new Event(v));
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))
);
}), W(t.prototype, "attributeChangedCallback", function(e, n, r) {
let [o, , c] = r;
n.dispatchEvent(new CustomEvent(O, {
n.dispatchEvent(new CustomEvent(D, {
detail: [o, c]
})), e.apply(n, r);
}), t.prototype[x] = !0, t;
@ -410,38 +410,38 @@ function _t(t, e, n) {
return o.dispatchEvent(f);
};
}
function S(t, e, n) {
function R(t, e, n) {
return function(o) {
return o.addEventListener(t, e, n), o;
};
}
var G = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 });
S.connected = function(t, e) {
R.connected = function(t, e) {
return e = G(e), function(r) {
return r.addEventListener(v, t, e), r[x] ? r : r.isConnected ? (r.dispatchEvent(new Event(v)), r) : (N(e.signal, () => w.offConnected(r, t)) && w.onConnected(r, t), 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);
};
};
S.disconnected = function(t, e) {
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;
};
};
var j = /* @__PURE__ */ new WeakMap();
S.disconnectedAsAbort = function(t) {
R.disconnectedAsAbort = function(t) {
if (j.has(t))
return j.get(t);
let e = new AbortController();
return j.set(t, e), t(S.disconnected(() => e.abort())), e;
return j.set(t, e), t(R.disconnected(() => e.abort())), e;
};
var st = /* @__PURE__ */ new WeakSet();
S.attributeChanged = function(t, e) {
R.attributeChanged = function(t, e) {
return typeof e != "object" && (e = {}), function(r) {
if (r.addEventListener(O, t, e), r[x] || st.has(r) || !a.M)
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(O, { detail: [d, p.getAttribute(d)] })
new CustomEvent(D, { detail: [d, p.getAttribute(d)] })
);
});
return N(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r;
@ -449,24 +449,24 @@ S.attributeChanged = function(t, e) {
};
globalThis.dde= {
assign: D,
assign: O,
assignAttribute: z,
chainableAppend: Q,
classListDeclarative: Y,
createElement: k,
createElementNS: vt,
customElementRender: Ot,
createElementNS: gt,
customElementRender: Dt,
customElementWithDDE: ct,
dispatchEvent: _t,
el: k,
elNS: vt,
elNS: gt,
elementAttribute: tt,
empty: gt,
empty: vt,
lifecyclesToEvents: ct,
observedAttributes: it,
on: S,
on: R,
registerReactivity: V,
scope: R,
scope: S,
simulateSlots: bt
};

View File

@ -1,17 +1,17 @@
export type Observable<V, A>= (set?: V)=> V & A;
type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typeof observable._ | void;
//type SymbolObservable= Symbol;
export type Signal<V, A>= (set?: V)=> V & A;
type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typeof signal._ | void;
//type SymbolSignal= Symbol;
type SymbolOnclear= symbol;
type Actions<V>= Record<string | SymbolOnclear, Action<V>>;
type OnListenerOptions= Pick<AddEventListenerOptions, "signal"> & { first_time?: boolean };
interface observable{
interface signal{
_: Symbol
/**
* Simple example:
* ```js
* const hello= S("Hello Observable");
* const hello= S("Hello Signal");
* ```
* simple todo observable:
* simple todo signal:
* ```js
* const todos= S([], {
* add(v){ this.value.push(S(v)); },
@ -19,48 +19,48 @@ interface observable{
* [S.symbols.onclear](){ S.clear(...this.value); },
* });
* ```
* computed observable:
* computed signal:
* ```js
* const name= S("Jan");
* const surname= S("Andrle");
* const fullname= S(()=> name()+" "+surname());
* ```
* @param value Initial observable value. Or function computing value from other observables.
* @param actions Use to define actions on the observable. Such as add item to the array.
* There is also a reserved function `S.symbol.onclear` which is called when the observable is cleared
* @param value Initial signal value. Or function computing value from other signals.
* @param actions Use to define actions on the signal. Such as add item to the array.
* There is also a reserved function `S.symbol.onclear` which is called when the signal is cleared
* by `S.clear`.
* */
<V, A extends Actions<V>>(value: V, actions?: A): Observable<V, A>;
<V, A extends Actions<V>>(value: V, actions?: A): Signal<V, A>;
/**
* Computations observable. This creates a observable which is computed from other observables.
* Computations signal. This creates a signal which is computed from other signals.
* */
<V>(computation: ()=> V): Observable<V, {}>
action<S extends Observable<any, Actions<any>>, A extends (S extends Observable<any, infer A> ? A : never), N extends keyof A>(
observable: S,
<V>(computation: ()=> V): Signal<V, {}>
action<S extends Signal<any, Actions<any>>, A extends (S extends Signal<any, infer A> ? A : never), N extends keyof A>(
signal: S,
name: N,
...params: A[N] extends (...args: infer P)=> any ? P : never
): void;
clear(...observables: Observable<any, any>[]): void;
on<T>(observable: Observable<T, any>, onchange: (a: T)=> void, options?: OnListenerOptions): void;
clear(...signals: Signal<any, any>[]): void;
on<T>(signal: Signal<T, any>, onchange: (a: T)=> void, options?: OnListenerOptions): void;
symbols: {
//observable: SymbolObservable;
//signal: SymbolSignal;
onclear: SymbolOnclear;
}
/**
* Reactive element, which is rendered based on the given observable.
* Reactive element, which is rendered based on the given signal.
* ```js
* S.el(observable, value=> value ? el("b", "True") : el("i", "False"));
* S.el(signal, value=> value ? el("b", "True") : el("i", "False"));
* S.el(listS, list=> list.map(li=> el("li", li)));
* ```
* */
el<S extends any>(observable: Observable<S, any>, el: (v: S)=> Element | Element[] | DocumentFragment): DocumentFragment;
el<S extends any>(signal: Signal<S, any>, el: (v: S)=> Element | Element[] | DocumentFragment): DocumentFragment;
observedAttributes(custom_element: HTMLElement): Record<string, Observable<any, any>>;
observedAttributes(custom_element: HTMLElement): Record<string, Signal<any, any>>;
}
export const observable: observable;
export const O: observable;
export const signal: signal;
export const S: signal;
declare global {
type ddeObservable<T, A= {}>= Observable<T, A>;
type ddeSignal<T, A= {}>= Signal<T, A>;
type ddeAction<V>= Action<V>
type ddeActions<V>= Actions<V>
}
@ -80,20 +80,20 @@ type AttrsModified= {
/**
* Use string like in HTML (internally uses `*.setAttribute("style", *)`), or object representation (like DOM API).
*/
style: string | Partial<CSSStyleDeclaration> | Observable<string, any> | Partial<{ [K in keyof CSSStyleDeclaration]: Observable<CSSStyleDeclaration[K], any> }>
style: string | Partial<CSSStyleDeclaration> | Signal<string, any> | Partial<{ [K in keyof CSSStyleDeclaration]: Signal<CSSStyleDeclaration[K], any> }>
/**
* Provide option to add/remove/toggle CSS clasess (index of object) using 1/0/-1. In fact `el.classList.toggle(class_name)` for `-1` and `el.classList.toggle(class_name, Boolean(...))` for others.
*/
classList: Record<string,-1|0|1|boolean|Observable<-1|0|1|boolean, any>>,
classList: Record<string,-1|0|1|boolean|Signal<-1|0|1|boolean, any>>,
/**
* By default simiral to `className`, but also supports `string[]`
* */
className: string | (string|boolean|undefined|Observable<string|boolean|undefined, any>)[];
className: string | (string|boolean|undefined|Signal<string|boolean|undefined, any>)[];
/**
* Sets `aria-*` simiraly to `dataset`
* */
ariaset: Record<string,string|Observable<string, any>>,
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|Observable<string, any>> & Record<`.${string}`, any>
ariaset: Record<string,string|Signal<string, any>>,
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|Signal<string, any>> & Record<`.${string}`, any>
type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>;
/**
* Just element attributtes
@ -103,13 +103,13 @@ type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModifi
* There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
* @private
*/
type ElementAttributes<T extends SupportedElement>= Partial<_fromElsInterfaces<T> & { [K in keyof _fromElsInterfaces<T>]: Observable<_fromElsInterfaces<T>[K], any> } & AttrsModified> & Record<string, any>;
type ElementAttributes<T extends SupportedElement>= Partial<_fromElsInterfaces<T> & { [K in keyof _fromElsInterfaces<T>]: Signal<_fromElsInterfaces<T>[K], any> } & AttrsModified> & Record<string, any>;
export function classListDeclarative<El extends SupportedElement>(element: El, classList: AttrsModified["classList"]): El
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(element: El, attr: ATT, value: ElementAttributes<El>[ATT]): ElementAttributes<El>[ATT]
type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap;
type textContent= string | ( (set?: string)=> string ); // TODO: for some reason `Observable<string, any>` leads to `attrs?: any`
type textContent= string | ( (set?: string)=> string ); // TODO: for some reason `Signal<string, any>` leads to `attrs?: any`
export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap & string,
EL extends (TAG extends keyof ExtendedHTMLElementTagNameMap ? ExtendedHTMLElementTagNameMap[TAG] : HTMLElement)
@ -148,7 +148,7 @@ export function elNS(
EL extends ( TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement ),
>(
tag_name: TAG,
attrs?: string | textContent | Partial<{ [key in keyof EL]: EL[key] | Observable<EL[key], any> | string | number | boolean }>,
attrs?: string | textContent | Partial<{ [key in keyof EL]: EL[key] | Signal<EL[key], any> | string | number | boolean }>,
...addons: ddeElementAddon<EL>[]
)=> ddeMathMLElement
export function elNS(

View File

@ -1,6 +1,6 @@
// src/observables-common.js
// src/signals-common.js
var k = {
isObservable(t) {
isSignal(t) {
return !1;
},
processReactiveAttribute(t, e, n, r) {
@ -16,7 +16,7 @@ function W(t) {
// src/helpers.js
var T = (...t) => Object.prototype.hasOwnProperty.call(...t);
function A(t) {
function S(t) {
return typeof t > "u";
}
function X(t) {
@ -52,17 +52,17 @@ var d = {
M: globalThis.MutationObserver
};
function lt(t, e, n) {
if (Reflect.set(t, e, n), !!A(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, "");
}
}
var C = "__dde_lifecyclesToEvents", y = "dde:connected", S = "dde:disconnected", P = "dde:attributeChanged";
var C = "__dde_lifecyclesToEvents", _ = "dde:connected", O = "dde:disconnected", M = "dde:attributeChanged";
// src/dom.js
var _ = [{
var A = [{
get scope() {
return d.D.body;
},
@ -70,7 +70,7 @@ var _ = [{
prevent: !0
}], m = {
get current() {
return _[_.length - 1];
return A[A.length - 1];
},
get host() {
return this.current.host;
@ -80,17 +80,17 @@ var _ = [{
return t.prevent = !0, t;
},
get state() {
return [..._];
return [...A];
},
push(t = {}) {
return _.push(Object.assign({}, this.current, { prevent: !1 }, t));
return A.push(Object.assign({}, this.current, { prevent: !1 }, t));
},
pushRoot() {
return _.push(_[0]);
return A.push(A[0]);
},
pop() {
if (_.length !== 1)
return _.pop();
if (A.length !== 1)
return A.pop();
}
};
function Y(...t) {
@ -100,68 +100,68 @@ function ht(t) {
return t.append === Y || (t.appendOriginal = t.append, t.append = Y), t;
}
var $;
function M(t, e, ...n) {
let r = W(this), o = 0, c, s;
switch ((Object(e) !== e || r.isObservable(e)) && (e = { textContent: e }), !0) {
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: (...g) => g.length ? (o === 1 ? n.unshift(...g) : g.forEach((l) => l(s)), void 0) : s }), c = t(e || void 0);
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 = M.mark({
let h = j.mark({
type: "component",
name: t.name,
host: a ? "this" : "parentElement"
});
c.prepend(h), a && (s = h);
c.prepend(h), a && (i = h);
break;
}
case t === "#text":
c = j.call(this, d.D.createTextNode(""), e);
c = P.call(this, d.D.createTextNode(""), e);
break;
case (t === "<>" || !t):
c = j.call(this, d.D.createDocumentFragment(), e);
c = P.call(this, d.D.createDocumentFragment(), e);
break;
case !!$:
c = j.call(this, d.D.createElementNS($, t), e);
c = P.call(this, d.D.createElementNS($, t), e);
break;
case !c:
c = j.call(this, d.D.createElement(t), e);
c = P.call(this, d.D.createElement(t), e);
}
return ht(c), s || (s = c), n.forEach((a) => a(s)), o && m.pop(), o = 2, c;
return ht(c), i || (i = c), n.forEach((a) => a(i)), o && m.pop(), o = 2, c;
}
function Wt(t, e = t, n = void 0) {
let r = Symbol.for("default"), o = Array.from(e.querySelectorAll("slot")).reduce((s, a) => Reflect.set(s, a.name || r, a) && s, {}), c = T(o, r);
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(s, a, h) {
apply(i, a, h) {
if (!h.length)
return t;
let g = d.D.createDocumentFragment();
let v = d.D.createDocumentFragment();
for (let l of h) {
if (!l || !l.slot) {
c && g.appendChild(l);
c && v.appendChild(l);
continue;
}
let x = l.slot, w = o[x];
gt(l, "remove", "slot"), w && (bt(w, l, n), Reflect.deleteProperty(o, x));
let x = l.slot, y = o[x];
vt(l, "remove", "slot"), y && (bt(y, l, n), Reflect.deleteProperty(o, x));
}
return c && (o[r].replaceWith(g), Reflect.deleteProperty(o, r)), t.append = s, t;
return c && (o[r].replaceWith(v), Reflect.deleteProperty(o, r)), t.append = i, t;
}
}), t !== e) {
let s = Array.from(t.childNodes);
s.forEach((a) => a.remove()), t.append(...s);
let i = Array.from(t.childNodes);
i.forEach((a) => a.remove()), t.append(...i);
}
return e;
}
function bt(t, e, n) {
n && n(t, e);
try {
t.replaceWith(j(e, { className: [e.className, t.className], dataset: { ...t.dataset } }));
t.replaceWith(P(e, { className: [e.className, t.className], dataset: { ...t.dataset } }));
} catch {
t.replaceWith(e);
}
}
M.mark = function(t, e = !1) {
j.mark = function(t, e = !1) {
t = Object.entries(t).map(([o, c]) => o + `="${c}"`).join(" ");
let n = e ? "" : "/", r = d.D.createComment(`<dde:mark ${t}${d.ssr}${n}>`);
return e && (r.end = d.D.createComment("</dde:mark>")), r;
@ -170,12 +170,12 @@ function qt(t) {
let e = this;
return function(...r) {
$ = t;
let o = M.call(e, ...r);
let o = j.call(e, ...r);
return $ = void 0, o;
};
}
var U = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: tt } = d;
function j(t, ...e) {
function P(t, ...e) {
if (!e.length)
return t;
U.set(t, rt(t, this));
@ -191,10 +191,10 @@ function nt(t, e, n) {
n,
(a, h) => nt.call(c, t, a, h)
);
let [s] = e;
if (s === "=")
let [i] = e;
if (i === "=")
return r(e.slice(1), n);
if (s === ".")
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);
@ -211,7 +211,7 @@ function nt(t, e, n) {
case "ariaset":
return I(o, n, (a, h) => r("aria-" + a, h));
case "classList":
return vt.call(c, t, n);
return gt.call(c, t, n);
}
return Et(t, e) ? tt(t, e, n) : r(e, n);
}
@ -221,7 +221,7 @@ function rt(t, e) {
let r = (t instanceof d.S ? xt : mt).bind(null, t, "Attribute"), o = W(e);
return { setRemoveAttr: r, s: o };
}
function vt(t, e) {
function gt(t, e) {
let n = W(this);
return I(
n,
@ -232,14 +232,14 @@ function vt(t, e) {
function Ft(t) {
return Array.from(t.children).forEach((e) => e.remove()), t;
}
function gt(t, e, n, r) {
function vt(t, e, n, r) {
return t instanceof d.H ? t[e + "Attribute"](n, r) : t[e + "AttributeNS"](null, n, r);
}
function Et(t, e) {
if (!(e in t))
return !1;
let n = ot(t, e);
return !A(n.set);
return !S(n.set);
}
function ot(t, e) {
if (t = Object.getPrototypeOf(t), !t)
@ -257,141 +257,141 @@ function ct(t) {
return Array.isArray(t) ? t.filter(Boolean).join(" ") : t;
}
function mt(t, e, n, r) {
return t[(A(r) ? "remove" : "set") + e](n, ct(r));
return t[(S(r) ? "remove" : "set") + e](n, ct(r));
}
function xt(t, e, n, r, o = null) {
return t[(A(r) ? "remove" : "set") + e + "NS"](o, n, ct(r));
return t[(S(r) ? "remove" : "set") + e + "NS"](o, n, ct(r));
}
function et(t, e, n) {
if (Reflect.set(t, e, n), !!A(n))
if (Reflect.set(t, e, n), !!S(n))
return Reflect.deleteProperty(t, e);
}
// src/events-observer.js
var D = d.M ? Ot() : new Proxy({}, {
var D = d.M ? wt() : new Proxy({}, {
get() {
return () => {
};
}
});
function Ot() {
let t = /* @__PURE__ */ new Map(), e = !1, n = (i) => function(u) {
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)) {
i();
s();
continue;
}
x(f.removedNodes, !0) && i();
x(f.removedNodes, !0) && s();
}
}, r = new d.M(n(a));
return {
observe(i) {
observe(s) {
let u = new d.M(n(() => {
}));
return u.observe(i, { childList: !0, subtree: !0 }), () => u.disconnect();
return u.observe(s, { childList: !0, subtree: !0 }), () => u.disconnect();
},
onConnected(i, u) {
s();
let f = c(i);
onConnected(s, u) {
i();
let f = c(s);
f.connected.has(u) || (f.connected.add(u), f.length_c += 1);
},
offConnected(i, u) {
if (!t.has(i))
offConnected(s, u) {
if (!t.has(s))
return;
let f = t.get(i);
f.connected.has(u) && (f.connected.delete(u), f.length_c -= 1, o(i, f));
let f = t.get(s);
f.connected.has(u) && (f.connected.delete(u), f.length_c -= 1, o(s, f));
},
onDisconnected(i, u) {
s();
let f = c(i);
onDisconnected(s, u) {
i();
let f = c(s);
f.disconnected.has(u) || (f.disconnected.add(u), f.length_d += 1);
},
offDisconnected(i, u) {
if (!t.has(i))
offDisconnected(s, u) {
if (!t.has(s))
return;
let f = t.get(i);
f.disconnected.has(u) && (f.disconnected.delete(u), f.length_d -= 1, o(i, f));
let f = t.get(s);
f.disconnected.has(u) && (f.disconnected.delete(u), f.length_d -= 1, o(s, f));
}
};
function o(i, u) {
u.length_c || u.length_d || (t.delete(i), a());
function o(s, u) {
u.length_c || u.length_d || (t.delete(s), a());
}
function c(i) {
if (t.has(i))
return t.get(i);
function c(s) {
if (t.has(s))
return t.get(s);
let u = {
connected: /* @__PURE__ */ new WeakSet(),
length_c: 0,
disconnected: /* @__PURE__ */ new WeakSet(),
length_d: 0
};
return t.set(i, u), u;
return t.set(s, u), u;
}
function s() {
function i() {
e || (e = !0, r.observe(d.D.body, { childList: !0, subtree: !0 }));
}
function a() {
!e || t.size || (e = !1, r.disconnect());
}
function h() {
return new Promise(function(i) {
(requestIdleCallback || requestAnimationFrame)(i);
return new Promise(function(s) {
(requestIdleCallback || requestAnimationFrame)(s);
});
}
async function g(i) {
async function v(s) {
t.size > 30 && await h();
let u = [];
if (!(i instanceof Node))
if (!(s instanceof Node))
return u;
for (let f of t.keys())
f === i || !(f instanceof Node) || i.contains(f) && u.push(f);
f === s || !(f instanceof Node) || s.contains(f) && u.push(f);
return u;
}
function l(i, u) {
function l(s, u) {
let f = !1;
for (let b of i) {
if (u && g(b).then(l), !t.has(b))
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(y)), N.connected = /* @__PURE__ */ new WeakSet(), N.length_c = 0, N.length_d || t.delete(b), f = !0);
N.length_c && (b.dispatchEvent(new Event(_)), N.connected = /* @__PURE__ */ new WeakSet(), N.length_c = 0, N.length_d || t.delete(b), f = !0);
}
return f;
}
function x(i, u) {
function x(s, u) {
let f = !1;
for (let b of i)
u && g(b).then(x), !(!t.has(b) || !t.get(b).length_d) && ((globalThis.queueMicrotask || setTimeout)(w(b)), f = !0);
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 w(i) {
function y(s) {
return () => {
i.isConnected || (i.dispatchEvent(new Event(S)), t.delete(i));
s.isConnected || (s.dispatchEvent(new Event(O)), t.delete(s));
};
}
}
// src/customElement.js
function Zt(t, e, n, r = yt) {
function Zt(t, e, n, r = _t) {
m.push({
scope: t,
host: (...s) => s.length ? s.forEach((a) => a(t)) : t
host: (...i) => i.length ? i.forEach((a) => a(t)) : t
}), typeof r == "function" && (r = r.call(t, t));
let o = t[C];
o || wt(t);
o || yt(t);
let c = n.call(t, r);
return o || t.dispatchEvent(new Event(y)), e.nodeType === 11 && typeof e.mode == "string" && t.addEventListener(S, D.observe(e), { once: !0 }), m.pop(), e.append(c);
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 wt(t) {
function yt(t) {
return J(t.prototype, "connectedCallback", function(e, n, r) {
e.apply(n, r), n.dispatchEvent(new Event(y));
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(S))
() => !n.isConnected && n.dispatchEvent(new Event(O))
);
}), J(t.prototype, "attributeChangedCallback", function(e, n, r) {
let [o, , c] = r;
n.dispatchEvent(new CustomEvent(P, {
n.dispatchEvent(new CustomEvent(M, {
detail: [o, c]
})), e.apply(n, r);
}), t.prototype[C] = !0, t;
@ -400,7 +400,7 @@ function J(t, e, n) {
t[e] = new Proxy(t[e] || (() => {
}), { apply: n });
}
function yt(t) {
function _t(t) {
return F(t, (e, n) => e.getAttribute(n));
}
@ -408,50 +408,50 @@ function yt(t) {
function Qt(t, e, n) {
return e || (e = {}), function(o, ...c) {
n && (c.unshift(o), o = typeof n == "function" ? n() : n);
let s = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e);
return o.dispatchEvent(s);
let i = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e);
return o.dispatchEvent(i);
};
}
function O(t, e, n) {
function w(t, e, n) {
return function(o) {
return o.addEventListener(t, e, n), o;
};
}
var st = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 });
O.connected = function(t, e) {
return e = st(e), function(r) {
return r.addEventListener(y, t, e), r[C] ? r : r.isConnected ? (r.dispatchEvent(new Event(y)), r) : (q(e.signal, () => D.offConnected(r, t)) && D.onConnected(r, t), r);
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);
};
};
O.disconnected = function(t, e) {
return e = st(e), function(r) {
return r.addEventListener(S, t, e), r[C] || q(e.signal, () => D.offDisconnected(r, t)) && D.onDisconnected(r, t), r;
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;
};
};
var Z = /* @__PURE__ */ new WeakMap();
O.disconnectedAsAbort = function(t) {
w.disconnectedAsAbort = function(t) {
if (Z.has(t))
return Z.get(t);
let e = new AbortController();
return Z.set(t, e), t(O.disconnected(() => e.abort())), e;
return Z.set(t, e), t(w.disconnected(() => e.abort())), e;
};
var _t = /* @__PURE__ */ new WeakSet();
O.attributeChanged = function(t, e) {
var At = /* @__PURE__ */ new WeakSet();
w.attributeChanged = function(t, e) {
return typeof e != "object" && (e = {}), function(r) {
if (r.addEventListener(P, t, e), r[C] || _t.has(r) || !d.M)
if (r.addEventListener(M, t, e), r[C] || At.has(r) || !d.M)
return r;
let o = new d.M(function(s) {
for (let { attributeName: a, target: h } of s)
let o = new d.M(function(i) {
for (let { attributeName: a, target: h } of i)
h.dispatchEvent(
new CustomEvent(P, { detail: [a, h.getAttribute(a)] })
new CustomEvent(M, { detail: [a, h.getAttribute(a)] })
);
});
return q(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r;
};
};
// src/observables-lib.js
var p = "__dde_observable";
// src/signals-lib.js
var p = "__dde_signal";
function z(t) {
try {
return T(t, p);
@ -459,21 +459,21 @@ function z(t) {
return !1;
}
}
var H = [], v = /* @__PURE__ */ new WeakMap();
var H = [], g = /* @__PURE__ */ new WeakMap();
function E(t, e) {
if (typeof t != "function")
return it(!1, t, e);
return st(!1, t, e);
if (z(t))
return t;
let n = it(!0), r = function() {
let [o, ...c] = v.get(r);
if (v.set(r, /* @__PURE__ */ new Set([o])), H.push(r), dt(n, t()), H.pop(), !c.length)
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 s = v.get(r);
let i = g.get(r);
for (let a of c)
s.has(a) || L(a, r);
i.has(a) || L(a, r);
};
return v.set(n[p], r), v.set(r, /* @__PURE__ */ new Set([n])), r(), n;
return g.set(n[p], r), g.set(r, /* @__PURE__ */ new Set([n])), r(), n;
}
E.action = function(t, e, ...n) {
let r = t[p], { actions: o } = r;
@ -492,8 +492,8 @@ E.on = function t(e, n, r = {}) {
}
};
E.symbols = {
//observable: mark,
onclear: Symbol.for("Observable.onclear")
//signal: mark,
onclear: Symbol.for("Signal.onclear")
};
E.clear = function(...t) {
for (let n of t) {
@ -502,37 +502,37 @@ E.clear = function(...t) {
}
function e(n, r) {
r.listeners.forEach((o) => {
if (r.listeners.delete(o), !v.has(o))
if (r.listeners.delete(o), !g.has(o))
return;
let c = v.get(o);
c.delete(n), !(c.size > 1) && (n.clear(...c), v.delete(o));
let c = g.get(o);
c.delete(n), !(c.size > 1) && (n.clear(...c), g.delete(o));
});
}
};
var R = "__dde_reactive";
E.el = function(t, e) {
let n = M.mark({ type: "reactive" }, !0), r = n.end, o = d.D.createDocumentFragment();
let n = j.mark({ type: "reactive" }, !0), r = n.end, o = d.D.createDocumentFragment();
o.append(n, r);
let { current: c } = m, s = {}, a = (h) => {
let { current: c } = m, i = {}, a = (h) => {
if (!n.parentNode || !r.parentNode)
return L(t, a);
let g = s;
s = {}, m.push(c);
let v = i;
i = {}, m.push(c);
let l = e(h, function(u, f) {
let b;
return T(g, u) ? (b = g[u], delete g[u]) : b = f(), s[u] = b, b;
return T(v, u) ? (b = v[u], delete v[u]) : b = f(), i[u] = b, b;
});
m.pop(), Array.isArray(l) || (l = [l]);
let x = document.createComment("");
l.push(x), n.after(...l);
let w;
for (; (w = x.nextSibling) && w !== r; )
w.remove();
x.remove(), n.isConnected && At(c.host());
let y;
for (; (y = x.nextSibling) && y !== r; )
y.remove();
x.remove(), n.isConnected && St(c.host());
};
return Q(t, a), ft(t, a, n, e), a(t()), o;
};
function At(t) {
function St(t) {
!t || !t[R] || (requestIdleCallback || setTimeout)(function() {
t[R] = t[R].filter(([e, n]) => n.isConnected ? !0 : (L(...e), !1));
});
@ -542,7 +542,7 @@ var Ct = {
this.value = t;
}
};
function St(t) {
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;
@ -550,21 +550,21 @@ function St(t) {
}
var G = "__dde_attributes";
E.observedAttributes = function(t) {
let e = t[G] = {}, n = F(t, St(e));
return O.attributeChanged(function({ detail: o }) {
/*! This maps attributes to observables (`O.observedAttributes`).
let e = t[G] = {}, n = F(t, Ot(e));
return w.attributeChanged(function({ detail: o }) {
/*! This maps attributes to signals (`S.observedAttributes`).
* Investigate `__dde_attributes` key of the element.*/
let [c, s] = o, a = this[G][c];
let [c, i] = o, a = this[G][c];
if (a)
return E.action(a, "_set", s);
})(t), O.disconnected(function() {
/*! This removes all observables mapped to attributes (`O.observedAttributes`).
return E.action(a, "_set", i);
})(t), w.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;
};
var ut = {
isObservable: z,
isSignal: z,
processReactiveAttribute(t, e, n, r) {
if (!z(n))
return n;
@ -579,18 +579,18 @@ var ut = {
function ft(t, e, ...n) {
let { current: r } = m;
r.prevent || r.host(function(o) {
o[R] || (o[R] = [], O.disconnected(
o[R] || (o[R] = [], w.disconnected(
() => (
/*!
* Clears all Observables listeners added in the current scope/host (`O.el`, `assign`, ?).
* 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, s]]) => L(c, s, c[p] && c[p].host && c[p].host() === o))
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 it(t, e, n) {
function st(t, e, n) {
let r = t ? () => K(r) : (...o) => o.length ? dt(r, ...o) : K(r);
return at(r, e, n, t);
}
@ -611,13 +611,13 @@ function at(t, e, n, r = !1) {
X(n) !== "[object Object]" && (n = {});
let { onclear: c } = E.symbols;
n[c] && (o.push(n[c]), delete n[c]);
let { host: s } = m;
let { host: i } = m;
return Reflect.defineProperty(t, p, {
value: {
value: e,
actions: n,
onclear: o,
host: s,
host: i,
listeners: /* @__PURE__ */ new Set(),
defined: new V().stack,
readonly: r
@ -634,7 +634,7 @@ function K(t) {
if (!t[p])
return;
let { value: e, listeners: n } = t[p], r = Rt();
return r && n.add(r), v.has(r) && v.get(r).add(t), e;
return r && n.add(r), g.has(r) && g.get(r).add(t), e;
}
function dt(t, e, n) {
if (!t[p])
@ -653,39 +653,39 @@ function L(t, e, n) {
return;
let o = r.listeners.delete(e);
if (n && !r.listeners.size) {
if (E.clear(t), !v.has(r))
if (E.clear(t), !g.has(r))
return o;
let c = v.get(r);
if (!v.has(c))
let c = g.get(r);
if (!g.has(c))
return o;
v.get(c).forEach((s) => L(s, c, !0));
g.get(c).forEach((i) => L(i, c, !0));
}
return o;
}
// observables.js
// signals.js
B(ut);
export {
E as O,
j as assign,
E as S,
P as assign,
nt as assignAttribute,
ht as chainableAppend,
vt as classListDeclarative,
M as createElement,
gt as classListDeclarative,
j as createElement,
qt as createElementNS,
Zt as customElementRender,
wt as customElementWithDDE,
yt as customElementWithDDE,
Qt as dispatchEvent,
M as el,
j as el,
qt as elNS,
gt as elementAttribute,
vt as elementAttribute,
Ft as empty,
z as isObservable,
wt as lifecyclesToEvents,
E as observable,
yt as observedAttributes,
O as on,
z as isSignal,
yt as lifecyclesToEvents,
_t as observedAttributes,
w as on,
B as registerReactivity,
m as scope,
E as signal,
Wt as simulateSlots
};

66
dist/esm.d.ts vendored
View File

@ -1,17 +1,17 @@
export type Observable<V, A>= (set?: V)=> V & A;
type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typeof observable._ | void;
//type SymbolObservable= Symbol;
export type Signal<V, A>= (set?: V)=> V & A;
type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typeof signal._ | void;
//type SymbolSignal= Symbol;
type SymbolOnclear= symbol;
type Actions<V>= Record<string | SymbolOnclear, Action<V>>;
type OnListenerOptions= Pick<AddEventListenerOptions, "signal"> & { first_time?: boolean };
interface observable{
interface signal{
_: Symbol
/**
* Simple example:
* ```js
* const hello= S("Hello Observable");
* const hello= S("Hello Signal");
* ```
* simple todo observable:
* simple todo signal:
* ```js
* const todos= S([], {
* add(v){ this.value.push(S(v)); },
@ -19,48 +19,48 @@ interface observable{
* [S.symbols.onclear](){ S.clear(...this.value); },
* });
* ```
* computed observable:
* computed signal:
* ```js
* const name= S("Jan");
* const surname= S("Andrle");
* const fullname= S(()=> name()+" "+surname());
* ```
* @param value Initial observable value. Or function computing value from other observables.
* @param actions Use to define actions on the observable. Such as add item to the array.
* There is also a reserved function `S.symbol.onclear` which is called when the observable is cleared
* @param value Initial signal value. Or function computing value from other signals.
* @param actions Use to define actions on the signal. Such as add item to the array.
* There is also a reserved function `S.symbol.onclear` which is called when the signal is cleared
* by `S.clear`.
* */
<V, A extends Actions<V>>(value: V, actions?: A): Observable<V, A>;
<V, A extends Actions<V>>(value: V, actions?: A): Signal<V, A>;
/**
* Computations observable. This creates a observable which is computed from other observables.
* Computations signal. This creates a signal which is computed from other signals.
* */
<V>(computation: ()=> V): Observable<V, {}>
action<S extends Observable<any, Actions<any>>, A extends (S extends Observable<any, infer A> ? A : never), N extends keyof A>(
observable: S,
<V>(computation: ()=> V): Signal<V, {}>
action<S extends Signal<any, Actions<any>>, A extends (S extends Signal<any, infer A> ? A : never), N extends keyof A>(
signal: S,
name: N,
...params: A[N] extends (...args: infer P)=> any ? P : never
): void;
clear(...observables: Observable<any, any>[]): void;
on<T>(observable: Observable<T, any>, onchange: (a: T)=> void, options?: OnListenerOptions): void;
clear(...signals: Signal<any, any>[]): void;
on<T>(signal: Signal<T, any>, onchange: (a: T)=> void, options?: OnListenerOptions): void;
symbols: {
//observable: SymbolObservable;
//signal: SymbolSignal;
onclear: SymbolOnclear;
}
/**
* Reactive element, which is rendered based on the given observable.
* Reactive element, which is rendered based on the given signal.
* ```js
* S.el(observable, value=> value ? el("b", "True") : el("i", "False"));
* S.el(signal, value=> value ? el("b", "True") : el("i", "False"));
* S.el(listS, list=> list.map(li=> el("li", li)));
* ```
* */
el<S extends any>(observable: Observable<S, any>, el: (v: S)=> Element | Element[] | DocumentFragment): DocumentFragment;
el<S extends any>(signal: Signal<S, any>, el: (v: S)=> Element | Element[] | DocumentFragment): DocumentFragment;
observedAttributes(custom_element: HTMLElement): Record<string, Observable<any, any>>;
observedAttributes(custom_element: HTMLElement): Record<string, Signal<any, any>>;
}
export const observable: observable;
export const O: observable;
export const signal: signal;
export const S: signal;
declare global {
type ddeObservable<T, A= {}>= Observable<T, A>;
type ddeSignal<T, A= {}>= Signal<T, A>;
type ddeAction<V>= Action<V>
type ddeActions<V>= Actions<V>
}
@ -80,20 +80,20 @@ type AttrsModified= {
/**
* Use string like in HTML (internally uses `*.setAttribute("style", *)`), or object representation (like DOM API).
*/
style: string | Partial<CSSStyleDeclaration> | Observable<string, any> | Partial<{ [K in keyof CSSStyleDeclaration]: Observable<CSSStyleDeclaration[K], any> }>
style: string | Partial<CSSStyleDeclaration> | Signal<string, any> | Partial<{ [K in keyof CSSStyleDeclaration]: Signal<CSSStyleDeclaration[K], any> }>
/**
* Provide option to add/remove/toggle CSS clasess (index of object) using 1/0/-1. In fact `el.classList.toggle(class_name)` for `-1` and `el.classList.toggle(class_name, Boolean(...))` for others.
*/
classList: Record<string,-1|0|1|boolean|Observable<-1|0|1|boolean, any>>,
classList: Record<string,-1|0|1|boolean|Signal<-1|0|1|boolean, any>>,
/**
* By default simiral to `className`, but also supports `string[]`
* */
className: string | (string|boolean|undefined|Observable<string|boolean|undefined, any>)[];
className: string | (string|boolean|undefined|Signal<string|boolean|undefined, any>)[];
/**
* Sets `aria-*` simiraly to `dataset`
* */
ariaset: Record<string,string|Observable<string, any>>,
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|Observable<string, any>> & Record<`.${string}`, any>
ariaset: Record<string,string|Signal<string, any>>,
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|Signal<string, any>> & Record<`.${string}`, any>
type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>;
/**
* Just element attributtes
@ -103,13 +103,13 @@ type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModifi
* There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
* @private
*/
type ElementAttributes<T extends SupportedElement>= Partial<_fromElsInterfaces<T> & { [K in keyof _fromElsInterfaces<T>]: Observable<_fromElsInterfaces<T>[K], any> } & AttrsModified> & Record<string, any>;
type ElementAttributes<T extends SupportedElement>= Partial<_fromElsInterfaces<T> & { [K in keyof _fromElsInterfaces<T>]: Signal<_fromElsInterfaces<T>[K], any> } & AttrsModified> & Record<string, any>;
export function classListDeclarative<El extends SupportedElement>(element: El, classList: AttrsModified["classList"]): El
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(element: El, attr: ATT, value: ElementAttributes<El>[ATT]): ElementAttributes<El>[ATT]
type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap;
type textContent= string | ( (set?: string)=> string ); // TODO: for some reason `Observable<string, any>` leads to `attrs?: any`
type textContent= string | ( (set?: string)=> string ); // TODO: for some reason `Signal<string, any>` leads to `attrs?: any`
export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap & string,
EL extends (TAG extends keyof ExtendedHTMLElementTagNameMap ? ExtendedHTMLElementTagNameMap[TAG] : HTMLElement)
@ -148,7 +148,7 @@ export function elNS(
EL extends ( TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement ),
>(
tag_name: TAG,
attrs?: string | textContent | Partial<{ [key in keyof EL]: EL[key] | Observable<EL[key], any> | string | number | boolean }>,
attrs?: string | textContent | Partial<{ [key in keyof EL]: EL[key] | Signal<EL[key], any> | string | number | boolean }>,
...addons: ddeElementAddon<EL>[]
)=> ddeMathMLElement
export function elNS(

88
dist/esm.js vendored
View File

@ -1,6 +1,6 @@
// src/observables-common.js
// src/signals-common.js
var C = {
isObservable(t) {
isSignal(t) {
return !1;
},
processReactiveAttribute(t, e, n, r) {
@ -55,18 +55,18 @@ function K(t, e, n) {
return Reflect.set(t, e, "");
}
}
var x = "__dde_lifecyclesToEvents", v = "dde:connected", y = "dde:disconnected", O = "dde:attributeChanged";
var x = "__dde_lifecyclesToEvents", g = "dde:connected", y = "dde:disconnected", D = "dde:attributeChanged";
// src/dom.js
var g = [{
var v = [{
get scope() {
return a.D.body;
},
host: (t) => t ? t(a.D.body) : a.D.body,
prevent: !0
}], R = {
}], S = {
get current() {
return g[g.length - 1];
return v[v.length - 1];
},
get host() {
return this.current.host;
@ -76,17 +76,17 @@ var g = [{
return t.prevent = !0, t;
},
get state() {
return [...g];
return [...v];
},
push(t = {}) {
return g.push(Object.assign({}, this.current, { prevent: !1 }, t));
return v.push(Object.assign({}, this.current, { prevent: !1 }, t));
},
pushRoot() {
return g.push(g[0]);
return v.push(v[0]);
},
pop() {
if (g.length !== 1)
return g.pop();
if (v.length !== 1)
return v.pop();
}
};
function $(...t) {
@ -98,9 +98,9 @@ function Q(t) {
var T;
function k(t, e, ...n) {
let r = L(this), o = 0, c, f;
switch ((Object(e) !== e || r.isObservable(e)) && (e = { textContent: e }), !0) {
switch ((Object(e) !== e || r.isSignal(e)) && (e = { textContent: e }), !0) {
case typeof t == "function": {
o = 1, R.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);
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;
@ -113,18 +113,18 @@ function k(t, e, ...n) {
break;
}
case t === "#text":
c = D.call(this, a.D.createTextNode(""), e);
c = O.call(this, a.D.createTextNode(""), e);
break;
case (t === "<>" || !t):
c = D.call(this, a.D.createDocumentFragment(), e);
c = O.call(this, a.D.createDocumentFragment(), e);
break;
case !!T:
c = D.call(this, a.D.createElementNS(T, t), e);
c = O.call(this, a.D.createElementNS(T, t), e);
break;
case !c:
c = D.call(this, a.D.createElement(t), e);
c = O.call(this, a.D.createElement(t), e);
}
return Q(c), f || (f = c), n.forEach((d) => d(f)), o && R.pop(), o = 2, c;
return Q(c), f || (f = c), n.forEach((d) => d(f)), o && S.pop(), o = 2, c;
}
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);
@ -152,7 +152,7 @@ function bt(t, e = t, n = void 0) {
function X(t, e, n) {
n && n(t, e);
try {
t.replaceWith(D(e, { className: [e.className, t.className], dataset: { ...t.dataset } }));
t.replaceWith(O(e, { className: [e.className, t.className], dataset: { ...t.dataset } }));
} catch {
t.replaceWith(e);
}
@ -162,7 +162,7 @@ k.mark = function(t, e = !1) {
let n = e ? "" : "/", r = a.D.createComment(`<dde:mark ${t}${a.ssr}${n}>`);
return e && (r.end = a.D.createComment("</dde:mark>")), r;
};
function vt(t) {
function gt(t) {
let e = this;
return function(...r) {
T = t;
@ -171,7 +171,7 @@ function vt(t) {
};
}
var P = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: U } = a;
function D(t, ...e) {
function O(t, ...e) {
if (!e.length)
return t;
P.set(t, B(t, this));
@ -225,7 +225,7 @@ function Y(t, e) {
(r, o) => t.classList.toggle(r, o === -1 ? void 0 : !!o)
), t;
}
function gt(t) {
function vt(t) {
return Array.from(t.children).forEach((e) => e.remove()), t;
}
function tt(t, e, n, r) {
@ -350,7 +350,7 @@ function ot() {
if (u && b(h).then(l), !t.has(h))
continue;
let m = t.get(h);
m.length_c && (h.dispatchEvent(new Event(v)), m.connected = /* @__PURE__ */ new WeakSet(), m.length_c = 0, m.length_d || t.delete(h), s = !0);
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);
}
return s;
}
@ -368,26 +368,26 @@ function ot() {
}
// src/customElement.js
function Ot(t, e, n, r = it) {
R.push({
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(v)), e.nodeType === 11 && typeof e.mode == "string" && t.addEventListener(y, w.observe(e), { once: !0 }), R.pop(), e.append(c);
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 ct(t) {
return W(t.prototype, "connectedCallback", function(e, n, r) {
e.apply(n, r), n.dispatchEvent(new Event(v));
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))
);
}), W(t.prototype, "attributeChangedCallback", function(e, n, r) {
let [o, , c] = r;
n.dispatchEvent(new CustomEvent(O, {
n.dispatchEvent(new CustomEvent(D, {
detail: [o, c]
})), e.apply(n, r);
}), t.prototype[x] = !0, t;
@ -408,61 +408,61 @@ function _t(t, e, n) {
return o.dispatchEvent(f);
};
}
function S(t, e, n) {
function R(t, e, n) {
return function(o) {
return o.addEventListener(t, e, n), o;
};
}
var G = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 });
S.connected = function(t, e) {
R.connected = function(t, e) {
return e = G(e), function(r) {
return r.addEventListener(v, t, e), r[x] ? r : r.isConnected ? (r.dispatchEvent(new Event(v)), r) : (N(e.signal, () => w.offConnected(r, t)) && w.onConnected(r, t), 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);
};
};
S.disconnected = function(t, e) {
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;
};
};
var j = /* @__PURE__ */ new WeakMap();
S.disconnectedAsAbort = function(t) {
R.disconnectedAsAbort = function(t) {
if (j.has(t))
return j.get(t);
let e = new AbortController();
return j.set(t, e), t(S.disconnected(() => e.abort())), e;
return j.set(t, e), t(R.disconnected(() => e.abort())), e;
};
var st = /* @__PURE__ */ new WeakSet();
S.attributeChanged = function(t, e) {
R.attributeChanged = function(t, e) {
return typeof e != "object" && (e = {}), function(r) {
if (r.addEventListener(O, t, e), r[x] || st.has(r) || !a.M)
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(O, { detail: [d, p.getAttribute(d)] })
new CustomEvent(D, { detail: [d, p.getAttribute(d)] })
);
});
return N(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r;
};
};
export {
D as assign,
O as assign,
z as assignAttribute,
Q as chainableAppend,
Y as classListDeclarative,
k as createElement,
vt as createElementNS,
Ot as customElementRender,
gt as createElementNS,
Dt as customElementRender,
ct as customElementWithDDE,
_t as dispatchEvent,
k as el,
vt as elNS,
gt as elNS,
tt as elementAttribute,
gt as empty,
vt as empty,
ct as lifecyclesToEvents,
it as observedAttributes,
S as on,
R as on,
V as registerReactivity,
R as scope,
S as scope,
bt as simulateSlots
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,12 @@
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="Using custom elements in combinantion with DDE"><title>`deka-dom-el` — Custom elements</title><!--<dde:mark type="component" name="metaAuthor" host="this" ssr/>--><meta name="author" content="Jan Andrle"><link type="text/plain" rel="author" href="https://jaandrle.github.io/humans.txt"><meta name="generator" content="deka-dom-el"><!--<dde:mark type="component" name="metaTwitter" host="this" ssr/>--><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="twitter:title" content="deka-dom-el"><meta name="twitter:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="twitter:creator" content="@jaandrle"><!--<dde:mark type="component" name="metaFacebook" host="this" ssr/>--><meta name="og:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="og:title" content="deka-dom-el"><meta name="og:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="og:creator" content="@jaandrle"><script src="https://cdn.jsdelivr.net/npm/shiki@0.9" defer=""></script><script type="module" src="code.js.js"></script><link rel="stylesheet" href="global.css"></head><body><!--<dde:mark type="component" name="page" host="this" ssr/>--><!--<dde:mark type="component" name="simplePage" host="this" ssr/>--><!--<dde:mark type="component" name="header" host="this" ssr/>--><header><h1>`deka-dom-el` — Custom elements</h1><p>Using custom elements in combinantion with DDE</p></header><nav><a href="https://github.com/jaandrle/deka-dom-el"><svg class="icon" viewBox="0 0 32 32"><!--<dde:mark type="component" name="iconGitHub" host="parentElement" ssr/>--><path d="M 16,0.395c -8.836,0 -16,7.163 -16,16c 0,7.069 4.585,13.067 10.942,15.182c 0.8,0.148 1.094,-0.347 1.094,-0.77c 0,-0.381 -0.015,-1.642 -0.022,-2.979c -4.452,0.968 -5.391,-1.888 -5.391,-1.888c -0.728,-1.849 -1.776,-2.341 -1.776,-2.341c -1.452,-0.993 0.11,-0.973 0.11,-0.973c 1.606,0.113 2.452,1.649 2.452,1.649c 1.427,2.446 3.743,1.739 4.656,1.33c 0.143,-1.034 0.558,-1.74 1.016,-2.14c -3.554,-0.404 -7.29,-1.777 -7.29,-7.907c 0,-1.747 0.625,-3.174 1.649,-4.295c -0.166,-0.403 -0.714,-2.03 0.155,-4.234c 0,0 1.344,-0.43 4.401,1.64c 1.276,-0.355 2.645,-0.532 4.005,-0.539c 1.359,0.006 2.729,0.184 4.008,0.539c 3.054,-2.07 4.395,-1.64 4.395,-1.64c 0.871,2.204 0.323,3.831 0.157,4.234c 1.026,1.12 1.647,2.548 1.647,4.295c 0,6.145 -3.743,7.498 -7.306,7.895c 0.574,0.497 1.085,1.47 1.085,2.963c 0,2.141 -0.019,3.864 -0.019,4.391c 0,0.426 0.288,0.925 1.099,0.768c 6.354,-2.118 10.933,-8.113 10.933,-15.18c 0,-8.837 -7.164,-16 -16,-16Z"></path></svg>GitHub</a><a href="./" title="Introducing a&nbsp;library.">1. Introduction</a><a href="p02-elements" title="Basic concepts of elements modifications and creations.">2. Elements</a><a href="p03-events" title="Using not only events in UI declaratively.">3. Events and Addons</a><a href="p04-observables" title="Handling reactivity in UI via observables.">4. Observables and reactivity</a><a href="p05-scopes" title="Organizing UI into components">5. Scopes and components</a><a href="p06-customElement" title="Using custom elements in combinantion with DDE" class="current">6. Custom elements</a></nav><main><h2>Using custom elements in combinantion with DDE</h2><p></p><div class="code" data-js="todo"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="Using custom elements in combinantion with DDE"><title>`deka-dom-el` — Custom elements</title><!--<dde:mark type="component" name="metaAuthor" host="this" ssr/>--><meta name="author" content="Jan Andrle"><link type="text/plain" rel="author" href="https://jaandrle.github.io/humans.txt"><meta name="generator" content="deka-dom-el"><!--<dde:mark type="component" name="metaTwitter" host="this" ssr/>--><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="twitter:title" content="deka-dom-el"><meta name="twitter:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="twitter:creator" content="@jaandrle"><!--<dde:mark type="component" name="metaFacebook" host="this" ssr/>--><meta name="og:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="og:title" content="deka-dom-el"><meta name="og:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="og:creator" content="@jaandrle"><script src="https://cdn.jsdelivr.net/npm/shiki@0.9" defer=""></script><script type="module" src="code.js.js"></script><link rel="stylesheet" href="global.css"></head><body><!--<dde:mark type="component" name="page" host="this" ssr/>--><!--<dde:mark type="component" name="simplePage" host="this" ssr/>--><!--<dde:mark type="component" name="header" host="this" ssr/>--><header><h1>`deka-dom-el` — Custom elements</h1><p>Using custom elements in combinantion with DDE</p></header><nav><a href="https://github.com/jaandrle/deka-dom-el"><svg class="icon" viewBox="0 0 32 32"><!--<dde:mark type="component" name="iconGitHub" host="parentElement" ssr/>--><path d="M 16,0.395c -8.836,0 -16,7.163 -16,16c 0,7.069 4.585,13.067 10.942,15.182c 0.8,0.148 1.094,-0.347 1.094,-0.77c 0,-0.381 -0.015,-1.642 -0.022,-2.979c -4.452,0.968 -5.391,-1.888 -5.391,-1.888c -0.728,-1.849 -1.776,-2.341 -1.776,-2.341c -1.452,-0.993 0.11,-0.973 0.11,-0.973c 1.606,0.113 2.452,1.649 2.452,1.649c 1.427,2.446 3.743,1.739 4.656,1.33c 0.143,-1.034 0.558,-1.74 1.016,-2.14c -3.554,-0.404 -7.29,-1.777 -7.29,-7.907c 0,-1.747 0.625,-3.174 1.649,-4.295c -0.166,-0.403 -0.714,-2.03 0.155,-4.234c 0,0 1.344,-0.43 4.401,1.64c 1.276,-0.355 2.645,-0.532 4.005,-0.539c 1.359,0.006 2.729,0.184 4.008,0.539c 3.054,-2.07 4.395,-1.64 4.395,-1.64c 0.871,2.204 0.323,3.831 0.157,4.234c 1.026,1.12 1.647,2.548 1.647,4.295c 0,6.145 -3.743,7.498 -7.306,7.895c 0.574,0.497 1.085,1.47 1.085,2.963c 0,2.141 -0.019,3.864 -0.019,4.391c 0,0.426 0.288,0.925 1.099,0.768c 6.354,-2.118 10.933,-8.113 10.933,-15.18c 0,-8.837 -7.164,-16 -16,-16Z"></path></svg>GitHub</a><a href="./" title="Introducing a&nbsp;library.">1. Introduction</a><a href="p02-elements" title="Basic concepts of elements modifications and creations.">2. Elements</a><a href="p03-events" title="Using not only events in UI declaratively.">3. Events and Addons</a><a href="p04-signals" title="Handling reactivity in UI via signals.">4. Signals and reactivity</a><a href="p05-scopes" title="Organizing UI into components">5. Scopes and components</a><a href="p06-customElement" title="Using custom elements in combinantion with DDE" class="current">6. Custom elements</a></nav><main><h2>Using custom elements in combinantion with DDE</h2><p></p><div class="code" data-js="todo"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js
import {
customElementRender,
customElementWithDDE,
observedAttributes,
} from "deka-dom-el";
/** @type {ddePublicElementTagNameMap} */
import { O } from "deka-dom-el/observables";
O.observedAttributes;
import { O as S } from "deka-dom-el/signals";
S.observedAttributes;
// “internal” utils
import { lifecyclesToEvents } from "deka-dom-el";
@ -31,4 +31,4 @@ import { lifecyclesToEvents } from "deka-dom-el";
set customAttribute(value){ this.setAttribute("custom-attribute", value); }
}
customElements.define(CustomHTMLElement.tagName, CustomHTMLElement);
</code></div><p><a href="https://gist.github.com/WebReflection/ec9f6687842aa385477c4afca625bbf4" title="Ideas and tips from WebReflection">Handy Custom Elements' Patterns</a></p><div class="notice"><!--<dde:mark type="component" name="mnemonic" host="parentElement" ssr/>--><h3 id="h-mnemonic"><!--<dde:mark type="component" name="h3" host="parentElement" ssr/>--><a href="#h-mnemonic" tabindex="-1">#</a> Mnemonic</h3><ul><li><code>customElementRender(&lt;custom-element&gt;, &lt;render-function&gt;[, &lt;properties&gt;])</code> — use function to render DOM structure for given &lt;custom-element&gt;</li><li><code>customElementWithDDE(&lt;custom-element&gt;)</code> — register &lt;custom-element&gt; to DDE library, see also `lifecyclesToEvents`, can be also used as decorator</li><li><code>observedAttributes(&lt;custom-element&gt;)</code> — returns record of observed attributes (keys uses camelCase)</li><li><code>O.observedAttributes(&lt;custom-element&gt;)</code> — returns record of observed attributes (keys uses camelCase and values are observables)</li><li><code>lifecyclesToEvents(&lt;class-declaration&gt;)</code> — convert lifecycle methods to events, can be also used as decorator</li></ul></div><div class="prevNext"><!--<dde:mark type="component" name="prevNext" host="parentElement" ssr/>--><a rel="prev" href="p05-scopes" title="Organizing UI into components"><!--<dde:mark type="component" name="pageLink" host="parentElement" ssr/>-->Scopes and components (previous)</a><!--<dde:mark type="component" name="pageLink" host="this" ssr/>--></div></main></body></html>
</code></div><p><a href="https://gist.github.com/WebReflection/ec9f6687842aa385477c4afca625bbf4" title="Ideas and tips from WebReflection">Handy Custom Elements' Patterns</a></p><div class="notice"><!--<dde:mark type="component" name="mnemonic" host="parentElement" ssr/>--><h3 id="h-mnemonic"><!--<dde:mark type="component" name="h3" host="parentElement" ssr/>--><a href="#h-mnemonic" tabindex="-1">#</a> Mnemonic</h3><ul><li><code>customElementRender(&lt;custom-element&gt;, &lt;render-function&gt;[, &lt;properties&gt;])</code> — use function to render DOM structure for given &lt;custom-element&gt;</li><li><code>customElementWithDDE(&lt;custom-element&gt;)</code> — register &lt;custom-element&gt; to DDE library, see also `lifecyclesToEvents`, can be also used as decorator</li><li><code>observedAttributes(&lt;custom-element&gt;)</code> — returns record of observed attributes (keys uses camelCase)</li><li><code>S.observedAttributes(&lt;custom-element&gt;)</code> — returns record of observed attributes (keys uses camelCase and values are signals)</li><li><code>lifecyclesToEvents(&lt;class-declaration&gt;)</code> — convert lifecycle methods to events, can be also used as decorator</li></ul></div><div class="prevNext"><!--<dde:mark type="component" name="prevNext" host="parentElement" ssr/>--><a rel="prev" href="p05-scopes" title="Organizing UI into components"><!--<dde:mark type="component" name="pageLink" host="parentElement" ssr/>-->Scopes and components (previous)</a><!--<dde:mark type="component" name="pageLink" host="this" ssr/>--></div></main></body></html>

View File

@ -14,7 +14,7 @@ ${host}{
}
`;
const dde_content= s.cat(new URL("../../dist/esm-with-observables.js", import.meta.url)).toString();
const dde_content= s.cat(new URL("../../dist/esm-with-signals.js", import.meta.url)).toString();
import { el } from "deka-dom-el";
import { code } from "./code.html.js";
@ -29,7 +29,7 @@ import { relative } from "node:path";
export function example({ src, language= "js", page_id }){
registerClientPart(page_id);
const content= s.cat(src).toString()
.replaceAll(/ from "deka-dom-el(\/observables)?";/g, ' from "./esm-with-observables.js";');
.replaceAll(/ from "deka-dom-el(\/signals)?";/g, ' from "./esm-with-signals.js";');
const id= "code-example-"+generateCodeId(src);
return el().append(
el(code, { id, content, language, className: example.name }),
@ -38,7 +38,7 @@ export function example({ src, language= "js", page_id }){
}
function elCode({ id, content, extension: name }){
const options= JSON.stringify({
files: [{ name, content }, { name: "esm-with-observables.js", content: dde_content }],
files: [{ name, content }, { name: "esm-with-signals.js", content: dde_content }],
toolbar: false
});
return el("script", `Flems(document.getElementById("${id}"), JSON.parse(${JSON.stringify(options)}));`);

View File

@ -1,12 +1,12 @@
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js
import {
customElementRender,
customElementWithDDE,
observedAttributes,
} from "deka-dom-el";
/** @type {ddePublicElementTagNameMap} */
import { O } from "deka-dom-el/observables";
O.observedAttributes;
import { S } from "deka-dom-el/signals";
S.observedAttributes;
// “internal” utils
import { lifecyclesToEvents } from "deka-dom-el";

View File

@ -1,9 +1,9 @@
import { el } from "deka-dom-el";
import { O } from "deka-dom-el/observables";
const clicks= O(0);
import { S } from "deka-dom-el/signals";
const clicks= S(0);
document.body.append(
el().append(
el("p", O(()=>
el("p", S(()=>
"Hello World "+"🎉".repeat(clicks())
)),
el("button", {

View File

@ -1,16 +0,0 @@
import { O } from "deka-dom-el/observables";
const observable= O(0);
// computation pattern
const double= O(()=> 2*observable());
const ac= new AbortController();
O.on(observable, v=> console.log("observable", v), { signal: ac.signal });
O.on(double, v=> console.log("double", v), { signal: ac.signal });
observable(observable()+1);
const interval= 5 * 1000;
const id= setInterval(()=> observable(observable()+1), interval);
ac.signal.addEventListener("abort",
()=> setTimeout(()=> clearInterval(id), 2*interval));
setTimeout(()=> ac.abort(), 3*interval)

View File

@ -1,6 +0,0 @@
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js
import { O, observable } from "deka-dom-el/observables";
O===observable
/** @type {ddeObservable} */
/** @type {ddeAction} */
/** @type {ddeActions} */

View File

@ -1,12 +0,0 @@
import { O } from "deka-dom-el/observables";
// α — `observable` represents a reactive value
const observable= O(0);
// β — just reacts on observable changes
O.on(observable, console.log);
// γ — just updates the value
const update= ()=> observable(observable()+1);
update();
const interval= 5*1000;
setTimeout(clearInterval, 10*interval,
setInterval(update, interval));

View File

@ -7,9 +7,9 @@ document.body.append(
type: "button"
})
);
import { O } from "deka-dom-el/observables";
import { S } from "deka-dom-el/signals";
function component(){
const textContent= O("Click to change text.");
const textContent= S("Click to change text.");
const onclickChange= on("click", function redispatch(){
textContent("Text changed! "+(new Date()).toString())

View File

@ -1,10 +1,10 @@
/* PSEUDO-CODE!!! */
import { el } from "deka-dom-el";
import { O } from "deka-dom-el/observables";
import { S } from "deka-dom-el/signals";
function component(){
/* prepare changeable data */
const dataA= O("data");
const dataB= O("data");
const dataA= S("data");
const dataB= S("data");
/* define data flow (can be asynchronous) */
fetchAPI().then(data_new=> dataA(data_new));
setTimeout(()=> dataB("DATA"));
@ -17,17 +17,17 @@ function component(){
}),
el("ul").append(
/* declarative element(s) */
O.el(dataA, data=> data.map(d=> el("li", d)))
S.el(dataA, data=> data.map(d=> el("li", d)))
),
el("ul").append(
/* declarative component(s) */
O.el(dataA, data=> data.map(d=> el(subcomponent, d)))
S.el(dataA, data=> data.map(d=> el(subcomponent, d)))
)
);
}
function subcomponent({ id }){
/* prepare changeable data */
const textContent= O("…");
const textContent= S("…");
/* define data flow (can be asynchronous) */
fetchAPI(id).then(text=> textContent(text));
/* declarative UI */

View File

@ -1,3 +1,3 @@
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js
import { scope, el } from "deka-dom-el";
/** @type {ddeElementAddon} */

View File

@ -1,14 +1,14 @@
import { O } from "deka-dom-el/observables";
const observable= O(0, {
import { S } from "deka-dom-el/signals";
const signal= S(0, {
increaseOnlyOdd(add){
console.info(add);
if(add%2 === 0) return this.stopPropagation();
this.value+= add;
}
});
O.on(observable, console.log);
S.on(signal, console.log);
const oninterval= ()=>
O.action(observable, "increaseOnlyOdd", Math.floor(Math.random()*100));
S.action(signal, "increaseOnlyOdd", Math.floor(Math.random()*100));
const interval= 5*1000;
setTimeout(

View File

@ -1,14 +1,14 @@
import { O } from "deka-dom-el/observables";
const todos= O([], {
import { S } from "deka-dom-el/signals";
const todos= S([], {
push(item){
this.value.push(O(item));
this.value.push(S(item));
},
pop(){
const removed= this.value.pop();
if(removed) O.clear(removed);
if(removed) S.clear(removed);
},
[O.symbols.onclear](){ // this covers `O.clear(todos)`
O.clear(...this.value);
[S.symbols.onclear](){ // this covers `O.clear(todos)`
S.clear(...this.value);
}
});
@ -19,7 +19,7 @@ const onsubmit= on("submit", function(event){
const data= new FormData(this);
switch (data.get("op")){
case "A"/*dd*/:
O.action(todos, "push", data.get("todo"));
S.action(todos, "push", data.get("todo"));
break;
case "E"/*dit*/: {
const last= todos().at(-1);
@ -28,13 +28,13 @@ const onsubmit= on("submit", function(event){
break;
}
case "R"/*emove*/:
O.action(todos, "pop");
S.action(todos, "pop");
break;
}
});
document.body.append(
el("ul").append(
O.el(todos, todos=>
S.el(todos, todos=>
todos.map(textContent=> el("li", textContent)))
),
el("form", null, onsubmit).append(

View File

@ -0,0 +1,16 @@
import { S } from "deka-dom-el/signals";
const signal= S(0);
// computation pattern
const double= S(()=> 2*signal());
const ac= new AbortController();
S.on(signal, v=> console.log("signal", v), { signal: ac.signal });
S.on(double, v=> console.log("double", v), { signal: ac.signal });
signal(signal()+1);
const interval= 5 * 1000;
const id= setInterval(()=> signal(signal()+1), interval);
ac.signal.addEventListener("abort",
()=> setTimeout(()=> clearInterval(id), 2*interval));
setTimeout(()=> ac.abort(), 3*interval)

View File

@ -1,10 +1,10 @@
import { O } from "deka-dom-el/observables";
const count= O(0);
import { S } from "deka-dom-el/signals";
const count= S(0);
import { el } from "deka-dom-el";
document.body.append(
el("p", O(()=> "Currently: "+count())),
el("p", { classList: { red: O(()=> count()%2) }, dataset: { count }, textContent: "Attributes example" })
el("p", S(()=> "Currently: "+count())),
el("p", { classList: { red: S(()=> count()%2) }, dataset: { count }, textContent: "Attributes example" })
);
document.head.append(
el("style", ".red { color: red; }")

View File

@ -1,26 +1,26 @@
import { O } from "deka-dom-el/observables";
const count= O(0, {
import { S } from "deka-dom-el/signals";
const count= S(0, {
add(){ this.value= this.value + Math.round(Math.random()*10); }
});
const numbers= O([ count() ], {
const numbers= S([ count() ], {
push(next){ this.value.push(next); }
});
import { el } from "deka-dom-el";
document.body.append(
O.el(count, count=> count%2
S.el(count, count=> count%2
? el("p", "Last number is odd.")
: el()
),
el("p", "Lucky numbers:"),
el("ul").append(
O.el(numbers, numbers=> numbers.toReversed()
S.el(numbers, numbers=> numbers.toReversed()
.map(n=> el("li", n)))
)
);
const interval= 5*1000;
setTimeout(clearInterval, 10*interval, setInterval(function(){
O.action(count, "add");
O.action(numbers, "push", count());
S.action(count, "add");
S.action(numbers, "push", count());
}, interval));

View File

@ -0,0 +1,6 @@
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js
import { S, signal } from "deka-dom-el/signals";
S===signal
/** @type {ddeSignal} */
/** @type {ddeAction} */
/** @type {ddeActions} */

View File

@ -0,0 +1,12 @@
import { S } from "deka-dom-el/signals";
// α — `signal` represents a reactive value
const signal= S(0);
// β — just reacts on signal changes
S.on(signal, console.log);
// γ — just updates the value
const update= ()=> signal(signal()+1);
update();
const interval= 5*1000;
setTimeout(clearInterval, 10*interval,
setInterval(update, interval));

View File

@ -13,7 +13,7 @@ export function mnemonic(){
el("code", "observedAttributes(<custom-element>)"), " — returns record of observed attributes (keys uses camelCase)",
),
el("li").append(
el("code", "O.observedAttributes(<custom-element>)"), " — returns record of observed attributes (keys uses camelCase and values are observables)",
el("code", "S.observedAttributes(<custom-element>)"), " — returns record of observed attributes (keys uses camelCase and values are signals)",
),
el("li").append(
el("code", "lifecyclesToEvents(<class-declaration>)"), " — convert lifecycle methods to events, can be also used as decorator",

View File

@ -1,28 +0,0 @@
import { el } from "deka-dom-el";
import { mnemonicUl } from "../mnemonicUl.html.js";
export function mnemonic(){
return mnemonicUl().append(
el("li").append(
el("code", "O(<value>)"), " — observable: reactive value",
),
el("li").append(
el("code", "O(()=> <computation>)"), " — read-only observable: reactive value dependent on calculation using other observables",
),
el("li").append(
el("code", "O.on(<observable>, <listener>[, <options>])"), " — listen to the observable value changes",
),
el("li").append(
el("code", "O.clear(...<observables>)"), " — off and clear observables",
),
el("li").append(
el("code", "O(<value>, <actions>)"), " — observable: pattern to create complex reactive objects/arrays",
),
el("li").append(
el("code", "O.action(<observable>, <action-name>, ...<action-arguments>)"), " — invoke an action for given observable"
),
el("li").append(
el("code", "O.el(<observable>, <function-returning-dom>)"), " — render partial dom structure (template) based on the current observable value",
)
);
}

View File

@ -0,0 +1,28 @@
import { el } from "deka-dom-el";
import { mnemonicUl } from "../mnemonicUl.html.js";
export function mnemonic(){
return mnemonicUl().append(
el("li").append(
el("code", "S(<value>)"), " — signal: reactive value",
),
el("li").append(
el("code", "S(()=> <computation>)"), " — read-only signal: reactive value dependent on calculation using other signals",
),
el("li").append(
el("code", "S.on(<signal>, <listener>[, <options>])"), " — listen to the signal value changes",
),
el("li").append(
el("code", "S.clear(...<signals>)"), " — off and clear signals",
),
el("li").append(
el("code", "S(<value>, <actions>)"), " — signal: pattern to create complex reactive objects/arrays",
),
el("li").append(
el("code", "S.action(<signal>, <action-name>, ...<action-arguments>)"), " — invoke an action for given signal"
),
el("li").append(
el("code", "S.el(<signal>, <function-returning-dom>)"), " — render partial dom structure (template) based on the current signal value",
)
);
}

View File

@ -1,6 +1,11 @@
import { simplePage } from "./layout/simplePage.html.js";
export const info= {
href: "./",
title: "Introduction",
description: "Introducing a library.",
};
import { el } from "deka-dom-el";
import { simplePage } from "./layout/simplePage.html.js";
import { example } from "./components/example.html.js";
/** @param {import("./types.d.ts").PageAttrs} attrs */
export function page({ pkg, info }){
@ -23,10 +28,10 @@ export function page({ pkg, info }){
el("p").append(
"Next step is providing interactivity not only for our UI templates.",
" ",
"We introduce observables (", el("code", "O"), ") and how them incorporate to UI templates.",
"We introduce signals (", el("code", "O"), ") and how them incorporate to UI templates.",
),
el("p").append(
"Now we will clarify how the observables are incorporated into our templates with regard ",
"Now we will clarify how the signals are incorporated into our templates with regard ",
"to application performance. This is not the only reason the library uses ",
el("code", "scope"), "s. We will look at how they work in components represented ",
"in JavaScript by functions."

View File

@ -1,6 +1,10 @@
import { simplePage } from "./layout/simplePage.html.js";
export const info= {
title: "Elements",
description: "Basic concepts of elements modifications and creations.",
};
import { el } from "deka-dom-el";
import { simplePage } from "./layout/simplePage.html.js";
import { example } from "./components/example.html.js";
import { h3 } from "./components/pageUtils.html.js";
import { mnemonic } from "./components/mnemonic/elements-init.js";
@ -64,7 +68,7 @@ export function page({ pkg, info }){
"This is handy to concat conditional classes."
),
el("li").append(
"Use ", el("code", "classList"), " to toggle specific classes. This will be handy later when the reactivity via observables is beeing introduced.",
"Use ", el("code", "classList"), " to toggle specific classes. This will be handy later when the reactivity via signals is beeing introduced.",
),
el("li").append(
"The ", el("code", "assign"), " also accepts the ", el("code", "undefined"), " as a value for any property to remove it from the element declaratively. ",

View File

@ -1,6 +1,10 @@
import { simplePage } from "./layout/simplePage.html.js";
export const info= {
title: "Events and Addons",
description: "Using not only events in UI declaratively.",
};
import { el } from "deka-dom-el";
import { simplePage } from "./layout/simplePage.html.js";
import { example } from "./components/example.html.js";
import { h3 } from "./components/pageUtils.html.js";
import { mnemonic } from "./components/mnemonic/events-init.js";

View File

@ -1,9 +1,13 @@
import { simplePage } from "./layout/simplePage.html.js";
export const info= {
title: "Signals and reactivity",
description: "Handling reactivity in UI via signals.",
};
import { el } from "deka-dom-el";
import { simplePage } from "./layout/simplePage.html.js";
import { example } from "./components/example.html.js";
import { h3 } from "./components/pageUtils.html.js";
import { mnemonic } from "./components/mnemonic/observables-init.js";
import { mnemonic } from "./components/mnemonic/signals-init.js";
import { code } from "./components/code.html.js";
/** @param {string} url */
const fileURL= url=> new URL(url, import.meta.url);
@ -12,24 +16,24 @@ const fileURL= url=> new URL(url, import.meta.url);
export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("h2", "Using observables to manage reactivity"),
el("h2", "Using signals to manage reactivity"),
el("p").append(
"How a program responds to variable data or user",
" interactions is one of the fundamental problems of programming.",
" If we desire to solve the issue in a declarative manner,",
" observables may be a viable approach.",
" signals may be a viable approach.",
),
el(code, { src: fileURL("./components/examples/observables/intro.js"), page_id }),
el(code, { src: fileURL("./components/examples/signals/intro.js"), page_id }),
el(h3, "Introducing observables"),
el(h3, "Introducing signals"),
el("p").append(
"Using observables, we split program logic into the three parts.",
"Using signals, we split program logic into the three parts.",
" Firstly (α), we create a variable (constant) representing reactive",
" value. Somewhere later, we can register (β) a logic reacting",
" to the observable value changes. Similarly, in a remaining part (γ), we",
" can update the observable value."
" to the signal value changes. Similarly, in a remaining part (γ), we",
" can update the signal value."
),
el(example, { src: fileURL("./components/examples/observables/observables.js"), page_id }),
el(example, { src: fileURL("./components/examples/signals/signals.js"), page_id }),
el("p").append(
"All this is just an example of ",
el("a", { textContent: "Event-driven programming", href: "https://en.wikipedia.org/wiki/Event-driven_programming", title: "Wikipedia: Event-driven programming" }),
@ -40,35 +44,35 @@ export function page({ pkg, info }){
" to the same reactive entity."
),
el("p").append(
"Observables are implemented in the library as functions. To see current value",
" of observable, just call it without any arguments ", el("code", "console.log(observable())"), ".",
" To update the observable value, pass any argument ", el("code", "observable('a new value')"), ".",
" For listenning the observable value changes, use ", el("code", "O.on(observable, console.log)"), "."
"Signals are implemented in the library as functions. To see current value",
" of signal, just call it without any arguments ", el("code", "console.log(signal())"), ".",
" To update the signal value, pass any argument ", el("code", "signal('a new value')"), ".",
" For listenning the signal value changes, use ", el("code", "S.on(signal, console.log)"), "."
),
el("p").append(
"Similarly to the ", el("code", "on"), " function to register DOM events listener.",
" You can use ", el("code", "AbortController"), "/", el("code", "AbortSignal"), " to",
" ", el("em", "off"), "/stop listenning. In example, you also found the way for representing",
" “live” piece of code computation pattern (derived observable):"
" “live” piece of code computation pattern (derived signal):"
),
el(example, { src: fileURL("./components/examples/observables/computations-abort.js"), page_id }),
el(example, { src: fileURL("./components/examples/signals/computations-abort.js"), page_id }),
el(h3, "Observables and actions"),
el(h3, "Signals and actions"),
el("p").append(
el("code", "O(/* primitive */)"), " allows you to declare simple reactive variables, typically",
el("code", "S(/* primitive */)"), " allows you to declare simple reactive variables, typically",
" around ", el("em", "immutable"), " ", el("a", { textContent: "primitive types", title: "Primitive | MDN", href: "https://developer.mozilla.org/en-US/docs/Glossary/Primitive" }), ".",
" ",
"However, it may also be necessary to use reactive arrays, objects, or other complex reactive structures."
),
el(example, { src: fileURL("./components/examples/observables/actions-demo.js"), page_id }),
el(example, { src: fileURL("./components/examples/signals/actions-demo.js"), page_id }),
el("p", "…but typical user-case is object/array (maps, sets and other mutable objects):"),
el(example, { src: fileURL("./components/examples/observables/actions-todos.js"), page_id }),
el(example, { src: fileURL("./components/examples/signals/actions-todos.js"), page_id }),
el("p").append(
"In some way, you can compare it with ", el("a", { textContent: "useReducer", href: "https://react.dev/reference/react/useReducer", title: "useReducer hook | React docs" }),
" hook from React. So, the ", el("code", "O(<data>, <actions>)"), " pattern creates",
" hook from React. So, the ", el("code", "S(<data>, <actions>)"), " pattern creates",
" a store “machine”. We can then invoke (dispatch) registered action by calling",
" ", el("code", "O.action(<observable>, <name>, ...<args>)"), " after the action call",
" the observable calls all its listeners. This can be stopped by calling ", el("code", "this.stopPropagation()"),
" ", el("code", "S.action(<signal>, <name>, ...<args>)"), " after the action call",
" the signal calls all its listeners. This can be stopped by calling ", el("code", "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 (", el("code", "this.value"), ")."
),
@ -79,24 +83,24 @@ export function page({ pkg, info }){
el("li", "to change some attribute(s) of existing element(s)"),
el("li", "to generate elements itself dynamically this covers conditions and loops")
),
el(example, { src: fileURL("./components/examples/observables/dom-attrs.js"), page_id }),
el(example, { src: fileURL("./components/examples/signals/dom-attrs.js"), page_id }),
el("p").append(
"To derived attribute based on value of observable variable just use the observable as",
" a value of the attribute (", el("code", "assign(element, { attribute: O('value') })"), ").",
"To derived attribute based on value of signal variable just use the signal as",
" a value of the attribute (", el("code", "assign(element, { attribute: S('value') })"), ").",
" ", el("code", "assign"), "/", el("code", "el"), " provides ways to glue reactive attributes/classes",
" more granularly into the DOM. Just use dedicated build-in attributes ", el("code", "dataset"), ", ",
el("code", "ariaset"), " and ", el("code", "classList"), "."
),
el("p").append(
"For computation, you can use the “derived observable” (see above) like ", el("code", "assign(element, { textContent: O(()=> 'Hello '+WorldObservable()) })"), ".",
"For computation, you can use the “derived signal” (see above) like ", el("code", "assign(element, { textContent: S(()=> 'Hello '+WorldSignal()) })"), ".",
" ",
"This is read-only observable its value is computed based on given function and updated when any observable used in the function changes."
"This is read-only signal its value is computed based on given function and updated when any signal used in the function changes."
),
el("p").append(
"To represent part of the template filled dynamically based on the observable value use ", el("code", "O.el(observable, DOMgenerator)"), ".",
"To represent part of the template filled dynamically based on the signal value use ", el("code", "S.el(signal, DOMgenerator)"), ".",
" This was already used in the todo example above or see:"
),
el(example, { src: fileURL("./components/examples/observables/dom-el.js"), page_id }),
el(example, { src: fileURL("./components/examples/signals/dom-el.js"), page_id }),
el(mnemonic)
);

View File

@ -1,6 +1,10 @@
import { simplePage } from "./layout/simplePage.html.js";
export const info= {
title: "Scopes and components",
description: "Organizing UI into components",
};
import { el } from "deka-dom-el";
import { simplePage } from "./layout/simplePage.html.js";
import { example } from "./components/example.html.js";
import { h3 } from "./components/pageUtils.html.js";
import { mnemonic } from "./components/mnemonic/scopes-init.js";
@ -43,14 +47,14 @@ export function page({ pkg, info }){
),
el(code, { src: fileURL("./components/examples/scopes/good-practise.js"), page_id }),
el(h3, "Scopes, observables and cleaning magic"),
el(h3, "Scopes, signals and cleaning magic"),
el("p").append(
"The ", el("code", "host"), " is internally used to register the cleaning procedure,",
" when the component (", el("code", "host"), " element) is removed from the DOM."
),
el(example, { src: fileURL("./components/examples/scopes/cleaning.js"), page_id }),
el("p").append(
"The text content of the paragraph is changing when the value of the observable ", el("code", "textContent"),
"The text content of the paragraph is changing when the value of the signal ", el("code", "textContent"),
" is changed. Internally, there is association between ", el("code", "textContent"), " and the paragraph",
" similar to using ", el("code", "S.on(textContent, /* update the paragraph */)"), "."
),
@ -59,13 +63,13 @@ export function page({ pkg, info }){
" assign internally ", el("code", "on.disconnected(/* remove the listener */)(host())"), " to the host element."
),
el("p", { className: "notice" }).append(
"The library DOM API and observables works ideally when used declaratively.",
" It means, you split your app logic into three parts as it was itroduced in ", el("a", { textContent: "Observables", href: "http://localhost:40911/docs/p04-observables#h-introducing-observables" }), "."
"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 ", el("a", { textContent: "Signals", href: "http://localhost:40911/docs/p04-signals#h-introducing-signals" }), "."
),
el(code, { src: fileURL("./components/examples/scopes/declarative.js"), page_id }),
el("p").append(
"Strictly speaking, the imperative way of using the library is not prohibited.",
" Just be careful (rather avoid) mixing declarative approach (using observables)",
" Just be careful (rather avoid) mixing declarative approach (using signals)",
" and imperative manipulation of elements.",
),
el(code, { src: fileURL("./components/examples/scopes/imperative.js"), page_id }),

View File

@ -1,6 +1,10 @@
import { simplePage } from "./layout/simplePage.html.js";
export const info= {
title: "Custom elements",
description: "Using custom elements in combinantion with DDE",
};
import { el } from "deka-dom-el";
import { simplePage } from "./layout/simplePage.html.js";
import { example } from "./components/example.html.js";
import { h3 } from "./components/pageUtils.html.js";
import { mnemonic } from "./components/mnemonic/customElement-init.js";

View File

@ -2,14 +2,11 @@ export const path_target= {
root: "docs/",
css: "docs/"
};
export const pages= [
{ id: "index", href: "./", title: "Introduction", description: "Introducing a library." },
{ id: "p02-elements", href: "p02-elements", title: "Elements", description: "Basic concepts of elements modifications and creations." },
{ id: "p03-events", href: "p03-events", title: "Events and Addons", description: "Using not only events in UI declaratively." },
{ id: "p04-observables", href: "p04-observables", title: "Observables and reactivity", description: "Handling reactivity in UI via observables." },
{ id: "p05-scopes", href: "p05-scopes", title: "Scopes and components", description: "Organizing UI into components" },
{ id: "p06-customElement", href: "p06-customElement", title: "Custom elements", description: "Using custom elements in combinantion with DDE" },
];
/**
* This variable will be filled with the list of pages during the build process (see `bs/docs.js`).
* @type {import("./types.d.ts").Info[]}
* */
export let pages= [];
/**
* @typedef registerClientFile
* @type {function}

View File

@ -1,4 +1,4 @@
import { style, el, O, isObservable } from '../exports.js';
import { style, el, S, isSignal } from '../exports.js';
const className= style.host(thirdParty).css`
:host {
color: green;
@ -10,22 +10,22 @@ const store_adapter= {
write(data){ console.log(data); history.replaceState("", "", "?"+(new URLSearchParams(data)).toString()); }
};
export function thirdParty(){
const store= O({
value: O("initial")
const store= S({
value: S("initial")
}, {
set(key, value){
const p= this.value[key] || O();
const p= this.value[key] || S();
p(value);
this.value[key]= p;
}
});
// Array.from((new URL(location)).searchParams.entries())
// .forEach(([ key, value ])=> O.action(store, "set", key, value));
// O.on(store, data=> history.replaceState("", "", "?"+(new URLSearchParams(JSON.parse(JSON.stringify(data)))).toString()));
// .forEach(([ key, value ])=> S.action(store, "set", key, value));
// S.on(store, data=> history.replaceState("", "", "?"+(new URLSearchParams(JSON.parse(JSON.stringify(data)))).toString()));
useStore(store_adapter, {
onread(data){
Array.from(data.entries())
.forEach(([ key, value ])=> O.action(store, "set", key, value));
.forEach(([ key, value ])=> S.action(store, "set", key, value));
return store;
}
})();
@ -33,18 +33,18 @@ export function thirdParty(){
className,
value: store().value(),
type: "text",
onchange: ev=> O.action(store, "set", "value", ev.target.value)
onchange: ev=> S.action(store, "set", "value", ev.target.value)
});
}
function useStore(adapter_in, { onread, onbeforewrite }= {}){
const adapter= typeof adapter_in === "function" ? { read: adapter_in } : adapter_in;
if(!onread) onread= O;
if(!onread) onread= S;
if(!onbeforewrite) onbeforewrite= data=> JSON.parse(JSON.stringify(data));
return function useStoreInner(data_read){
const observable= onread(adapter.read(data_read)); //TODO OK as synchronous
if(adapter.write && isObservable(observable))
O.on(observable, data=> adapter.write(onbeforewrite(data)));
return observable;
const signal= onread(adapter.read(data_read)); //TODO OK as synchronous
if(adapter.write && isSignal(signal))
S.on(signal, data=> adapter.write(onbeforewrite(data)));
return signal;
};
}

View File

@ -1,4 +1,4 @@
import { style, el, elNS, on, O, scope } from '../exports.js';
import { style, el, elNS, on, S, scope } from '../exports.js';
const className= style.host(fullNameComponent).css`
:host form{
display: flex;
@ -7,8 +7,8 @@ const className= style.host(fullNameComponent).css`
`;
export function fullNameComponent(){
const labels= [ "Name", "Surname" ];
const name= labels.map(_=> O(""));
const full_name= O(()=>
const name= labels.map(_=> S(""));
const full_name= S(()=>
name.map(l=> l()).filter(Boolean).join(" ") || "-");
scope.host(
on.connected(()=> console.log(fullNameComponent)),

View File

@ -1,4 +1,4 @@
import { style, el, dispatchEvent, on, O, scope } from '../exports.js';
import { style, el, dispatchEvent, on, S, scope } from '../exports.js';
const className= style.host(todosComponent).css`
:host{
display: flex;
@ -17,27 +17,27 @@ const className= style.host(todosComponent).css`
/** @param {{ todos: string[] }} */
export function todosComponent({ todos= [ "Task A" ] }= {}){
let key= 0;
const todosO= O(new Map(), {
add(v){ this.value.set(key++, O(v)); },
remove(key){ O.clear(this.value.get(key)); this.value.delete(key); }
const todosO= S(new Map(), {
add(v){ this.value.set(key++, S(v)); },
remove(key){ S.clear(this.value.get(key)); this.value.delete(key); }
});
todos.forEach(text=> O.action(todosO, "add", text));
todos.forEach(text=> S.action(todosO, "add", text));
const name= "todoName";
const onsubmitAdd= on("submit", event=> {
const el= event.target.elements[name];
event.preventDefault();
O.action(todosO, "add", el.value);
S.action(todosO, "add", el.value);
el.value= "";
});
const onremove= on("remove", event=>
O.action(todosO, "remove", event.detail));
S.action(todosO, "remove", event.detail));
return el("div", { className }).append(
el("div").append(
el("h2", "Todos:"),
el("h3", "List of todos:"),
O.el(todosO, (ts, memo)=> !ts.size
S.el(todosO, (ts, memo)=> !ts.size
? el("p", "No todos yet")
: el("ul").append(
...Array.from(ts).map(([ value, textContent ])=>
@ -55,7 +55,7 @@ export function todosComponent({ todos= [ "Task A" ] }= {}){
),
el("div").append(
el("h3", "Output (JSON):"),
el("output", O(()=> JSON.stringify(Array.from(todosO()), null, "\t")))
el("output", S(()=> JSON.stringify(Array.from(todosO()), null, "\t")))
)
)
}
@ -70,13 +70,13 @@ function todoComponent({ textContent, value }){
event.stopPropagation();
dispatchEvent("remove")(host(), value);
});
const is_editable= O(false);
const is_editable= S(false);
const onedited= on("change", ev=> {
textContent(ev.target.value);
is_editable(false);
});
return el("li").append(
O.el(is_editable, is=> is
S.el(is_editable, is=> is
? el("input", { value: textContent(), type: "text" }, onedited)
: el("span", { textContent, onclick: is_editable.bind(null, true) })
),

View File

@ -1,5 +1,5 @@
import { el, on, customElementRender, customElementWithDDE, scope, simulateSlots } from "../../index.js";
import { O } from "../../observables.js";
import { S } from "../../signals.js";
/**
* Compatible with `npx -y web-component-analyzer examples/components/webComponent.js`
@ -16,7 +16,7 @@ export class CustomHTMLTestElement extends HTMLElement{
}
attributes(element){
const observed= O.observedAttributes(element);
const observed= S.observedAttributes(element);
return Object.assign({ test: element.test }, observed);
}
render({ name, preName, test }){

View File

@ -1,10 +1,10 @@
import * as dde_dom from "../index.js";
export * from "../index.js";
import * as dde_s from "../observables.js";
export * from "../observables.js";
import * as dde_s from "../signals.js";
export * from "../signals.js";
Object.assign(globalThis, dde_dom, dde_s);
//import * as dde_dom from "../dist/esm-with-observables.js";
//export * from "../dist/esm-with-observables.js";
//import * as dde_dom from "../dist/esm-with-signals.js";
//export * from "../dist/esm-with-signals.js";
//Object.assign(globalThis, dde_dom);
export const style= createStyle();

View File

@ -1,2 +0,0 @@
export * from "./index";
export * from "./observables";

View File

@ -1,2 +0,0 @@
export * from "./index.js";
export * from "./observables.js";

2
index-with-signals.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
export * from "./index";
export * from "./signals";

2
index-with-signals.js Normal file
View File

@ -0,0 +1,2 @@
export * from "./index.js";
export * from "./signals.js";

18
index.d.ts vendored
View File

@ -1,4 +1,4 @@
import { Observable } from "./observables";
import { Signal } from "./signals";
type CustomElementTagNameMap= { '#text': Text, '#comment': Comment }
type SupportedElement=
@ -15,20 +15,20 @@ type AttrsModified= {
/**
* Use string like in HTML (internally uses `*.setAttribute("style", *)`), or object representation (like DOM API).
*/
style: string | Partial<CSSStyleDeclaration> | Observable<string, any> | Partial<{ [K in keyof CSSStyleDeclaration]: Observable<CSSStyleDeclaration[K], any> }>
style: string | Partial<CSSStyleDeclaration> | Signal<string, any> | Partial<{ [K in keyof CSSStyleDeclaration]: Signal<CSSStyleDeclaration[K], any> }>
/**
* Provide option to add/remove/toggle CSS clasess (index of object) using 1/0/-1. In fact `el.classList.toggle(class_name)` for `-1` and `el.classList.toggle(class_name, Boolean(...))` for others.
*/
classList: Record<string,-1|0|1|boolean|Observable<-1|0|1|boolean, any>>,
classList: Record<string,-1|0|1|boolean|Signal<-1|0|1|boolean, any>>,
/**
* By default simiral to `className`, but also supports `string[]`
* */
className: string | (string|boolean|undefined|Observable<string|boolean|undefined, any>)[];
className: string | (string|boolean|undefined|Signal<string|boolean|undefined, any>)[];
/**
* Sets `aria-*` simiraly to `dataset`
* */
ariaset: Record<string,string|Observable<string, any>>,
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|Observable<string, any>> & Record<`.${string}`, any>
ariaset: Record<string,string|Signal<string, any>>,
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|Signal<string, any>> & Record<`.${string}`, any>
type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>;
/**
* Just element attributtes
@ -38,13 +38,13 @@ type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModifi
* There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
* @private
*/
type ElementAttributes<T extends SupportedElement>= Partial<_fromElsInterfaces<T> & { [K in keyof _fromElsInterfaces<T>]: Observable<_fromElsInterfaces<T>[K], any> } & AttrsModified> & Record<string, any>;
type ElementAttributes<T extends SupportedElement>= Partial<_fromElsInterfaces<T> & { [K in keyof _fromElsInterfaces<T>]: Signal<_fromElsInterfaces<T>[K], any> } & AttrsModified> & Record<string, any>;
export function classListDeclarative<El extends SupportedElement>(element: El, classList: AttrsModified["classList"]): El
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(element: El, attr: ATT, value: ElementAttributes<El>[ATT]): ElementAttributes<El>[ATT]
type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap;
type textContent= string | ( (set?: string)=> string ); // TODO: for some reason `Observable<string, any>` leads to `attrs?: any`
type textContent= string | ( (set?: string)=> string ); // TODO: for some reason `Signal<string, any>` leads to `attrs?: any`
export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap & string,
EL extends (TAG extends keyof ExtendedHTMLElementTagNameMap ? ExtendedHTMLElementTagNameMap[TAG] : HTMLElement)
@ -83,7 +83,7 @@ export function elNS(
EL extends ( TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement ),
>(
tag_name: TAG,
attrs?: string | textContent | Partial<{ [key in keyof EL]: EL[key] | Observable<EL[key], any> | string | number | boolean }>,
attrs?: string | textContent | Partial<{ [key in keyof EL]: EL[key] | Signal<EL[key], any> | string | number | boolean }>,
...addons: ddeElementAddon<EL>[]
)=> ddeMathMLElement
export function elNS(

66
observables.d.ts vendored
View File

@ -1,66 +0,0 @@
export type Observable<V, A>= (set?: V)=> V & A;
type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typeof observable._ | void;
//type SymbolObservable= Symbol;
type SymbolOnclear= symbol;
type Actions<V>= Record<string | SymbolOnclear, Action<V>>;
type OnListenerOptions= Pick<AddEventListenerOptions, "signal"> & { first_time?: boolean };
interface observable{
_: Symbol
/**
* Simple example:
* ```js
* const hello= S("Hello Observable");
* ```
* simple todo observable:
* ```js
* const todos= S([], {
* add(v){ this.value.push(S(v)); },
* remove(i){ this.value.splice(i, 1); },
* [S.symbols.onclear](){ S.clear(...this.value); },
* });
* ```
* computed observable:
* ```js
* const name= S("Jan");
* const surname= S("Andrle");
* const fullname= S(()=> name()+" "+surname());
* ```
* @param value Initial observable value. Or function computing value from other observables.
* @param actions Use to define actions on the observable. Such as add item to the array.
* There is also a reserved function `S.symbol.onclear` which is called when the observable is cleared
* by `S.clear`.
* */
<V, A extends Actions<V>>(value: V, actions?: A): Observable<V, A>;
/**
* Computations observable. This creates a observable which is computed from other observables.
* */
<V>(computation: ()=> V): Observable<V, {}>
action<S extends Observable<any, Actions<any>>, A extends (S extends Observable<any, infer A> ? A : never), N extends keyof A>(
observable: S,
name: N,
...params: A[N] extends (...args: infer P)=> any ? P : never
): void;
clear(...observables: Observable<any, any>[]): void;
on<T>(observable: Observable<T, any>, onchange: (a: T)=> void, options?: OnListenerOptions): void;
symbols: {
//observable: SymbolObservable;
onclear: SymbolOnclear;
}
/**
* Reactive element, which is rendered based on the given observable.
* ```js
* S.el(observable, value=> value ? el("b", "True") : el("i", "False"));
* S.el(listS, list=> list.map(li=> el("li", li)));
* ```
* */
el<S extends any>(observable: Observable<S, any>, el: (v: S)=> Element | Element[] | DocumentFragment): DocumentFragment;
observedAttributes(custom_element: HTMLElement): Record<string, Observable<any, any>>;
}
export const observable: observable;
export const O: observable;
declare global {
type ddeObservable<T, A= {}>= Observable<T, A>;
type ddeAction<V>= Action<V>
type ddeActions<V>= Actions<V>
}

View File

@ -1,4 +0,0 @@
export { observable, O, isObservable } from "./src/observables-lib.js";
import { observables_config } from "./src/observables-lib.js";
import { registerReactivity } from "./src/observables-common.js";
registerReactivity(observables_config);

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "deka-dom-el",
"version": "0.7.7",
"version": "0.8.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "deka-dom-el",
"version": "0.7.7",
"version": "0.8.0",
"license": "MIT",
"devDependencies": {
"@size-limit/preset-small-lib": "^11.0.1",

View File

@ -1,6 +1,6 @@
{
"name": "deka-dom-el",
"version": "0.7.8",
"version": "0.8.0",
"description": "A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks.",
"author": "Jan Andrle <andrle.jan@centrum.cz>",
"license": "MIT",
@ -20,17 +20,17 @@
"import": "./index.js",
"types": "./index.d.ts"
},
"./observables": {
"import": "./observables.js",
"types": "./observables.d.ts"
"./signals": {
"import": "./signals.js",
"types": "./signals.d.ts"
},
"./jsdom": {
"import": "./jsdom.js",
"types": "./jsdom.d.ts"
},
"./src/observables-lib": {
"import": "./src/observables-lib.js",
"types": "./src/observables-lib.d.ts"
"./src/signals-lib": {
"import": "./src/signals-lib.js",
"types": "./src/signals-lib.d.ts"
}
},
"files": [
@ -65,20 +65,20 @@
},
{
"path": "./observables.js",
"path": "./signals.js",
"limit": "12 kB",
"gzip": false,
"brotli": false
},
{
"path": "./index-with-observables.js",
"path": "./index-with-signals.js",
"limit": "15 kB",
"gzip": false,
"brotli": false
},
{
"path": "./index-with-observables.js",
"path": "./index-with-signals.js",
"limit": "5.25 kB"
}
],

66
signals.d.ts vendored Normal file
View File

@ -0,0 +1,66 @@
export type Signal<V, A>= (set?: V)=> V & A;
type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typeof signal._ | void;
//type SymbolSignal= Symbol;
type SymbolOnclear= symbol;
type Actions<V>= Record<string | SymbolOnclear, Action<V>>;
type OnListenerOptions= Pick<AddEventListenerOptions, "signal"> & { first_time?: boolean };
interface signal{
_: Symbol
/**
* Simple example:
* ```js
* const hello= S("Hello Signal");
* ```
* simple todo signal:
* ```js
* const todos= S([], {
* add(v){ this.value.push(S(v)); },
* remove(i){ this.value.splice(i, 1); },
* [S.symbols.onclear](){ S.clear(...this.value); },
* });
* ```
* computed signal:
* ```js
* const name= S("Jan");
* const surname= S("Andrle");
* const fullname= S(()=> name()+" "+surname());
* ```
* @param value Initial signal value. Or function computing value from other signals.
* @param actions Use to define actions on the signal. Such as add item to the array.
* There is also a reserved function `S.symbol.onclear` which is called when the signal is cleared
* by `S.clear`.
* */
<V, A extends Actions<V>>(value: V, actions?: A): Signal<V, A>;
/**
* Computations signal. This creates a signal which is computed from other signals.
* */
<V>(computation: ()=> V): Signal<V, {}>
action<S extends Signal<any, Actions<any>>, A extends (S extends Signal<any, infer A> ? A : never), N extends keyof A>(
signal: S,
name: N,
...params: A[N] extends (...args: infer P)=> any ? P : never
): void;
clear(...signals: Signal<any, any>[]): void;
on<T>(signal: Signal<T, any>, onchange: (a: T)=> void, options?: OnListenerOptions): void;
symbols: {
//signal: SymbolSignal;
onclear: SymbolOnclear;
}
/**
* Reactive element, which is rendered based on the given signal.
* ```js
* S.el(signal, value=> value ? el("b", "True") : el("i", "False"));
* S.el(listS, list=> list.map(li=> el("li", li)));
* ```
* */
el<S extends any>(signal: Signal<S, any>, el: (v: S)=> Element | Element[] | DocumentFragment): DocumentFragment;
observedAttributes(custom_element: HTMLElement): Record<string, Signal<any, any>>;
}
export const signal: signal;
export const S: signal;
declare global {
type ddeSignal<T, A= {}>= Signal<T, A>;
type ddeAction<V>= Action<V>
type ddeActions<V>= Actions<V>
}

4
signals.js Normal file
View File

@ -0,0 +1,4 @@
export { signal, S, isSignal } from "./src/signals-lib.js";
import { signals_config } from "./src/signals-lib.js";
import { registerReactivity } from "./src/signals-common.js";
registerReactivity(signals_config);

View File

@ -1,4 +1,4 @@
import { observables } from "./observables-common.js";
import { signals } from "./signals-common.js";
import { enviroment as env } from './dom-common.js';
/** @type {{ scope: object, prevent: boolean, host: function }[]} */
@ -31,11 +31,11 @@ export function chainableAppend(el){ if(el.append===append) return el; el.append
let namespace;
export function createElement(tag, attributes, ...addons){
/* jshint maxcomplexity: 15 */
const s= observables(this);
const s= signals(this);
let scoped= 0;
let el, el_host;
//TODO Array.isArray(tag) ⇒ set key (cache els)
if(Object(attributes)!==attributes || s.isObservable(attributes))
if(Object(attributes)!==attributes || s.isSignal(attributes))
attributes= { textContent: attributes };
switch(true){
case typeof tag==="function": {
@ -177,11 +177,11 @@ function assignContext(element, _this){
if(assign_context.has(element)) return assign_context.get(element);
const is_svg= element instanceof env.S;
const setRemoveAttr= (is_svg ? setRemoveNS : setRemove).bind(null, element, "Attribute");
const s= observables(_this);
const s= signals(_this);
return { setRemoveAttr, s };
}
export function classListDeclarative(element, toggle){
const s= observables(this);
const s= signals(this);
forEachEntries(s, toggle,
(class_name, val)=>
element.classList.toggle(class_name, val===-1 ? undefined : Boolean(val)));

View File

@ -1,4 +1,4 @@
export { registerReactivity } from './observables-common.js';
export { registerReactivity } from './signals-common.js';
import { enviroment as env, keyLTE, evc, evd, eva } from './dom-common.js';
export function dispatchEvent(name, options, host){

View File

@ -1,13 +0,0 @@
export const observables_global= {
isObservable(attributes){ return false; },
processReactiveAttribute(obj, key, attr, set){ return attr; },
};
export function registerReactivity(def, global= true){
if(global) return Object.assign(observables_global, def);
Object.setPrototypeOf(def, observables_global);
return def;
}
/** @param {unknown} _this @returns {typeof observables_global} */
export function observables(_this){
return observables_global.isPrototypeOf(_this) && _this!==observables_global ? _this : observables_global;
}

View File

@ -1,4 +0,0 @@
import type { Action, Actions, observable as o, Observable, SymbolOnclear } from "../observables.d.ts";
export { Action, Actions, Observable, SymbolOnclear };
export const O: o;
export const observable: o;

View File

@ -1,26 +1,26 @@
export const mark= "__dde_observable";
export const mark= "__dde_signal";
import { hasOwn } from "./helpers.js";
export function isObservable(candidate){
export function isSignal(candidate){
try{ return hasOwn(candidate, mark); }
catch(e){ return false; }
}
/** @type {function[]} */
const stack_watch= [];
/**
* ### `WeakMap<function, Set<ddeObservable<any, any>>>`
* The `Set` is in the form of `[ source, ...depended observables (DSs) ]`.
* When the DS is cleaned (`O.clear`) it is removed from DSs,
* ### `WeakMap<function, Set<ddeSignal<any, any>>>`
* The `Set` is in the form of `[ source, ...depended signals (DSs) ]`.
* When the DS is cleaned (`S.clear`) it is removed from DSs,
* if remains only one (`source`) it is cleared too.
* ### `WeakMap<object, function>`
* This is used for revesed deps, the `function` is also key for `deps`.
* @type {WeakMap<function|object,Set<ddeObservable<any, any>>|function>}
* @type {WeakMap<function|object,Set<ddeSignal<any, any>>|function>}
* */
const deps= new WeakMap();
export function observable(value, actions){
export function signal(value, actions){
if(typeof value!=="function")
return create(false, value, actions);
if(isObservable(value)) return value;
if(isSignal(value)) return value;
const out= create(true);
const contextReWatch= function(){
@ -33,9 +33,9 @@ export function observable(value, actions){
if(!deps_old.length) return;
const deps_curr= deps.get(contextReWatch);
for (const dep_observable of deps_old){
if(deps_curr.has(dep_observable)) continue;
removeObservableListener(dep_observable, contextReWatch);
for (const dep_signal of deps_old){
if(deps_curr.has(dep_signal)) continue;
removeSignalListener(dep_signal, contextReWatch);
}
};
deps.set(out[mark], contextReWatch);
@ -43,46 +43,46 @@ export function observable(value, actions){
contextReWatch();
return out;
}
export { observable as O };
observable.action= function(o, name, ...a){
const s= o[mark], { actions }= s;
export { signal as S };
signal.action= function(s, name, ...a){
const M= s[mark], { actions }= M;
if(!actions || !(name in actions))
throw new Error(`'${o}' has no action with name '${name}'!`);
actions[name].apply(s, a);
if(s.skip) return (delete s.skip);
s.listeners.forEach(l=> l(s.value));
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));
};
observable.on= function on(o, listener, options= {}){
signal.on= function on(s, listener, options= {}){
const { signal: as }= options;
if(as && as.aborted) return;
if(Array.isArray(o)) return o.forEach(s=> on(s, listener, options));
addObservableListener(o, listener);
if(as) as.addEventListener("abort", ()=> removeObservableListener(o, listener));
//TODO cleanup when observable removed
if(Array.isArray(s)) return s.forEach(s=> on(s, listener, options));
addSignalListener(s, listener);
if(as) as.addEventListener("abort", ()=> removeSignalListener(s, listener));
//TODO cleanup when signal removed
};
observable.symbols= {
//observable: mark,
onclear: Symbol.for("Observable.onclear")
signal.symbols= {
//signal: mark,
onclear: Symbol.for("Signal.onclear")
};
observable.clear= function(...observables){
for(const o of observables){
const s= o[mark];
if(!s) continue;
delete o.toJSON;
s.onclear.forEach(f=> f.call(s));
clearListDeps(o, s);
delete o[mark];
signal.clear= function(...signals){
for(const s of signals){
const M= s[mark];
if(!M) continue;
delete s.toJSON;
M.onclear.forEach(f=> f.call(M));
clearListDeps(s, M);
delete s[mark];
}
function clearListDeps(o, s){
s.listeners.forEach(l=> {
s.listeners.delete(l);
function clearListDeps(s, o){
o.listeners.forEach(l=> {
o.listeners.delete(l);
if(!deps.has(l)) return;
const ls= deps.get(l);
ls.delete(o);
ls.delete(s);
if(ls.size>1) return;
o.clear(...ls);
s.clear(...ls);
deps.delete(l);
});
}
@ -92,7 +92,7 @@ import { enviroment as env } from "./dom-common.js";
import { el } from "./dom.js";
import { scope } from "./dom.js";
// TODO: third argument for handle `cache_tmp` in re-render
observable.el= function(o, map){
signal.el= function(s, map){
const mark_start= el.mark({ type: "reactive" }, true);
const mark_end= mark_start.end;
const out= env.D.createDocumentFragment();
@ -101,7 +101,7 @@ observable.el= function(o, map){
let cache= {};
const reRenderReactiveElement= v=> {
if(!mark_start.parentNode || !mark_end.parentNode) // === `isConnected` or wasnt yet rendered
return removeObservableListener(o, reRenderReactiveElement);
return removeSignalListener(s, reRenderReactiveElement);
const cache_tmp= cache; // will be reused in the useCache or removed in the while loop on the end
cache= {};
scope.push(current);
@ -128,16 +128,16 @@ observable.el= function(o, map){
if(mark_start.isConnected)
requestCleanUpReactives(current.host());
};
addObservableListener(o, reRenderReactiveElement);
removeObservablesFromElements(o, reRenderReactiveElement, mark_start, map);
reRenderReactiveElement(o());
addSignalListener(s, reRenderReactiveElement);
removeSignalsFromElements(s, reRenderReactiveElement, mark_start, map);
reRenderReactiveElement(s());
return out;
};
function requestCleanUpReactives(host){
if(!host || !host[key_reactive]) return;
(requestIdleCallback || setTimeout)(function(){
host[key_reactive]= host[key_reactive]
.filter(([ o, el ])=> el.isConnected ? true : (removeObservableListener(...o), false));
.filter(([ s, el ])=> el.isConnected ? true : (removeSignalListener(...s), false));
});
}
import { on } from "./events.js";
@ -147,49 +147,49 @@ const observedAttributeActions= {
};
function observedAttribute(store){
return function(instance, name){
const varO= (...args)=> !args.length
? read(varO)
const varS= (...args)=> !args.length
? read(varS)
: instance.setAttribute(name, ...args);
const out= toObservable(varO, instance.getAttribute(name), observedAttributeActions);
const out= toSignal(varS, instance.getAttribute(name), observedAttributeActions);
store[name]= out;
return out;
};
}
const key_attributes= "__dde_attributes";
observable.observedAttributes= function(element){
signal.observedAttributes= function(element){
const store= element[key_attributes]= {};
const attrs= observedAttributes(element, observedAttribute(store));
on.attributeChanged(function attributeChangeToObservable({ detail }){
/*! This maps attributes to observables (`O.observedAttributes`).
on.attributeChanged(function attributeChangeToSignal({ detail }){
/*! This maps attributes to signals (`S.observedAttributes`).
* Investigate `__dde_attributes` key of the element.*/
const [ name, value ]= detail;
const curr= this[key_attributes][name];
if(curr) return observable.action(curr, "_set", value);
if(curr) return signal.action(curr, "_set", value);
})(element);
on.disconnected(function(){
/*! This removes all observables mapped to attributes (`O.observedAttributes`).
/*! This removes all signals mapped to attributes (`S.observedAttributes`).
* Investigate `__dde_attributes` key of the element.*/
observable.clear(...Object.values(this[key_attributes]));
signal.clear(...Object.values(this[key_attributes]));
})(element);
return attrs;
};
import { typeOf } from './helpers.js';
export const observables_config= {
isObservable,
export const signals_config= {
isSignal,
processReactiveAttribute(element, key, attrs, set){
if(!isObservable(attrs)) return attrs;
if(!isSignal(attrs)) return attrs;
const l= attr=> {
if(!element.isConnected)
return removeObservableListener(attrs, l);
return removeSignalListener(attrs, l);
set(key, attr);
};
addObservableListener(attrs, l);
removeObservablesFromElements(attrs, l, element, key);
addSignalListener(attrs, l);
removeSignalsFromElements(attrs, l, element, key);
return attrs();
}
};
function removeObservablesFromElements(o, listener, ...notes){
function removeSignalsFromElements(s, listener, ...notes){
const { current }= scope;
if(current.prevent) return;
current.host(function(element){
@ -197,29 +197,29 @@ function removeObservablesFromElements(o, listener, ...notes){
element[key_reactive]= [];
on.disconnected(()=>
/*!
* Clears all Observables listeners added in the current scope/host (`O.el`, `assign`, ?).
* 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(([ [ o, listener ] ])=>
removeObservableListener(o, listener, o[mark] && o[mark].host && o[mark].host() === element))
element[key_reactive].forEach(([ [ s, listener ] ])=>
removeSignalListener(s, listener, s[mark] && s[mark].host && s[mark].host() === element))
)(element);
}
element[key_reactive].push([ [ o, listener ], ...notes ]);
element[key_reactive].push([ [ s, listener ], ...notes ]);
});
}
function create(is_readonly, value, actions){
const varO= is_readonly
? ()=> read(varO)
: (...value)=> value.length ? write(varO, ...value) : read(varO);
return toObservable(varO, value, actions, is_readonly);
const varS= is_readonly
? ()=> read(varS)
: (...value)=> value.length ? write(varS, ...value) : read(varS);
return toSignal(varS, value, actions, is_readonly);
}
const protoSigal= Object.assign(Object.create(null), {
stopPropagation(){
this.skip= true;
}
});
class ObservableDefined extends Error{
class SignalDefined extends Error{
constructor(){
super();
const [ curr, ...rest ]= this.stack.split("\n");
@ -227,66 +227,66 @@ class ObservableDefined extends Error{
this.stack= rest.find(l=> !l.includes(curr_file));
}
}
function toObservable(o, value, actions, readonly= false){
function toSignal(s, value, actions, readonly= false){
const onclear= [];
if(typeOf(actions)!=="[object Object]")
actions= {};
const { onclear: ocs }= observable.symbols;
const { onclear: ocs }= signal.symbols;
if(actions[ocs]){
onclear.push(actions[ocs]);
delete actions[ocs];
}
const { host }= scope;
Reflect.defineProperty(o, mark, {
Reflect.defineProperty(s, mark, {
value: {
value, actions, onclear, host,
listeners: new Set(),
defined: (new ObservableDefined()).stack,
defined: (new SignalDefined()).stack,
readonly
},
enumerable: false,
writable: false,
configurable: true
});
o.toJSON= ()=> o();
o.valueOf= ()=> o[mark] && o[mark].value;
Object.setPrototypeOf(o[mark], protoSigal);
return o;
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(o){
if(!o[mark]) return;
const { value, listeners }= o[mark];
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(o);
if(deps.has(context)) deps.get(context).add(s);
return value;
}
function write(o, value, force){
if(!o[mark]) return;
const s= o[mark];
if(!force && s.value===value) return;
s.value= value;
s.listeners.forEach(l=> l(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 addObservableListener(o, listener){
if(!o[mark]) return;
return o[mark].listeners.add(listener);
function addSignalListener(s, listener){
if(!s[mark]) return;
return s[mark].listeners.add(listener);
}
function removeObservableListener(o, listener, clear_when_empty){
const s= o[mark];
if(!s) return;
const out= s.listeners.delete(listener);
if(clear_when_empty && !s.listeners.size){
observable.clear(o);
if(!deps.has(s)) return out;
const c= deps.get(s);
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=> removeObservableListener(sig, c, true));
deps.get(c).forEach(sig=> removeSignalListener(sig, c, true));
}
return out;
}

13
src/signals-common.js Normal file
View File

@ -0,0 +1,13 @@
export const signals_global= {
isSignal(attributes){ return false; },
processReactiveAttribute(obj, key, attr, set){ return attr; },
};
export function registerReactivity(def, global= true){
if(global) return Object.assign(signals_global, def);
Object.setPrototypeOf(def, signals_global);
return def;
}
/** @param {unknown} _this @returns {typeof signals_global} */
export function signals(_this){
return signals_global.isPrototypeOf(_this) && _this!==signals_global ? _this : signals_global;
}

4
src/signals-lib.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
import type { Action, Actions, signal as s, Signal, SymbolOnclear } from "../signals.d.ts";
export { Action, Actions, Signal, SymbolOnclear };
export const S: s;
export const signal: s;

292
src/signals-lib.js Normal file
View File

@ -0,0 +1,292 @@
export const mark= "__dde_signal";
import { hasOwn } from "./helpers.js";
export function isSignal(candidate){
try{ return hasOwn(candidate, mark); }
catch(e){ return false; }
}
/** @type {function[]} */
const stack_watch= [];
/**
* ### `WeakMap<function, Set<ddeSignal<any, any>>>`
* The `Set` is in the form of `[ source, ...depended signals (DSs) ]`.
* When the DS is cleaned (`S.clear`) it is removed from DSs,
* if remains only one (`source`) it is cleared too.
* ### `WeakMap<object, function>`
* This is used for revesed deps, the `function` is also key for `deps`.
* @type {WeakMap<function|object,Set<ddeSignal<any, any>>|function>}
* */
const deps= new WeakMap();
export 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, 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);
}
};
deps.set(out[mark], contextReWatch);
deps.set(contextReWatch, new Set([ out ]));
contextReWatch();
return out;
}
export { signal as S };
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));
};
signal.on= function on(s, listener, options= {}){
const { signal: as }= options;
if(as && as.aborted) return;
if(Array.isArray(s)) return s.forEach(s=> on(s, listener, options));
addSignalListener(s, listener);
if(as) as.addEventListener("abort", ()=> removeSignalListener(s, listener));
//TODO cleanup when signal removed
};
signal.symbols= {
//signal: mark,
onclear: Symbol.for("Signal.onclear")
};
signal.clear= function(...signals){
for(const s of signals){
const M= s[mark];
if(!M) continue;
delete s.toJSON;
M.onclear.forEach(f=> f.call(M));
clearListDeps(s, M);
delete s[mark];
}
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);
});
}
};
const key_reactive= "__dde_reactive";
import { enviroment as env } from "./dom-common.js";
import { el } from "./dom.js";
import { scope } from "./dom.js";
// TODO: third argument for handle `cache_tmp` in re-render
signal.el= function(s, map){
const mark_start= el.mark({ type: "reactive" }, true);
const mark_end= mark_start.end;
const out= env.D.createDocumentFragment();
out.append(mark_start, mark_end);
const { current }= scope;
let cache= {};
const reRenderReactiveElement= v=> {
if(!mark_start.parentNode || !mark_end.parentNode) // === `isConnected` or wasnt yet rendered
return removeSignalListener(s, reRenderReactiveElement);
const cache_tmp= cache; // will be reused in the useCache or removed in the while loop on the end
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;
});
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());
};
addSignalListener(s, reRenderReactiveElement);
removeSignalsFromElements(s, reRenderReactiveElement, mark_start, map);
reRenderReactiveElement(s());
return out;
};
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));
});
}
import { on } from "./events.js";
import { observedAttributes } from "./helpers.js";
const observedAttributeActions= {
_set(value){ this.value= value; },
};
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;
};
}
const 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.*/
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.*/
signal.clear(...Object.values(this[key_attributes]));
})(element);
return attrs;
};
import { typeOf } from './helpers.js';
export const 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);
};
addSignalListener(attrs, l);
removeSignalsFromElements(attrs, l, element, key);
return attrs();
}
};
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(([ [ s, listener ] ])=>
removeSignalListener(s, listener, s[mark] && s[mark].host && s[mark].host() === element))
)(element);
}
element[key_reactive].push([ [ s, listener ], ...notes ]);
});
}
function create(is_readonly, value, actions){
const varS= is_readonly
? ()=> read(varS)
: (...value)=> value.length ? write(varS, ...value) : read(varS);
return toSignal(varS, value, actions, is_readonly);
}
const protoSigal= Object.assign(Object.create(null), {
stopPropagation(){
this.skip= true;
}
});
class SignalDefined extends Error{
constructor(){
super();
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 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];
}
const { host }= scope;
Reflect.defineProperty(s, mark, {
value: {
value, actions, onclear, host,
listeners: 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;
}