1
0
mirror of https://github.com/jaandrle/deka-dom-el synced 2024-11-21 15:39:36 +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 }){ function component({ textContent, className }){
const value= O("onchange"); const value= S("onchange");
return el().append( return el().append(
el("p", { textContent, className }), el("p", { textContent, className }),
@ -35,8 +35,7 @@ function component({ textContent, className }){
} }
``` ```
# Deka DOM Elements # 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 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).
([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)).
## Inspiration and suggested alternatives ## 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) - 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 pure JavaScript (DOM API) and gradually adding auxiliary functions, ranging from “minor” improvements
to the capability of writing complete declarative reactive UI templates. 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, 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. hopefully, help in integrating the library into existing projects.
To balance these requirements, numerous compromises have been made. To summarize: To balance these requirements, numerous compromises have been made. To summarize:
- [ ] Library size: 1015kB minified (the original goal was a maximum of 10kB) - [ ] 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] *No build step required*
- [x] Preference for a *declarative/functional* approach - [x] Preference for a *declarative/functional* approach
- [x] Focus on zero/minimal dependencies - [x] Focus on zero/minimal dependencies
@ -73,3 +72,9 @@ To balance these requirements, numerous compromises have been made. To summarize
- Installation - Installation
- npm - npm
- [dist/](dist/) (`https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/`…) - [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 #!/usr/bin/env -S npx nodejsscript
import { bundle as bundleDTS } from "dts-bundler"; 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 filesOut= (file, mark= "esm")=> "dist/"+file.replace("index", mark);
const css= echo.css` const css= echo.css`
.info{ color: gray; } .info{ color: gray; }
`; `;
$.api("", true) $.api("", true)
.option("--minify", "Level of minification [ full (default), partial ]") .option("--minify", "Level of minification [ full, partial (default) ]")
.action(async function main({ minify= "full" }){ .action(async function main({ minify= "partial" }){
for(const file_root of files){ for(const file_root of files){
const file= file_root+".js"; const file= file_root+".js";
echo("Processing: "+ file); 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 */ /* 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("Building static documentation files…");
echo("Preparing…"); 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 { createHTMl } from "./docs/jsdom.js";
import { register } from "../jsdom.js"; import { register } from "../jsdom.js";
const pkg= s.cat("package.json").xargs(JSON.parse); const pkg= s.cat("package.json").xargs(JSON.parse);
for(const info of pages){ echo("Collecting list of pages…");
const { id }= info; const pages= s.ls($.xdg.main`../docs_src/*.html.js`).map(addPage);
for(const { id, info } of pages){
echo(`Generating ${id}.html…`); echo(`Generating ${id}.html…`);
const serverDOM= createHTMl(""); const serverDOM= createHTMl("");
serverDOM.registerGlobally( serverDOM.registerGlobally(
"HTMLScriptElement" "HTMLScriptElement"
); );
const { el }= await register(serverDOM.dom); 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( serverDOM.document.body.append(
el(page, { pkg, info }), el(page, { pkg, info }),
); );
@ -27,3 +28,17 @@ for(const info of pages){
s.echo(styles.content).to(path_target.css+styles.name); s.echo(styles.content).to(path_target.css+styles.name);
dispatchEvent("onssrend"); dispatchEvent("onssrend");
echo("Done"); 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` //deka-dom-el library is available via global namespace `dde`
(()=> { (()=> {
// src/observables-common.js // src/signals-common.js
var k = { var k = {
isObservable(t) { isSignal(t) {
return !1; return !1;
}, },
processReactiveAttribute(t, e, n, r) { processReactiveAttribute(t, e, n, r) {
@ -18,7 +18,7 @@ function W(t) {
// src/helpers.js // src/helpers.js
var T = (...t) => Object.prototype.hasOwnProperty.call(...t); var T = (...t) => Object.prototype.hasOwnProperty.call(...t);
function A(t) { function S(t) {
return typeof t > "u"; return typeof t > "u";
} }
function X(t) { function X(t) {
@ -54,17 +54,17 @@ var d = {
M: globalThis.MutationObserver M: globalThis.MutationObserver
}; };
function lt(t, e, n) { 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") if (Reflect.deleteProperty(t, e), t instanceof d.H && t.getAttribute(e) === "undefined")
return t.removeAttribute(e); return t.removeAttribute(e);
if (Reflect.get(t, e) === "undefined") if (Reflect.get(t, e) === "undefined")
return Reflect.set(t, e, ""); 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 // src/dom.js
var _ = [{ var A = [{
get scope() { get scope() {
return d.D.body; return d.D.body;
}, },
@ -72,7 +72,7 @@ var _ = [{
prevent: !0 prevent: !0
}], m = { }], m = {
get current() { get current() {
return _[_.length - 1]; return A[A.length - 1];
}, },
get host() { get host() {
return this.current.host; return this.current.host;
@ -82,17 +82,17 @@ var _ = [{
return t.prevent = !0, t; return t.prevent = !0, t;
}, },
get state() { get state() {
return [..._]; return [...A];
}, },
push(t = {}) { push(t = {}) {
return _.push(Object.assign({}, this.current, { prevent: !1 }, t)); return A.push(Object.assign({}, this.current, { prevent: !1 }, t));
}, },
pushRoot() { pushRoot() {
return _.push(_[0]); return A.push(A[0]);
}, },
pop() { pop() {
if (_.length !== 1) if (A.length !== 1)
return _.pop(); return A.pop();
} }
}; };
function Y(...t) { function Y(...t) {
@ -102,68 +102,68 @@ function ht(t) {
return t.append === Y || (t.appendOriginal = t.append, t.append = Y), t; return t.append === Y || (t.appendOriginal = t.append, t.append = Y), t;
} }
var $; var $;
function M(t, e, ...n) { function j(t, e, ...n) {
let r = W(this), o = 0, c, s; let r = W(this), o = 0, c, i;
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": { 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; let a = c instanceof d.F;
if (c.nodeName === "#comment") if (c.nodeName === "#comment")
break; break;
let h = M.mark({ let h = j.mark({
type: "component", type: "component",
name: t.name, name: t.name,
host: a ? "this" : "parentElement" host: a ? "this" : "parentElement"
}); });
c.prepend(h), a && (s = h); c.prepend(h), a && (i = h);
break; break;
} }
case t === "#text": case t === "#text":
c = j.call(this, d.D.createTextNode(""), e); c = P.call(this, d.D.createTextNode(""), e);
break; break;
case (t === "<>" || !t): case (t === "<>" || !t):
c = j.call(this, d.D.createDocumentFragment(), e); c = P.call(this, d.D.createDocumentFragment(), e);
break; break;
case !!$: case !!$:
c = j.call(this, d.D.createElementNS($, t), e); c = P.call(this, d.D.createElementNS($, t), e);
break; break;
case !c: 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) { 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, { if (t.append = new Proxy(t.append, {
apply(s, a, h) { apply(i, a, h) {
if (!h.length) if (!h.length)
return t; return t;
let g = d.D.createDocumentFragment(); let v = d.D.createDocumentFragment();
for (let l of h) { for (let l of h) {
if (!l || !l.slot) { if (!l || !l.slot) {
c && g.appendChild(l); c && v.appendChild(l);
continue; continue;
} }
let x = l.slot, w = o[x]; let x = l.slot, y = o[x];
gt(l, "remove", "slot"), w && (bt(w, l, n), Reflect.deleteProperty(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) { }), t !== e) {
let s = Array.from(t.childNodes); let i = Array.from(t.childNodes);
s.forEach((a) => a.remove()), t.append(...s); i.forEach((a) => a.remove()), t.append(...i);
} }
return e; return e;
} }
function bt(t, e, n) { function bt(t, e, n) {
n && n(t, e); n && n(t, e);
try { 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 { } catch {
t.replaceWith(e); 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(" "); t = Object.entries(t).map(([o, c]) => o + `="${c}"`).join(" ");
let n = e ? "" : "/", r = d.D.createComment(`<dde:mark ${t}${d.ssr}${n}>`); let n = e ? "" : "/", r = d.D.createComment(`<dde:mark ${t}${d.ssr}${n}>`);
return e && (r.end = d.D.createComment("</dde:mark>")), r; return e && (r.end = d.D.createComment("</dde:mark>")), r;
@ -172,12 +172,12 @@ function qt(t) {
let e = this; let e = this;
return function(...r) { return function(...r) {
$ = t; $ = t;
let o = M.call(e, ...r); let o = j.call(e, ...r);
return $ = void 0, o; return $ = void 0, o;
}; };
} }
var U = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: tt } = d; var U = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: tt } = d;
function j(t, ...e) { function P(t, ...e) {
if (!e.length) if (!e.length)
return t; return t;
U.set(t, rt(t, this)); U.set(t, rt(t, this));
@ -193,10 +193,10 @@ function nt(t, e, n) {
n, n,
(a, h) => nt.call(c, t, a, h) (a, h) => nt.call(c, t, a, h)
); );
let [s] = e; let [i] = e;
if (s === "=") if (i === "=")
return r(e.slice(1), n); return r(e.slice(1), n);
if (s === ".") if (i === ".")
return et(t, e.slice(1), n); return et(t, e.slice(1), n);
if (/(aria|data)([A-Z])/.test(e)) if (/(aria|data)([A-Z])/.test(e))
return e = e.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(), r(e, n); 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": case "ariaset":
return I(o, n, (a, h) => r("aria-" + a, h)); return I(o, n, (a, h) => r("aria-" + a, h));
case "classList": 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); 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); let r = (t instanceof d.S ? xt : mt).bind(null, t, "Attribute"), o = W(e);
return { setRemoveAttr: r, s: o }; return { setRemoveAttr: r, s: o };
} }
function vt(t, e) { function gt(t, e) {
let n = W(this); let n = W(this);
return I( return I(
n, n,
@ -234,14 +234,14 @@ function vt(t, e) {
function Ft(t) { function Ft(t) {
return Array.from(t.children).forEach((e) => e.remove()), 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); return t instanceof d.H ? t[e + "Attribute"](n, r) : t[e + "AttributeNS"](null, n, r);
} }
function Et(t, e) { function Et(t, e) {
if (!(e in t)) if (!(e in t))
return !1; return !1;
let n = ot(t, e); let n = ot(t, e);
return !A(n.set); return !S(n.set);
} }
function ot(t, e) { function ot(t, e) {
if (t = Object.getPrototypeOf(t), !t) if (t = Object.getPrototypeOf(t), !t)
@ -259,141 +259,141 @@ function ct(t) {
return Array.isArray(t) ? t.filter(Boolean).join(" ") : t; return Array.isArray(t) ? t.filter(Boolean).join(" ") : t;
} }
function mt(t, e, n, r) { 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) { 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) { 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); return Reflect.deleteProperty(t, e);
} }
// src/events-observer.js // src/events-observer.js
var D = d.M ? Ot() : new Proxy({}, { var D = d.M ? wt() : new Proxy({}, {
get() { get() {
return () => { return () => {
}; };
} }
}); });
function Ot() { function wt() {
let t = /* @__PURE__ */ new Map(), e = !1, n = (i) => function(u) { let t = /* @__PURE__ */ new Map(), e = !1, n = (s) => function(u) {
for (let f of u) for (let f of u)
if (f.type === "childList") { if (f.type === "childList") {
if (l(f.addedNodes, !0)) { if (l(f.addedNodes, !0)) {
i(); s();
continue; continue;
} }
x(f.removedNodes, !0) && i(); x(f.removedNodes, !0) && s();
} }
}, r = new d.M(n(a)); }, r = new d.M(n(a));
return { return {
observe(i) { observe(s) {
let u = new d.M(n(() => { 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) { onConnected(s, u) {
s(); i();
let f = c(i); let f = c(s);
f.connected.has(u) || (f.connected.add(u), f.length_c += 1); f.connected.has(u) || (f.connected.add(u), f.length_c += 1);
}, },
offConnected(i, u) { offConnected(s, u) {
if (!t.has(i)) if (!t.has(s))
return; return;
let f = t.get(i); let f = t.get(s);
f.connected.has(u) && (f.connected.delete(u), f.length_c -= 1, o(i, f)); f.connected.has(u) && (f.connected.delete(u), f.length_c -= 1, o(s, f));
}, },
onDisconnected(i, u) { onDisconnected(s, u) {
s(); i();
let f = c(i); let f = c(s);
f.disconnected.has(u) || (f.disconnected.add(u), f.length_d += 1); f.disconnected.has(u) || (f.disconnected.add(u), f.length_d += 1);
}, },
offDisconnected(i, u) { offDisconnected(s, u) {
if (!t.has(i)) if (!t.has(s))
return; return;
let f = t.get(i); let f = t.get(s);
f.disconnected.has(u) && (f.disconnected.delete(u), f.length_d -= 1, o(i, f)); f.disconnected.has(u) && (f.disconnected.delete(u), f.length_d -= 1, o(s, f));
} }
}; };
function o(i, u) { function o(s, u) {
u.length_c || u.length_d || (t.delete(i), a()); u.length_c || u.length_d || (t.delete(s), a());
} }
function c(i) { function c(s) {
if (t.has(i)) if (t.has(s))
return t.get(i); return t.get(s);
let u = { let u = {
connected: /* @__PURE__ */ new WeakSet(), connected: /* @__PURE__ */ new WeakSet(),
length_c: 0, length_c: 0,
disconnected: /* @__PURE__ */ new WeakSet(), disconnected: /* @__PURE__ */ new WeakSet(),
length_d: 0 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 })); e || (e = !0, r.observe(d.D.body, { childList: !0, subtree: !0 }));
} }
function a() { function a() {
!e || t.size || (e = !1, r.disconnect()); !e || t.size || (e = !1, r.disconnect());
} }
function h() { function h() {
return new Promise(function(i) { return new Promise(function(s) {
(requestIdleCallback || requestAnimationFrame)(i); (requestIdleCallback || requestAnimationFrame)(s);
}); });
} }
async function g(i) { async function v(s) {
t.size > 30 && await h(); t.size > 30 && await h();
let u = []; let u = [];
if (!(i instanceof Node)) if (!(s instanceof Node))
return u; return u;
for (let f of t.keys()) 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; return u;
} }
function l(i, u) { function l(s, u) {
let f = !1; let f = !1;
for (let b of i) { for (let b of s) {
if (u && g(b).then(l), !t.has(b)) if (u && v(b).then(l), !t.has(b))
continue; continue;
let N = t.get(b); 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; return f;
} }
function x(i, u) { function x(s, u) {
let f = !1; let f = !1;
for (let b of i) for (let b of s)
u && g(b).then(x), !(!t.has(b) || !t.get(b).length_d) && ((globalThis.queueMicrotask || setTimeout)(w(b)), f = !0); u && v(b).then(x), !(!t.has(b) || !t.get(b).length_d) && ((globalThis.queueMicrotask || setTimeout)(y(b)), f = !0);
return f; return f;
} }
function w(i) { function y(s) {
return () => { return () => {
i.isConnected || (i.dispatchEvent(new Event(S)), t.delete(i)); s.isConnected || (s.dispatchEvent(new Event(O)), t.delete(s));
}; };
} }
} }
// src/customElement.js // src/customElement.js
function Zt(t, e, n, r = yt) { function Zt(t, e, n, r = _t) {
m.push({ m.push({
scope: t, 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)); }), typeof r == "function" && (r = r.call(t, t));
let o = t[C]; let o = t[C];
o || wt(t); o || yt(t);
let c = n.call(t, r); 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) { 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) { }), J(t.prototype, "disconnectedCallback", function(e, n, r) {
e.apply(n, r), (globalThis.queueMicrotask || setTimeout)( 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) { }), J(t.prototype, "attributeChangedCallback", function(e, n, r) {
let [o, , c] = r; let [o, , c] = r;
n.dispatchEvent(new CustomEvent(P, { n.dispatchEvent(new CustomEvent(M, {
detail: [o, c] detail: [o, c]
})), e.apply(n, r); })), e.apply(n, r);
}), t.prototype[C] = !0, t; }), t.prototype[C] = !0, t;
@ -402,7 +402,7 @@ function J(t, e, n) {
t[e] = new Proxy(t[e] || (() => { t[e] = new Proxy(t[e] || (() => {
}), { apply: n }); }), { apply: n });
} }
function yt(t) { function _t(t) {
return F(t, (e, n) => e.getAttribute(n)); return F(t, (e, n) => e.getAttribute(n));
} }
@ -410,50 +410,50 @@ function yt(t) {
function Qt(t, e, n) { function Qt(t, e, n) {
return e || (e = {}), function(o, ...c) { return e || (e = {}), function(o, ...c) {
n && (c.unshift(o), o = typeof n == "function" ? n() : n); 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); let i = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e);
return o.dispatchEvent(s); return o.dispatchEvent(i);
}; };
} }
function O(t, e, n) { function w(t, e, n) {
return function(o) { return function(o) {
return o.addEventListener(t, e, n), o; return o.addEventListener(t, e, n), o;
}; };
} }
var st = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 }); var it = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 });
O.connected = function(t, e) { w.connected = function(t, e) {
return e = st(e), function(r) { return e = it(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); 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) { w.disconnected = function(t, e) {
return e = st(e), function(r) { return e = it(e), function(r) {
return r.addEventListener(S, t, e), r[C] || q(e.signal, () => D.offDisconnected(r, t)) && D.onDisconnected(r, t), 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(); var Z = /* @__PURE__ */ new WeakMap();
O.disconnectedAsAbort = function(t) { w.disconnectedAsAbort = function(t) {
if (Z.has(t)) if (Z.has(t))
return Z.get(t); return Z.get(t);
let e = new AbortController(); 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(); var At = /* @__PURE__ */ new WeakSet();
O.attributeChanged = function(t, e) { w.attributeChanged = function(t, e) {
return typeof e != "object" && (e = {}), function(r) { 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; return r;
let o = new d.M(function(s) { let o = new d.M(function(i) {
for (let { attributeName: a, target: h } of s) for (let { attributeName: a, target: h } of i)
h.dispatchEvent( 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; return q(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r;
}; };
}; };
// src/observables-lib.js // src/signals-lib.js
var p = "__dde_observable"; var p = "__dde_signal";
function z(t) { function z(t) {
try { try {
return T(t, p); return T(t, p);
@ -461,21 +461,21 @@ function z(t) {
return !1; return !1;
} }
} }
var H = [], v = /* @__PURE__ */ new WeakMap(); var H = [], g = /* @__PURE__ */ new WeakMap();
function E(t, e) { function E(t, e) {
if (typeof t != "function") if (typeof t != "function")
return it(!1, t, e); return st(!1, t, e);
if (z(t)) if (z(t))
return t; return t;
let n = it(!0), r = function() { let n = st(!0), r = function() {
let [o, ...c] = v.get(r); let [o, ...c] = g.get(r);
if (v.set(r, /* @__PURE__ */ new Set([o])), H.push(r), dt(n, t()), H.pop(), !c.length) if (g.set(r, /* @__PURE__ */ new Set([o])), H.push(r), dt(n, t()), H.pop(), !c.length)
return; return;
let s = v.get(r); let i = g.get(r);
for (let a of c) 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) { E.action = function(t, e, ...n) {
let r = t[p], { actions: o } = r; let r = t[p], { actions: o } = r;
@ -494,8 +494,8 @@ E.on = function t(e, n, r = {}) {
} }
}; };
E.symbols = { E.symbols = {
//observable: mark, //signal: mark,
onclear: Symbol.for("Observable.onclear") onclear: Symbol.for("Signal.onclear")
}; };
E.clear = function(...t) { E.clear = function(...t) {
for (let n of t) { for (let n of t) {
@ -504,37 +504,37 @@ E.clear = function(...t) {
} }
function e(n, r) { function e(n, r) {
r.listeners.forEach((o) => { r.listeners.forEach((o) => {
if (r.listeners.delete(o), !v.has(o)) if (r.listeners.delete(o), !g.has(o))
return; return;
let c = v.get(o); let c = g.get(o);
c.delete(n), !(c.size > 1) && (n.clear(...c), v.delete(o)); c.delete(n), !(c.size > 1) && (n.clear(...c), g.delete(o));
}); });
} }
}; };
var R = "__dde_reactive"; var R = "__dde_reactive";
E.el = function(t, e) { 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); o.append(n, r);
let { current: c } = m, s = {}, a = (h) => { let { current: c } = m, i = {}, a = (h) => {
if (!n.parentNode || !r.parentNode) if (!n.parentNode || !r.parentNode)
return L(t, a); return L(t, a);
let g = s; let v = i;
s = {}, m.push(c); i = {}, m.push(c);
let l = e(h, function(u, f) { let l = e(h, function(u, f) {
let b; 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]); m.pop(), Array.isArray(l) || (l = [l]);
let x = document.createComment(""); let x = document.createComment("");
l.push(x), n.after(...l); l.push(x), n.after(...l);
let w; let y;
for (; (w = x.nextSibling) && w !== r; ) for (; (y = x.nextSibling) && y !== r; )
w.remove(); y.remove();
x.remove(), n.isConnected && At(c.host()); x.remove(), n.isConnected && St(c.host());
}; };
return Q(t, a), ft(t, a, n, e), a(t()), o; 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 || !t[R] || (requestIdleCallback || setTimeout)(function() {
t[R] = t[R].filter(([e, n]) => n.isConnected ? !0 : (L(...e), !1)); t[R] = t[R].filter(([e, n]) => n.isConnected ? !0 : (L(...e), !1));
}); });
@ -544,7 +544,7 @@ var Ct = {
this.value = t; this.value = t;
} }
}; };
function St(t) { function Ot(t) {
return function(e, n) { return function(e, n) {
let r = (...c) => c.length ? e.setAttribute(n, ...c) : K(r), o = at(r, e.getAttribute(n), Ct); let r = (...c) => c.length ? e.setAttribute(n, ...c) : K(r), o = at(r, e.getAttribute(n), Ct);
return t[n] = o, o; return t[n] = o, o;
@ -552,21 +552,21 @@ function St(t) {
} }
var G = "__dde_attributes"; var G = "__dde_attributes";
E.observedAttributes = function(t) { E.observedAttributes = function(t) {
let e = t[G] = {}, n = F(t, St(e)); let e = t[G] = {}, n = F(t, Ot(e));
return O.attributeChanged(function({ detail: o }) { return w.attributeChanged(function({ detail: o }) {
/*! This maps attributes to observables (`O.observedAttributes`). /*! This maps attributes to signals (`S.observedAttributes`).
* Investigate `__dde_attributes` key of the element.*/ * 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) if (a)
return E.action(a, "_set", s); return E.action(a, "_set", i);
})(t), O.disconnected(function() { })(t), w.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.*/ * Investigate `__dde_attributes` key of the element.*/
E.clear(...Object.values(this[G])); E.clear(...Object.values(this[G]));
})(t), n; })(t), n;
}; };
var ut = { var ut = {
isObservable: z, isSignal: z,
processReactiveAttribute(t, e, n, r) { processReactiveAttribute(t, e, n, r) {
if (!z(n)) if (!z(n))
return n; return n;
@ -581,18 +581,18 @@ var ut = {
function ft(t, e, ...n) { function ft(t, e, ...n) {
let { current: r } = m; let { current: r } = m;
r.prevent || r.host(function(o) { 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. * 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]); )(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); let r = t ? () => K(r) : (...o) => o.length ? dt(r, ...o) : K(r);
return at(r, e, n, t); return at(r, e, n, t);
} }
@ -613,13 +613,13 @@ function at(t, e, n, r = !1) {
X(n) !== "[object Object]" && (n = {}); X(n) !== "[object Object]" && (n = {});
let { onclear: c } = E.symbols; let { onclear: c } = E.symbols;
n[c] && (o.push(n[c]), delete n[c]); n[c] && (o.push(n[c]), delete n[c]);
let { host: s } = m; let { host: i } = m;
return Reflect.defineProperty(t, p, { return Reflect.defineProperty(t, p, {
value: { value: {
value: e, value: e,
actions: n, actions: n,
onclear: o, onclear: o,
host: s, host: i,
listeners: /* @__PURE__ */ new Set(), listeners: /* @__PURE__ */ new Set(),
defined: new V().stack, defined: new V().stack,
readonly: r readonly: r
@ -636,7 +636,7 @@ function K(t) {
if (!t[p]) if (!t[p])
return; return;
let { value: e, listeners: n } = t[p], r = Rt(); 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) { function dt(t, e, n) {
if (!t[p]) if (!t[p])
@ -655,41 +655,41 @@ function L(t, e, n) {
return; return;
let o = r.listeners.delete(e); let o = r.listeners.delete(e);
if (n && !r.listeners.size) { if (n && !r.listeners.size) {
if (E.clear(t), !v.has(r)) if (E.clear(t), !g.has(r))
return o; return o;
let c = v.get(r); let c = g.get(r);
if (!v.has(c)) if (!g.has(c))
return o; return o;
v.get(c).forEach((s) => L(s, c, !0)); g.get(c).forEach((i) => L(i, c, !0));
} }
return o; return o;
} }
// observables.js // signals.js
B(ut); B(ut);
globalThis.dde= { globalThis.dde= {
O: E, S: E,
assign: j, assign: P,
assignAttribute: nt, assignAttribute: nt,
chainableAppend: ht, chainableAppend: ht,
classListDeclarative: vt, classListDeclarative: gt,
createElement: M, createElement: j,
createElementNS: qt, createElementNS: qt,
customElementRender: Zt, customElementRender: Zt,
customElementWithDDE: wt, customElementWithDDE: yt,
dispatchEvent: Qt, dispatchEvent: Qt,
el: M, el: j,
elNS: qt, elNS: qt,
elementAttribute: gt, elementAttribute: vt,
empty: Ft, empty: Ft,
isObservable: z, isSignal: z,
lifecyclesToEvents: wt, lifecyclesToEvents: yt,
observable: E, observedAttributes: _t,
observedAttributes: yt, on: w,
on: O,
registerReactivity: B, registerReactivity: B,
scope: m, scope: m,
signal: E,
simulateSlots: Wt simulateSlots: Wt
}; };

88
dist/dde.js vendored
View File

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

View File

@ -1,17 +1,17 @@
export type Observable<V, A>= (set?: V)=> V & A; export type Signal<V, A>= (set?: V)=> V & A;
type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typeof observable._ | void; type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typeof signal._ | void;
//type SymbolObservable= Symbol; //type SymbolSignal= Symbol;
type SymbolOnclear= symbol; type SymbolOnclear= symbol;
type Actions<V>= Record<string | SymbolOnclear, Action<V>>; type Actions<V>= Record<string | SymbolOnclear, Action<V>>;
type OnListenerOptions= Pick<AddEventListenerOptions, "signal"> & { first_time?: boolean }; type OnListenerOptions= Pick<AddEventListenerOptions, "signal"> & { first_time?: boolean };
interface observable{ interface signal{
_: Symbol _: Symbol
/** /**
* Simple example: * Simple example:
* ```js * ```js
* const hello= S("Hello Observable"); * const hello= S("Hello Signal");
* ``` * ```
* simple todo observable: * simple todo signal:
* ```js * ```js
* const todos= S([], { * const todos= S([], {
* add(v){ this.value.push(S(v)); }, * add(v){ this.value.push(S(v)); },
@ -19,48 +19,48 @@ interface observable{
* [S.symbols.onclear](){ S.clear(...this.value); }, * [S.symbols.onclear](){ S.clear(...this.value); },
* }); * });
* ``` * ```
* computed observable: * computed signal:
* ```js * ```js
* const name= S("Jan"); * const name= S("Jan");
* const surname= S("Andrle"); * const surname= S("Andrle");
* const fullname= S(()=> name()+" "+surname()); * const fullname= S(()=> name()+" "+surname());
* ``` * ```
* @param value Initial observable value. Or function computing value from other observables. * @param value Initial signal value. Or function computing value from other signals.
* @param actions Use to define actions on the observable. Such as add item to the array. * @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 observable is cleared * There is also a reserved function `S.symbol.onclear` which is called when the signal is cleared
* by `S.clear`. * 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, {}> <V>(computation: ()=> V): Signal<V, {}>
action<S extends Observable<any, Actions<any>>, A extends (S extends Observable<any, infer A> ? A : never), N extends keyof A>( action<S extends Signal<any, Actions<any>>, A extends (S extends Signal<any, infer A> ? A : never), N extends keyof A>(
observable: S, signal: S,
name: N, name: N,
...params: A[N] extends (...args: infer P)=> any ? P : never ...params: A[N] extends (...args: infer P)=> any ? P : never
): void; ): void;
clear(...observables: Observable<any, any>[]): void; clear(...signals: Signal<any, any>[]): void;
on<T>(observable: Observable<T, any>, onchange: (a: T)=> void, options?: OnListenerOptions): void; on<T>(signal: Signal<T, any>, onchange: (a: T)=> void, options?: OnListenerOptions): void;
symbols: { symbols: {
//observable: SymbolObservable; //signal: SymbolSignal;
onclear: SymbolOnclear; onclear: SymbolOnclear;
} }
/** /**
* Reactive element, which is rendered based on the given observable. * Reactive element, which is rendered based on the given signal.
* ```js * ```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))); * 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 signal: signal;
export const O: observable; export const S: signal;
declare global { declare global {
type ddeObservable<T, A= {}>= Observable<T, A>; type ddeSignal<T, A= {}>= Signal<T, A>;
type ddeAction<V>= Action<V> type ddeAction<V>= Action<V>
type ddeActions<V>= Actions<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). * 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. * 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[]` * 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` * Sets `aria-*` simiraly to `dataset`
* */ * */
ariaset: Record<string,string|Observable<string, any>>, ariaset: Record<string,string|Signal<string, any>>,
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|Observable<string, any>> & Record<`.${string}`, any> } & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|Signal<string, any>> & Record<`.${string}`, any>
type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>; type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>;
/** /**
* Just element attributtes * 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. * There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
* @private * @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 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 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] 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 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< export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap & string, TAG extends keyof ExtendedHTMLElementTagNameMap & string,
EL extends (TAG extends keyof ExtendedHTMLElementTagNameMap ? ExtendedHTMLElementTagNameMap[TAG] : HTMLElement) 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 ), EL extends ( TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement ),
>( >(
tag_name: TAG, 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>[] ...addons: ddeElementAddon<EL>[]
)=> ddeMathMLElement )=> ddeMathMLElement
export function elNS( export function elNS(

View File

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

66
dist/esm.d.ts vendored
View File

@ -1,17 +1,17 @@
export type Observable<V, A>= (set?: V)=> V & A; export type Signal<V, A>= (set?: V)=> V & A;
type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typeof observable._ | void; type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typeof signal._ | void;
//type SymbolObservable= Symbol; //type SymbolSignal= Symbol;
type SymbolOnclear= symbol; type SymbolOnclear= symbol;
type Actions<V>= Record<string | SymbolOnclear, Action<V>>; type Actions<V>= Record<string | SymbolOnclear, Action<V>>;
type OnListenerOptions= Pick<AddEventListenerOptions, "signal"> & { first_time?: boolean }; type OnListenerOptions= Pick<AddEventListenerOptions, "signal"> & { first_time?: boolean };
interface observable{ interface signal{
_: Symbol _: Symbol
/** /**
* Simple example: * Simple example:
* ```js * ```js
* const hello= S("Hello Observable"); * const hello= S("Hello Signal");
* ``` * ```
* simple todo observable: * simple todo signal:
* ```js * ```js
* const todos= S([], { * const todos= S([], {
* add(v){ this.value.push(S(v)); }, * add(v){ this.value.push(S(v)); },
@ -19,48 +19,48 @@ interface observable{
* [S.symbols.onclear](){ S.clear(...this.value); }, * [S.symbols.onclear](){ S.clear(...this.value); },
* }); * });
* ``` * ```
* computed observable: * computed signal:
* ```js * ```js
* const name= S("Jan"); * const name= S("Jan");
* const surname= S("Andrle"); * const surname= S("Andrle");
* const fullname= S(()=> name()+" "+surname()); * const fullname= S(()=> name()+" "+surname());
* ``` * ```
* @param value Initial observable value. Or function computing value from other observables. * @param value Initial signal value. Or function computing value from other signals.
* @param actions Use to define actions on the observable. Such as add item to the array. * @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 observable is cleared * There is also a reserved function `S.symbol.onclear` which is called when the signal is cleared
* by `S.clear`. * 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, {}> <V>(computation: ()=> V): Signal<V, {}>
action<S extends Observable<any, Actions<any>>, A extends (S extends Observable<any, infer A> ? A : never), N extends keyof A>( action<S extends Signal<any, Actions<any>>, A extends (S extends Signal<any, infer A> ? A : never), N extends keyof A>(
observable: S, signal: S,
name: N, name: N,
...params: A[N] extends (...args: infer P)=> any ? P : never ...params: A[N] extends (...args: infer P)=> any ? P : never
): void; ): void;
clear(...observables: Observable<any, any>[]): void; clear(...signals: Signal<any, any>[]): void;
on<T>(observable: Observable<T, any>, onchange: (a: T)=> void, options?: OnListenerOptions): void; on<T>(signal: Signal<T, any>, onchange: (a: T)=> void, options?: OnListenerOptions): void;
symbols: { symbols: {
//observable: SymbolObservable; //signal: SymbolSignal;
onclear: SymbolOnclear; onclear: SymbolOnclear;
} }
/** /**
* Reactive element, which is rendered based on the given observable. * Reactive element, which is rendered based on the given signal.
* ```js * ```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))); * 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 signal: signal;
export const O: observable; export const S: signal;
declare global { declare global {
type ddeObservable<T, A= {}>= Observable<T, A>; type ddeSignal<T, A= {}>= Signal<T, A>;
type ddeAction<V>= Action<V> type ddeAction<V>= Action<V>
type ddeActions<V>= Actions<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). * 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. * 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[]` * 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` * Sets `aria-*` simiraly to `dataset`
* */ * */
ariaset: Record<string,string|Observable<string, any>>, ariaset: Record<string,string|Signal<string, any>>,
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|Observable<string, any>> & Record<`.${string}`, any> } & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|Signal<string, any>> & Record<`.${string}`, any>
type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>; type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>;
/** /**
* Just element attributtes * 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. * There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
* @private * @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 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 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] 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 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< export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap & string, TAG extends keyof ExtendedHTMLElementTagNameMap & string,
EL extends (TAG extends keyof ExtendedHTMLElementTagNameMap ? ExtendedHTMLElementTagNameMap[TAG] : HTMLElement) 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 ), EL extends ( TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement ),
>( >(
tag_name: TAG, 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>[] ...addons: ddeElementAddon<EL>[]
)=> ddeMathMLElement )=> ddeMathMLElement
export function elNS( export function elNS(

88
dist/esm.js vendored
View File

@ -1,6 +1,6 @@
// src/observables-common.js // src/signals-common.js
var C = { var C = {
isObservable(t) { isSignal(t) {
return !1; return !1;
}, },
processReactiveAttribute(t, e, n, r) { processReactiveAttribute(t, e, n, r) {
@ -55,18 +55,18 @@ function K(t, e, n) {
return Reflect.set(t, e, ""); 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 // src/dom.js
var g = [{ var v = [{
get scope() { get scope() {
return a.D.body; return a.D.body;
}, },
host: (t) => t ? t(a.D.body) : a.D.body, host: (t) => t ? t(a.D.body) : a.D.body,
prevent: !0 prevent: !0
}], R = { }], S = {
get current() { get current() {
return g[g.length - 1]; return v[v.length - 1];
}, },
get host() { get host() {
return this.current.host; return this.current.host;
@ -76,17 +76,17 @@ var g = [{
return t.prevent = !0, t; return t.prevent = !0, t;
}, },
get state() { get state() {
return [...g]; return [...v];
}, },
push(t = {}) { push(t = {}) {
return g.push(Object.assign({}, this.current, { prevent: !1 }, t)); return v.push(Object.assign({}, this.current, { prevent: !1 }, t));
}, },
pushRoot() { pushRoot() {
return g.push(g[0]); return v.push(v[0]);
}, },
pop() { pop() {
if (g.length !== 1) if (v.length !== 1)
return g.pop(); return v.pop();
} }
}; };
function $(...t) { function $(...t) {
@ -98,9 +98,9 @@ function Q(t) {
var T; var T;
function k(t, e, ...n) { function k(t, e, ...n) {
let r = L(this), o = 0, c, f; 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": { 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; let d = c instanceof a.F;
if (c.nodeName === "#comment") if (c.nodeName === "#comment")
break; break;
@ -113,18 +113,18 @@ function k(t, e, ...n) {
break; break;
} }
case t === "#text": case t === "#text":
c = D.call(this, a.D.createTextNode(""), e); c = O.call(this, a.D.createTextNode(""), e);
break; break;
case (t === "<>" || !t): case (t === "<>" || !t):
c = D.call(this, a.D.createDocumentFragment(), e); c = O.call(this, a.D.createDocumentFragment(), e);
break; break;
case !!T: case !!T:
c = D.call(this, a.D.createElementNS(T, t), e); c = O.call(this, a.D.createElementNS(T, t), e);
break; break;
case !c: 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) { 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); 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) { function X(t, e, n) {
n && n(t, e); n && n(t, e);
try { 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 { } catch {
t.replaceWith(e); 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}>`); let n = e ? "" : "/", r = a.D.createComment(`<dde:mark ${t}${a.ssr}${n}>`);
return e && (r.end = a.D.createComment("</dde:mark>")), r; return e && (r.end = a.D.createComment("</dde:mark>")), r;
}; };
function vt(t) { function gt(t) {
let e = this; let e = this;
return function(...r) { return function(...r) {
T = t; T = t;
@ -171,7 +171,7 @@ function vt(t) {
}; };
} }
var P = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: U } = a; var P = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: U } = a;
function D(t, ...e) { function O(t, ...e) {
if (!e.length) if (!e.length)
return t; return t;
P.set(t, B(t, this)); 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) (r, o) => t.classList.toggle(r, o === -1 ? void 0 : !!o)
), t; ), t;
} }
function gt(t) { function vt(t) {
return Array.from(t.children).forEach((e) => e.remove()), t; return Array.from(t.children).forEach((e) => e.remove()), t;
} }
function tt(t, e, n, r) { function tt(t, e, n, r) {
@ -350,7 +350,7 @@ function ot() {
if (u && b(h).then(l), !t.has(h)) if (u && b(h).then(l), !t.has(h))
continue; continue;
let m = t.get(h); 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; return s;
} }
@ -368,26 +368,26 @@ function ot() {
} }
// src/customElement.js // src/customElement.js
function Ot(t, e, n, r = it) { function Dt(t, e, n, r = it) {
R.push({ S.push({
scope: t, scope: t,
host: (...f) => f.length ? f.forEach((d) => d(t)) : t host: (...f) => f.length ? f.forEach((d) => d(t)) : t
}), typeof r == "function" && (r = r.call(t, t)); }), typeof r == "function" && (r = r.call(t, t));
let o = t[x]; let o = t[x];
o || ct(t); o || ct(t);
let c = n.call(t, r); 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) { function ct(t) {
return W(t.prototype, "connectedCallback", function(e, n, r) { 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) { }), W(t.prototype, "disconnectedCallback", function(e, n, r) {
e.apply(n, r), (globalThis.queueMicrotask || setTimeout)( e.apply(n, r), (globalThis.queueMicrotask || setTimeout)(
() => !n.isConnected && n.dispatchEvent(new Event(y)) () => !n.isConnected && n.dispatchEvent(new Event(y))
); );
}), W(t.prototype, "attributeChangedCallback", function(e, n, r) { }), W(t.prototype, "attributeChangedCallback", function(e, n, r) {
let [o, , c] = r; let [o, , c] = r;
n.dispatchEvent(new CustomEvent(O, { n.dispatchEvent(new CustomEvent(D, {
detail: [o, c] detail: [o, c]
})), e.apply(n, r); })), e.apply(n, r);
}), t.prototype[x] = !0, t; }), t.prototype[x] = !0, t;
@ -408,61 +408,61 @@ function _t(t, e, n) {
return o.dispatchEvent(f); return o.dispatchEvent(f);
}; };
} }
function S(t, e, n) { function R(t, e, n) {
return function(o) { return function(o) {
return o.addEventListener(t, e, n), o; return o.addEventListener(t, e, n), o;
}; };
} }
var G = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 }); 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 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 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; 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(); var j = /* @__PURE__ */ new WeakMap();
S.disconnectedAsAbort = function(t) { R.disconnectedAsAbort = function(t) {
if (j.has(t)) if (j.has(t))
return j.get(t); return j.get(t);
let e = new AbortController(); 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(); var st = /* @__PURE__ */ new WeakSet();
S.attributeChanged = function(t, e) { R.attributeChanged = function(t, e) {
return typeof e != "object" && (e = {}), function(r) { 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; return r;
let o = new a.M(function(f) { let o = new a.M(function(f) {
for (let { attributeName: d, target: p } of f) for (let { attributeName: d, target: p } of f)
p.dispatchEvent( 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; return N(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r;
}; };
}; };
export { export {
D as assign, O as assign,
z as assignAttribute, z as assignAttribute,
Q as chainableAppend, Q as chainableAppend,
Y as classListDeclarative, Y as classListDeclarative,
k as createElement, k as createElement,
vt as createElementNS, gt as createElementNS,
Ot as customElementRender, Dt as customElementRender,
ct as customElementWithDDE, ct as customElementWithDDE,
_t as dispatchEvent, _t as dispatchEvent,
k as el, k as el,
vt as elNS, gt as elNS,
tt as elementAttribute, tt as elementAttribute,
gt as empty, vt as empty,
ct as lifecyclesToEvents, ct as lifecyclesToEvents,
it as observedAttributes, it as observedAttributes,
S as on, R as on,
V as registerReactivity, V as registerReactivity,
R as scope, S as scope,
bt as simulateSlots 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 { import {
customElementRender, customElementRender,
customElementWithDDE, customElementWithDDE,
observedAttributes, observedAttributes,
} from "deka-dom-el"; } from "deka-dom-el";
/** @type {ddePublicElementTagNameMap} */ /** @type {ddePublicElementTagNameMap} */
import { O } from "deka-dom-el/observables"; import { O as S } from "deka-dom-el/signals";
O.observedAttributes; S.observedAttributes;
// “internal” utils // “internal” utils
import { lifecyclesToEvents } from "deka-dom-el"; import { lifecyclesToEvents } from "deka-dom-el";
@ -31,4 +31,4 @@ import { lifecyclesToEvents } from "deka-dom-el";
set customAttribute(value){ this.setAttribute("custom-attribute", value); } set customAttribute(value){ this.setAttribute("custom-attribute", value); }
} }
customElements.define(CustomHTMLElement.tagName, CustomHTMLElement); 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 { el } from "deka-dom-el";
import { code } from "./code.html.js"; import { code } from "./code.html.js";
@ -29,7 +29,7 @@ import { relative } from "node:path";
export function example({ src, language= "js", page_id }){ export function example({ src, language= "js", page_id }){
registerClientPart(page_id); registerClientPart(page_id);
const content= s.cat(src).toString() 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); const id= "code-example-"+generateCodeId(src);
return el().append( return el().append(
el(code, { id, content, language, className: example.name }), 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 }){ function elCode({ id, content, extension: name }){
const options= JSON.stringify({ 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 toolbar: false
}); });
return el("script", `Flems(document.getElementById("${id}"), JSON.parse(${JSON.stringify(options)}));`); 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 { import {
customElementRender, customElementRender,
customElementWithDDE, customElementWithDDE,
observedAttributes, observedAttributes,
} from "deka-dom-el"; } from "deka-dom-el";
/** @type {ddePublicElementTagNameMap} */ /** @type {ddePublicElementTagNameMap} */
import { O } from "deka-dom-el/observables"; import { S } from "deka-dom-el/signals";
O.observedAttributes; S.observedAttributes;
// “internal” utils // “internal” utils
import { lifecyclesToEvents } from "deka-dom-el"; import { lifecyclesToEvents } from "deka-dom-el";

View File

@ -1,9 +1,9 @@
import { el } from "deka-dom-el"; import { el } from "deka-dom-el";
import { O } from "deka-dom-el/observables"; import { S } from "deka-dom-el/signals";
const clicks= O(0); const clicks= S(0);
document.body.append( document.body.append(
el().append( el().append(
el("p", O(()=> el("p", S(()=>
"Hello World "+"🎉".repeat(clicks()) "Hello World "+"🎉".repeat(clicks())
)), )),
el("button", { 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" type: "button"
}) })
); );
import { O } from "deka-dom-el/observables"; import { S } from "deka-dom-el/signals";
function component(){ function component(){
const textContent= O("Click to change text."); const textContent= S("Click to change text.");
const onclickChange= on("click", function redispatch(){ const onclickChange= on("click", function redispatch(){
textContent("Text changed! "+(new Date()).toString()) textContent("Text changed! "+(new Date()).toString())

View File

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

View File

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

View File

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

View File

@ -1,26 +1,26 @@
import { O } from "deka-dom-el/observables"; import { S } from "deka-dom-el/signals";
const count= O(0, { const count= S(0, {
add(){ this.value= this.value + Math.round(Math.random()*10); } add(){ this.value= this.value + Math.round(Math.random()*10); }
}); });
const numbers= O([ count() ], { const numbers= S([ count() ], {
push(next){ this.value.push(next); } push(next){ this.value.push(next); }
}); });
import { el } from "deka-dom-el"; import { el } from "deka-dom-el";
document.body.append( document.body.append(
O.el(count, count=> count%2 S.el(count, count=> count%2
? el("p", "Last number is odd.") ? el("p", "Last number is odd.")
: el() : el()
), ),
el("p", "Lucky numbers:"), el("p", "Lucky numbers:"),
el("ul").append( el("ul").append(
O.el(numbers, numbers=> numbers.toReversed() S.el(numbers, numbers=> numbers.toReversed()
.map(n=> el("li", n))) .map(n=> el("li", n)))
) )
); );
const interval= 5*1000; const interval= 5*1000;
setTimeout(clearInterval, 10*interval, setInterval(function(){ setTimeout(clearInterval, 10*interval, setInterval(function(){
O.action(count, "add"); S.action(count, "add");
O.action(numbers, "push", count()); S.action(numbers, "push", count());
}, interval)); }, 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("code", "observedAttributes(<custom-element>)"), " — returns record of observed attributes (keys uses camelCase)",
), ),
el("li").append( 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("li").append(
el("code", "lifecyclesToEvents(<class-declaration>)"), " — convert lifecycle methods to events, can be also used as decorator", 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 { el } from "deka-dom-el";
import { simplePage } from "./layout/simplePage.html.js";
import { example } from "./components/example.html.js"; import { example } from "./components/example.html.js";
/** @param {import("./types.d.ts").PageAttrs} attrs */ /** @param {import("./types.d.ts").PageAttrs} attrs */
export function page({ pkg, info }){ export function page({ pkg, info }){
@ -23,10 +28,10 @@ export function page({ pkg, info }){
el("p").append( el("p").append(
"Next step is providing interactivity not only for our UI templates.", "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( 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 ", "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 ", el("code", "scope"), "s. We will look at how they work in components represented ",
"in JavaScript by functions." "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 { el } from "deka-dom-el";
import { simplePage } from "./layout/simplePage.html.js";
import { example } from "./components/example.html.js"; import { example } from "./components/example.html.js";
import { h3 } from "./components/pageUtils.html.js"; import { h3 } from "./components/pageUtils.html.js";
import { mnemonic } from "./components/mnemonic/elements-init.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." "This is handy to concat conditional classes."
), ),
el("li").append( 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( 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. ", "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 { el } from "deka-dom-el";
import { simplePage } from "./layout/simplePage.html.js";
import { example } from "./components/example.html.js"; import { example } from "./components/example.html.js";
import { h3 } from "./components/pageUtils.html.js"; import { h3 } from "./components/pageUtils.html.js";
import { mnemonic } from "./components/mnemonic/events-init.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 { el } from "deka-dom-el";
import { simplePage } from "./layout/simplePage.html.js";
import { example } from "./components/example.html.js"; import { example } from "./components/example.html.js";
import { h3 } from "./components/pageUtils.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"; import { code } from "./components/code.html.js";
/** @param {string} url */ /** @param {string} url */
const fileURL= url=> new URL(url, import.meta.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 }){ export function page({ pkg, info }){
const page_id= info.id; const page_id= info.id;
return el(simplePage, { info, pkg }).append( return el(simplePage, { info, pkg }).append(
el("h2", "Using observables to manage reactivity"), el("h2", "Using signals to manage reactivity"),
el("p").append( el("p").append(
"How a program responds to variable data or user", "How a program responds to variable data or user",
" interactions is one of the fundamental problems of programming.", " interactions is one of the fundamental problems of programming.",
" If we desire to solve the issue in a declarative manner,", " 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( 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", " Firstly (α), we create a variable (constant) representing reactive",
" value. Somewhere later, we can register (β) a logic reacting", " value. Somewhere later, we can register (β) a logic reacting",
" to the observable value changes. Similarly, in a remaining part (γ), we", " to the signal value changes. Similarly, in a remaining part (γ), we",
" can update the observable value." " 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( el("p").append(
"All this is just an example of ", "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" }), 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." " to the same reactive entity."
), ),
el("p").append( el("p").append(
"Observables are implemented in the library as functions. To see current value", "Signals are implemented in the library as functions. To see current value",
" of observable, just call it without any arguments ", el("code", "console.log(observable())"), ".", " of signal, just call it without any arguments ", el("code", "console.log(signal())"), ".",
" To update the observable value, pass any argument ", el("code", "observable('a new value')"), ".", " To update the signal value, pass any argument ", el("code", "signal('a new value')"), ".",
" For listenning the observable value changes, use ", el("code", "O.on(observable, console.log)"), "." " For listenning the signal value changes, use ", el("code", "S.on(signal, console.log)"), "."
), ),
el("p").append( el("p").append(
"Similarly to the ", el("code", "on"), " function to register DOM events listener.", "Similarly to the ", el("code", "on"), " function to register DOM events listener.",
" You can use ", el("code", "AbortController"), "/", el("code", "AbortSignal"), " to", " You can use ", el("code", "AbortController"), "/", el("code", "AbortSignal"), " to",
" ", el("em", "off"), "/stop listenning. In example, you also found the way for representing", " ", 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("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" }), ".", " 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." "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("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( 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" }), "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", " a store “machine”. We can then invoke (dispatch) registered action by calling",
" ", el("code", "O.action(<observable>, <name>, ...<args>)"), " after the action call", " ", el("code", "S.action(<signal>, <name>, ...<args>)"), " after the action call",
" the observable calls all its listeners. This can be stopped by calling ", el("code", "this.stopPropagation()"), " 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", " 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"), ")." " 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 change some attribute(s) of existing element(s)"),
el("li", "to generate elements itself dynamically this covers conditions and loops") 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( el("p").append(
"To derived attribute based on value of observable variable just use the observable as", "To derived attribute based on value of signal variable just use the signal as",
" a value of the attribute (", el("code", "assign(element, { attribute: O('value') })"), ").", " 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", " ", 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"), ", ", " more granularly into the DOM. Just use dedicated build-in attributes ", el("code", "dataset"), ", ",
el("code", "ariaset"), " and ", el("code", "classList"), "." el("code", "ariaset"), " and ", el("code", "classList"), "."
), ),
el("p").append( 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( 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:" " 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) 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 { el } from "deka-dom-el";
import { simplePage } from "./layout/simplePage.html.js";
import { example } from "./components/example.html.js"; import { example } from "./components/example.html.js";
import { h3 } from "./components/pageUtils.html.js"; import { h3 } from "./components/pageUtils.html.js";
import { mnemonic } from "./components/mnemonic/scopes-init.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(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( el("p").append(
"The ", el("code", "host"), " is internally used to register the cleaning procedure,", "The ", el("code", "host"), " is internally used to register the cleaning procedure,",
" when the component (", el("code", "host"), " element) is removed from the DOM." " when the component (", el("code", "host"), " element) is removed from the DOM."
), ),
el(example, { src: fileURL("./components/examples/scopes/cleaning.js"), page_id }), el(example, { src: fileURL("./components/examples/scopes/cleaning.js"), page_id }),
el("p").append( 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", " is changed. Internally, there is association between ", el("code", "textContent"), " and the paragraph",
" similar to using ", el("code", "S.on(textContent, /* update 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." " assign internally ", el("code", "on.disconnected(/* remove the listener */)(host())"), " to the host element."
), ),
el("p", { className: "notice" }).append( el("p", { className: "notice" }).append(
"The library DOM API and observables works ideally when used declaratively.", "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: "Observables", href: "http://localhost:40911/docs/p04-observables#h-introducing-observables" }), "." " 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(code, { src: fileURL("./components/examples/scopes/declarative.js"), page_id }),
el("p").append( el("p").append(
"Strictly speaking, the imperative way of using the library is not prohibited.", "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.", " and imperative manipulation of elements.",
), ),
el(code, { src: fileURL("./components/examples/scopes/imperative.js"), page_id }), 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 { el } from "deka-dom-el";
import { simplePage } from "./layout/simplePage.html.js";
import { example } from "./components/example.html.js"; import { example } from "./components/example.html.js";
import { h3 } from "./components/pageUtils.html.js"; import { h3 } from "./components/pageUtils.html.js";
import { mnemonic } from "./components/mnemonic/customElement-init.js"; import { mnemonic } from "./components/mnemonic/customElement-init.js";

View File

@ -2,14 +2,11 @@ export const path_target= {
root: "docs/", root: "docs/",
css: "docs/" css: "docs/"
}; };
export const pages= [ /**
{ id: "index", href: "./", title: "Introduction", description: "Introducing a library." }, * This variable will be filled with the list of pages during the build process (see `bs/docs.js`).
{ id: "p02-elements", href: "p02-elements", title: "Elements", description: "Basic concepts of elements modifications and creations." }, * @type {import("./types.d.ts").Info[]}
{ 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." }, export let pages= [];
{ 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" },
];
/** /**
* @typedef registerClientFile * @typedef registerClientFile
* @type {function} * @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` const className= style.host(thirdParty).css`
:host { :host {
color: green; color: green;
@ -10,22 +10,22 @@ const store_adapter= {
write(data){ console.log(data); history.replaceState("", "", "?"+(new URLSearchParams(data)).toString()); } write(data){ console.log(data); history.replaceState("", "", "?"+(new URLSearchParams(data)).toString()); }
}; };
export function thirdParty(){ export function thirdParty(){
const store= O({ const store= S({
value: O("initial") value: S("initial")
}, { }, {
set(key, value){ set(key, value){
const p= this.value[key] || O(); const p= this.value[key] || S();
p(value); p(value);
this.value[key]= p; this.value[key]= p;
} }
}); });
// Array.from((new URL(location)).searchParams.entries()) // Array.from((new URL(location)).searchParams.entries())
// .forEach(([ key, value ])=> O.action(store, "set", key, value)); // .forEach(([ key, value ])=> S.action(store, "set", key, value));
// O.on(store, data=> history.replaceState("", "", "?"+(new URLSearchParams(JSON.parse(JSON.stringify(data)))).toString())); // S.on(store, data=> history.replaceState("", "", "?"+(new URLSearchParams(JSON.parse(JSON.stringify(data)))).toString()));
useStore(store_adapter, { useStore(store_adapter, {
onread(data){ onread(data){
Array.from(data.entries()) Array.from(data.entries())
.forEach(([ key, value ])=> O.action(store, "set", key, value)); .forEach(([ key, value ])=> S.action(store, "set", key, value));
return store; return store;
} }
})(); })();
@ -33,18 +33,18 @@ export function thirdParty(){
className, className,
value: store().value(), value: store().value(),
type: "text", 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 }= {}){ function useStore(adapter_in, { onread, onbeforewrite }= {}){
const adapter= typeof adapter_in === "function" ? { read: adapter_in } : adapter_in; 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)); if(!onbeforewrite) onbeforewrite= data=> JSON.parse(JSON.stringify(data));
return function useStoreInner(data_read){ return function useStoreInner(data_read){
const observable= onread(adapter.read(data_read)); //TODO OK as synchronous const signal= onread(adapter.read(data_read)); //TODO OK as synchronous
if(adapter.write && isObservable(observable)) if(adapter.write && isSignal(signal))
O.on(observable, data=> adapter.write(onbeforewrite(data))); S.on(signal, data=> adapter.write(onbeforewrite(data)));
return observable; 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` const className= style.host(fullNameComponent).css`
:host form{ :host form{
display: flex; display: flex;
@ -7,8 +7,8 @@ const className= style.host(fullNameComponent).css`
`; `;
export function fullNameComponent(){ export function fullNameComponent(){
const labels= [ "Name", "Surname" ]; const labels= [ "Name", "Surname" ];
const name= labels.map(_=> O("")); const name= labels.map(_=> S(""));
const full_name= O(()=> const full_name= S(()=>
name.map(l=> l()).filter(Boolean).join(" ") || "-"); name.map(l=> l()).filter(Boolean).join(" ") || "-");
scope.host( scope.host(
on.connected(()=> console.log(fullNameComponent)), 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` const className= style.host(todosComponent).css`
:host{ :host{
display: flex; display: flex;
@ -17,27 +17,27 @@ const className= style.host(todosComponent).css`
/** @param {{ todos: string[] }} */ /** @param {{ todos: string[] }} */
export function todosComponent({ todos= [ "Task A" ] }= {}){ export function todosComponent({ todos= [ "Task A" ] }= {}){
let key= 0; let key= 0;
const todosO= O(new Map(), { const todosO= S(new Map(), {
add(v){ this.value.set(key++, O(v)); }, add(v){ this.value.set(key++, S(v)); },
remove(key){ O.clear(this.value.get(key)); this.value.delete(key); } 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 name= "todoName";
const onsubmitAdd= on("submit", event=> { const onsubmitAdd= on("submit", event=> {
const el= event.target.elements[name]; const el= event.target.elements[name];
event.preventDefault(); event.preventDefault();
O.action(todosO, "add", el.value); S.action(todosO, "add", el.value);
el.value= ""; el.value= "";
}); });
const onremove= on("remove", event=> const onremove= on("remove", event=>
O.action(todosO, "remove", event.detail)); S.action(todosO, "remove", event.detail));
return el("div", { className }).append( return el("div", { className }).append(
el("div").append( el("div").append(
el("h2", "Todos:"), el("h2", "Todos:"),
el("h3", "List of 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("p", "No todos yet")
: el("ul").append( : el("ul").append(
...Array.from(ts).map(([ value, textContent ])=> ...Array.from(ts).map(([ value, textContent ])=>
@ -55,7 +55,7 @@ export function todosComponent({ todos= [ "Task A" ] }= {}){
), ),
el("div").append( el("div").append(
el("h3", "Output (JSON):"), 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(); event.stopPropagation();
dispatchEvent("remove")(host(), value); dispatchEvent("remove")(host(), value);
}); });
const is_editable= O(false); const is_editable= S(false);
const onedited= on("change", ev=> { const onedited= on("change", ev=> {
textContent(ev.target.value); textContent(ev.target.value);
is_editable(false); is_editable(false);
}); });
return el("li").append( return el("li").append(
O.el(is_editable, is=> is S.el(is_editable, is=> is
? el("input", { value: textContent(), type: "text" }, onedited) ? el("input", { value: textContent(), type: "text" }, onedited)
: el("span", { textContent, onclick: is_editable.bind(null, true) }) : 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 { 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` * Compatible with `npx -y web-component-analyzer examples/components/webComponent.js`
@ -16,7 +16,7 @@ export class CustomHTMLTestElement extends HTMLElement{
} }
attributes(element){ attributes(element){
const observed= O.observedAttributes(element); const observed= S.observedAttributes(element);
return Object.assign({ test: element.test }, observed); return Object.assign({ test: element.test }, observed);
} }
render({ name, preName, test }){ render({ name, preName, test }){

View File

@ -1,10 +1,10 @@
import * as dde_dom from "../index.js"; import * as dde_dom from "../index.js";
export * from "../index.js"; export * from "../index.js";
import * as dde_s from "../observables.js"; import * as dde_s from "../signals.js";
export * from "../observables.js"; export * from "../signals.js";
Object.assign(globalThis, dde_dom, dde_s); Object.assign(globalThis, dde_dom, dde_s);
//import * as dde_dom from "../dist/esm-with-observables.js"; //import * as dde_dom from "../dist/esm-with-signals.js";
//export * from "../dist/esm-with-observables.js"; //export * from "../dist/esm-with-signals.js";
//Object.assign(globalThis, dde_dom); //Object.assign(globalThis, dde_dom);
export const style= createStyle(); 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 CustomElementTagNameMap= { '#text': Text, '#comment': Comment }
type SupportedElement= type SupportedElement=
@ -15,20 +15,20 @@ type AttrsModified= {
/** /**
* Use string like in HTML (internally uses `*.setAttribute("style", *)`), or object representation (like DOM API). * 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. * 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[]` * 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` * Sets `aria-*` simiraly to `dataset`
* */ * */
ariaset: Record<string,string|Observable<string, any>>, ariaset: Record<string,string|Signal<string, any>>,
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|Observable<string, any>> & Record<`.${string}`, any> } & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|Signal<string, any>> & Record<`.${string}`, any>
type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>; type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>;
/** /**
* Just element attributtes * 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. * There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
* @private * @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 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 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] 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 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< export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap & string, TAG extends keyof ExtendedHTMLElementTagNameMap & string,
EL extends (TAG extends keyof ExtendedHTMLElementTagNameMap ? ExtendedHTMLElementTagNameMap[TAG] : HTMLElement) 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 ), EL extends ( TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement ),
>( >(
tag_name: TAG, 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>[] ...addons: ddeElementAddon<EL>[]
)=> ddeMathMLElement )=> ddeMathMLElement
export function elNS( 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", "name": "deka-dom-el",
"version": "0.7.7", "version": "0.8.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "deka-dom-el", "name": "deka-dom-el",
"version": "0.7.7", "version": "0.8.0",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@size-limit/preset-small-lib": "^11.0.1", "@size-limit/preset-small-lib": "^11.0.1",

View File

@ -1,6 +1,6 @@
{ {
"name": "deka-dom-el", "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.", "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>", "author": "Jan Andrle <andrle.jan@centrum.cz>",
"license": "MIT", "license": "MIT",
@ -20,17 +20,17 @@
"import": "./index.js", "import": "./index.js",
"types": "./index.d.ts" "types": "./index.d.ts"
}, },
"./observables": { "./signals": {
"import": "./observables.js", "import": "./signals.js",
"types": "./observables.d.ts" "types": "./signals.d.ts"
}, },
"./jsdom": { "./jsdom": {
"import": "./jsdom.js", "import": "./jsdom.js",
"types": "./jsdom.d.ts" "types": "./jsdom.d.ts"
}, },
"./src/observables-lib": { "./src/signals-lib": {
"import": "./src/observables-lib.js", "import": "./src/signals-lib.js",
"types": "./src/observables-lib.d.ts" "types": "./src/signals-lib.d.ts"
} }
}, },
"files": [ "files": [
@ -65,20 +65,20 @@
}, },
{ {
"path": "./observables.js", "path": "./signals.js",
"limit": "12 kB", "limit": "12 kB",
"gzip": false, "gzip": false,
"brotli": false "brotli": false
}, },
{ {
"path": "./index-with-observables.js", "path": "./index-with-signals.js",
"limit": "15 kB", "limit": "15 kB",
"gzip": false, "gzip": false,
"brotli": false "brotli": false
}, },
{ {
"path": "./index-with-observables.js", "path": "./index-with-signals.js",
"limit": "5.25 kB" "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'; import { enviroment as env } from './dom-common.js';
/** @type {{ scope: object, prevent: boolean, host: function }[]} */ /** @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; let namespace;
export function createElement(tag, attributes, ...addons){ export function createElement(tag, attributes, ...addons){
/* jshint maxcomplexity: 15 */ /* jshint maxcomplexity: 15 */
const s= observables(this); const s= signals(this);
let scoped= 0; let scoped= 0;
let el, el_host; let el, el_host;
//TODO Array.isArray(tag) ⇒ set key (cache els) //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 }; attributes= { textContent: attributes };
switch(true){ switch(true){
case typeof tag==="function": { case typeof tag==="function": {
@ -177,11 +177,11 @@ function assignContext(element, _this){
if(assign_context.has(element)) return assign_context.get(element); if(assign_context.has(element)) return assign_context.get(element);
const is_svg= element instanceof env.S; const is_svg= element instanceof env.S;
const setRemoveAttr= (is_svg ? setRemoveNS : setRemove).bind(null, element, "Attribute"); const setRemoveAttr= (is_svg ? setRemoveNS : setRemove).bind(null, element, "Attribute");
const s= observables(_this); const s= signals(_this);
return { setRemoveAttr, s }; return { setRemoveAttr, s };
} }
export function classListDeclarative(element, toggle){ export function classListDeclarative(element, toggle){
const s= observables(this); const s= signals(this);
forEachEntries(s, toggle, forEachEntries(s, toggle,
(class_name, val)=> (class_name, val)=>
element.classList.toggle(class_name, val===-1 ? undefined : Boolean(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'; import { enviroment as env, keyLTE, evc, evd, eva } from './dom-common.js';
export function dispatchEvent(name, options, host){ 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"; import { hasOwn } from "./helpers.js";
export function isObservable(candidate){ export function isSignal(candidate){
try{ return hasOwn(candidate, mark); } try{ return hasOwn(candidate, mark); }
catch(e){ return false; } catch(e){ return false; }
} }
/** @type {function[]} */ /** @type {function[]} */
const stack_watch= []; const stack_watch= [];
/** /**
* ### `WeakMap<function, Set<ddeObservable<any, any>>>` * ### `WeakMap<function, Set<ddeSignal<any, any>>>`
* The `Set` is in the form of `[ source, ...depended observables (DSs) ]`. * The `Set` is in the form of `[ source, ...depended signals (DSs) ]`.
* When the DS is cleaned (`O.clear`) it is removed from DSs, * When the DS is cleaned (`S.clear`) it is removed from DSs,
* if remains only one (`source`) it is cleared too. * if remains only one (`source`) it is cleared too.
* ### `WeakMap<object, function>` * ### `WeakMap<object, function>`
* This is used for revesed deps, the `function` is also key for `deps`. * 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(); const deps= new WeakMap();
export function observable(value, actions){ export function signal(value, actions){
if(typeof value!=="function") if(typeof value!=="function")
return create(false, value, actions); return create(false, value, actions);
if(isObservable(value)) return value; if(isSignal(value)) return value;
const out= create(true); const out= create(true);
const contextReWatch= function(){ const contextReWatch= function(){
@ -33,9 +33,9 @@ export function observable(value, actions){
if(!deps_old.length) return; if(!deps_old.length) return;
const deps_curr= deps.get(contextReWatch); const deps_curr= deps.get(contextReWatch);
for (const dep_observable of deps_old){ for (const dep_signal of deps_old){
if(deps_curr.has(dep_observable)) continue; if(deps_curr.has(dep_signal)) continue;
removeObservableListener(dep_observable, contextReWatch); removeSignalListener(dep_signal, contextReWatch);
} }
}; };
deps.set(out[mark], contextReWatch); deps.set(out[mark], contextReWatch);
@ -43,46 +43,46 @@ export function observable(value, actions){
contextReWatch(); contextReWatch();
return out; return out;
} }
export { observable as O }; export { signal as S };
observable.action= function(o, name, ...a){ signal.action= function(s, name, ...a){
const s= o[mark], { actions }= s; const M= s[mark], { actions }= M;
if(!actions || !(name in actions)) if(!actions || !(name in actions))
throw new Error(`'${o}' has no action with name '${name}'!`); throw new Error(`'${s}' has no action with name '${name}'!`);
actions[name].apply(s, a); actions[name].apply(M, a);
if(s.skip) return (delete s.skip); if(M.skip) return (delete M.skip);
s.listeners.forEach(l=> l(s.value)); 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; const { signal: as }= options;
if(as && as.aborted) return; if(as && as.aborted) return;
if(Array.isArray(o)) return o.forEach(s=> on(s, listener, options)); if(Array.isArray(s)) return s.forEach(s=> on(s, listener, options));
addObservableListener(o, listener); addSignalListener(s, listener);
if(as) as.addEventListener("abort", ()=> removeObservableListener(o, listener)); if(as) as.addEventListener("abort", ()=> removeSignalListener(s, listener));
//TODO cleanup when observable removed //TODO cleanup when signal removed
}; };
observable.symbols= { signal.symbols= {
//observable: mark, //signal: mark,
onclear: Symbol.for("Observable.onclear") onclear: Symbol.for("Signal.onclear")
}; };
observable.clear= function(...observables){ signal.clear= function(...signals){
for(const o of observables){ for(const s of signals){
const s= o[mark]; const M= s[mark];
if(!s) continue; if(!M) continue;
delete o.toJSON; delete s.toJSON;
s.onclear.forEach(f=> f.call(s)); M.onclear.forEach(f=> f.call(M));
clearListDeps(o, s); clearListDeps(s, M);
delete o[mark]; delete s[mark];
} }
function clearListDeps(o, s){ function clearListDeps(s, o){
s.listeners.forEach(l=> { o.listeners.forEach(l=> {
s.listeners.delete(l); o.listeners.delete(l);
if(!deps.has(l)) return; if(!deps.has(l)) return;
const ls= deps.get(l); const ls= deps.get(l);
ls.delete(o); ls.delete(s);
if(ls.size>1) return; if(ls.size>1) return;
o.clear(...ls); s.clear(...ls);
deps.delete(l); deps.delete(l);
}); });
} }
@ -92,7 +92,7 @@ import { enviroment as env } from "./dom-common.js";
import { el } from "./dom.js"; import { el } from "./dom.js";
import { scope } from "./dom.js"; import { scope } from "./dom.js";
// TODO: third argument for handle `cache_tmp` in re-render // 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_start= el.mark({ type: "reactive" }, true);
const mark_end= mark_start.end; const mark_end= mark_start.end;
const out= env.D.createDocumentFragment(); const out= env.D.createDocumentFragment();
@ -101,7 +101,7 @@ observable.el= function(o, map){
let cache= {}; let cache= {};
const reRenderReactiveElement= v=> { const reRenderReactiveElement= v=> {
if(!mark_start.parentNode || !mark_end.parentNode) // === `isConnected` or wasnt yet rendered 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 const cache_tmp= cache; // will be reused in the useCache or removed in the while loop on the end
cache= {}; cache= {};
scope.push(current); scope.push(current);
@ -128,16 +128,16 @@ observable.el= function(o, map){
if(mark_start.isConnected) if(mark_start.isConnected)
requestCleanUpReactives(current.host()); requestCleanUpReactives(current.host());
}; };
addObservableListener(o, reRenderReactiveElement); addSignalListener(s, reRenderReactiveElement);
removeObservablesFromElements(o, reRenderReactiveElement, mark_start, map); removeSignalsFromElements(s, reRenderReactiveElement, mark_start, map);
reRenderReactiveElement(o()); reRenderReactiveElement(s());
return out; return out;
}; };
function requestCleanUpReactives(host){ function requestCleanUpReactives(host){
if(!host || !host[key_reactive]) return; if(!host || !host[key_reactive]) return;
(requestIdleCallback || setTimeout)(function(){ (requestIdleCallback || setTimeout)(function(){
host[key_reactive]= host[key_reactive] 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"; import { on } from "./events.js";
@ -147,49 +147,49 @@ const observedAttributeActions= {
}; };
function observedAttribute(store){ function observedAttribute(store){
return function(instance, name){ return function(instance, name){
const varO= (...args)=> !args.length const varS= (...args)=> !args.length
? read(varO) ? read(varS)
: instance.setAttribute(name, ...args); : instance.setAttribute(name, ...args);
const out= toObservable(varO, instance.getAttribute(name), observedAttributeActions); const out= toSignal(varS, instance.getAttribute(name), observedAttributeActions);
store[name]= out; store[name]= out;
return out; return out;
}; };
} }
const key_attributes= "__dde_attributes"; const key_attributes= "__dde_attributes";
observable.observedAttributes= function(element){ signal.observedAttributes= function(element){
const store= element[key_attributes]= {}; const store= element[key_attributes]= {};
const attrs= observedAttributes(element, observedAttribute(store)); const attrs= observedAttributes(element, observedAttribute(store));
on.attributeChanged(function attributeChangeToObservable({ detail }){ on.attributeChanged(function attributeChangeToSignal({ detail }){
/*! This maps attributes to observables (`O.observedAttributes`). /*! This maps attributes to signals (`S.observedAttributes`).
* Investigate `__dde_attributes` key of the element.*/ * Investigate `__dde_attributes` key of the element.*/
const [ name, value ]= detail; const [ name, value ]= detail;
const curr= this[key_attributes][name]; const curr= this[key_attributes][name];
if(curr) return observable.action(curr, "_set", value); if(curr) return signal.action(curr, "_set", value);
})(element); })(element);
on.disconnected(function(){ 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.*/ * Investigate `__dde_attributes` key of the element.*/
observable.clear(...Object.values(this[key_attributes])); signal.clear(...Object.values(this[key_attributes]));
})(element); })(element);
return attrs; return attrs;
}; };
import { typeOf } from './helpers.js'; import { typeOf } from './helpers.js';
export const observables_config= { export const signals_config= {
isObservable, isSignal,
processReactiveAttribute(element, key, attrs, set){ processReactiveAttribute(element, key, attrs, set){
if(!isObservable(attrs)) return attrs; if(!isSignal(attrs)) return attrs;
const l= attr=> { const l= attr=> {
if(!element.isConnected) if(!element.isConnected)
return removeObservableListener(attrs, l); return removeSignalListener(attrs, l);
set(key, attr); set(key, attr);
}; };
addObservableListener(attrs, l); addSignalListener(attrs, l);
removeObservablesFromElements(attrs, l, element, key); removeSignalsFromElements(attrs, l, element, key);
return attrs(); return attrs();
} }
}; };
function removeObservablesFromElements(o, listener, ...notes){ function removeSignalsFromElements(s, listener, ...notes){
const { current }= scope; const { current }= scope;
if(current.prevent) return; if(current.prevent) return;
current.host(function(element){ current.host(function(element){
@ -197,29 +197,29 @@ function removeObservablesFromElements(o, listener, ...notes){
element[key_reactive]= []; element[key_reactive]= [];
on.disconnected(()=> 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. * You can investigate the `__dde_reactive` key of the element.
* */ * */
element[key_reactive].forEach(([ [ o, listener ] ])=> element[key_reactive].forEach(([ [ s, listener ] ])=>
removeObservableListener(o, listener, o[mark] && o[mark].host && o[mark].host() === element)) removeSignalListener(s, listener, s[mark] && s[mark].host && s[mark].host() === element))
)(element); )(element);
} }
element[key_reactive].push([ [ o, listener ], ...notes ]); element[key_reactive].push([ [ s, listener ], ...notes ]);
}); });
} }
function create(is_readonly, value, actions){ function create(is_readonly, value, actions){
const varO= is_readonly const varS= is_readonly
? ()=> read(varO) ? ()=> read(varS)
: (...value)=> value.length ? write(varO, ...value) : read(varO); : (...value)=> value.length ? write(varS, ...value) : read(varS);
return toObservable(varO, value, actions, is_readonly); return toSignal(varS, value, actions, is_readonly);
} }
const protoSigal= Object.assign(Object.create(null), { const protoSigal= Object.assign(Object.create(null), {
stopPropagation(){ stopPropagation(){
this.skip= true; this.skip= true;
} }
}); });
class ObservableDefined extends Error{ class SignalDefined extends Error{
constructor(){ constructor(){
super(); super();
const [ curr, ...rest ]= this.stack.split("\n"); const [ curr, ...rest ]= this.stack.split("\n");
@ -227,66 +227,66 @@ class ObservableDefined extends Error{
this.stack= rest.find(l=> !l.includes(curr_file)); 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= []; const onclear= [];
if(typeOf(actions)!=="[object Object]") if(typeOf(actions)!=="[object Object]")
actions= {}; actions= {};
const { onclear: ocs }= observable.symbols; const { onclear: ocs }= signal.symbols;
if(actions[ocs]){ if(actions[ocs]){
onclear.push(actions[ocs]); onclear.push(actions[ocs]);
delete actions[ocs]; delete actions[ocs];
} }
const { host }= scope; const { host }= scope;
Reflect.defineProperty(o, mark, { Reflect.defineProperty(s, mark, {
value: { value: {
value, actions, onclear, host, value, actions, onclear, host,
listeners: new Set(), listeners: new Set(),
defined: (new ObservableDefined()).stack, defined: (new SignalDefined()).stack,
readonly readonly
}, },
enumerable: false, enumerable: false,
writable: false, writable: false,
configurable: true configurable: true
}); });
o.toJSON= ()=> o(); s.toJSON= ()=> s();
o.valueOf= ()=> o[mark] && o[mark].value; s.valueOf= ()=> s[mark] && s[mark].value;
Object.setPrototypeOf(o[mark], protoSigal); Object.setPrototypeOf(s[mark], protoSigal);
return o; return s;
} }
function currentContext(){ function currentContext(){
return stack_watch[stack_watch.length - 1]; return stack_watch[stack_watch.length - 1];
} }
function read(o){ function read(s){
if(!o[mark]) return; if(!s[mark]) return;
const { value, listeners }= o[mark]; const { value, listeners }= s[mark];
const context= currentContext(); const context= currentContext();
if(context) listeners.add(context); 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; return value;
} }
function write(o, value, force){ function write(s, value, force){
if(!o[mark]) return; if(!s[mark]) return;
const s= o[mark]; const M= s[mark];
if(!force && s.value===value) return; if(!force && M.value===value) return;
s.value= value; M.value= value;
s.listeners.forEach(l=> l(value)); M.listeners.forEach(l=> l(value));
return value; return value;
} }
function addObservableListener(o, listener){ function addSignalListener(s, listener){
if(!o[mark]) return; if(!s[mark]) return;
return o[mark].listeners.add(listener); return s[mark].listeners.add(listener);
} }
function removeObservableListener(o, listener, clear_when_empty){ function removeSignalListener(s, listener, clear_when_empty){
const s= o[mark]; const M= s[mark];
if(!s) return; if(!M) return;
const out= s.listeners.delete(listener); const out= M.listeners.delete(listener);
if(clear_when_empty && !s.listeners.size){ if(clear_when_empty && !M.listeners.size){
observable.clear(o); signal.clear(s);
if(!deps.has(s)) return out; if(!deps.has(M)) return out;
const c= deps.get(s); const c= deps.get(M);
if(!deps.has(c)) return out; 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; 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;
}