1
0
mirror of https://github.com/jaandrle/deka-dom-el synced 2025-01-18 07:23:15 +01:00

Refact docs and examples (linting) (#22)

This commit is contained in:
Jan Andrle 2024-12-13 15:04:52 +01:00 committed by GitHub
parent b50f8449aa
commit eef4e8dfa6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
58 changed files with 1851 additions and 1593 deletions

26
.editorconfig Normal file
View File

@ -0,0 +1,26 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = tab
trim_trailing_whitespace = true
max_line_length = 120
[*.json]
max_line_length = unset
[*.yml]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false
[LICENSE]
max_line_length = unset
[dist/**]
indent_style = unset
max_line_length = unset
trim_trailing_whitespace = unset

23
.github/workflows/pr.yml vendored Normal file
View File

@ -0,0 +1,23 @@
# https://nektosact.com/usage/index.html
# https://github.com/reviewdog/action-eclint
name: On PR
on:
workflow_dispatch:
pull_request:
branches: [main]
jobs:
pr:
name: Validates formatting and linting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: reviewdog/action-eclint@d51e853275e707b64c0526881ada324f454c1110 # v1.7.1
with:
reporter: github-pr-check
eclint_flags: '--fix'
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: 20.16
- run: npm ci
- run: bs/lint.sh

View File

@ -1,52 +1,64 @@
**WIP** (the experimentation phase) | [source code on GitHub](https://github.com/jaandrle/deka-dom-el) | [*mirrored* on Gitea](https://gitea.jaandrle.cz/jaandrle/deka-dom-el) **WIP** (the experimentation phase)
| [source code on GitHub](https://github.com/jaandrle/deka-dom-el)
| [*mirrored* on Gitea](https://gitea.jaandrle.cz/jaandrle/deka-dom-el)
***Vanilla for flavouring — a full-fledged feast for large projects*** ***Vanilla for flavouring — a full-fledged feast for large projects***
*…use simple DOM API by default and library tools and logic when you need them* *…use simple DOM API by default and library tools and logic when you need them*
```js ```javascript
document.body.append( document.body.append(
el("h1", "Hello World 👋"), el(HelloWorldComponent, { initial: "🚀" })
el("p", "See some syntax examples here:"),
el("ul").append(
el("li").append(
el("a", { textContent: "Link to the library repo", title: "Deka DOM El — GitHub", href: "https://github.com/jaandrle/deka-dom-el" })
),
el("li").append(
"Use extended Vanilla JavaScript DOM/IDL API: ",
el("span", { textContent: "» this is a span with class=cN and data-a=A, data-b=B «", className: "cN", dataset: { a: "A", b: "B" } })
),
el("li").append(
el(component, { textContent: "A component", className: "example" }, on("change", console.log))
)
)
); );
function component({ textContent, className }){ /** @typedef {"🎉" | "🚀"} Emoji */
const value= S("onchange"); /** @param {{ initial: Emoji }} attrs */
function HelloWorldComponent({ initial }){
const clicks= S(0);
const emoji= S(initial);
/** @param {HTMLOptionElement} el */
const isSelected= el=> (el.selected= el.value===initial);
// @ts-expect-error 2339: The <select> has only two options with {@link Emoji}
const onChange= on("change", event=> emoji(event.target.value));
return el().append( return el().append(
el("p", { textContent, className }), el("p", {
el("p", { className: [ className, "second-line" ] }).append( textContent: S(() => `Hello World ${emoji().repeat(clicks())}`),
"…with reactivity: ", el("em", { style: { fontWeight: "bold" }, ariaset: { live: "polite" }, textContent: value }), className: "example",
ariaLive: "polite", //OR ariaset: { live: "polite" },
dataset: { example: "Example" }, //OR dataExample: "Example",
}),
el("button",
{ textContent: "Fire", type: "button" },
on("click", ()=> clicks(clicks() + 1)),
on("keyup", ()=> clicks(clicks() - 2)),
), ),
el("input", { type: "text", value: value() }, on("change", event=> value(event.target.value))) el("select", null, onChange).append(
el(OptionComponent, "🎉", isSelected),//OR { textContent: "🎉" }
el(OptionComponent, "🚀", isSelected),//OR { textContent: "🚀" }
)
); );
} }
function OptionComponent({ textContent }){
return el("option", { value: textContent, textContent })
}
``` ```
# 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**](#signals). Creating reactive elements, components and Web components using [IDL](https://developer.mozilla.org/en-US/docs/
Glossary/IDL)/JavaScript DOM API and [**signals/observables**](#signals).
## Inspiration and suggested alternatives ## 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
- [vanjs-org/van: 🍦 VanJS: World's smallest reactive UI framework. Incredibly Powerful, Insanely Small - Everyone can build a useful UI app in an hour.](https://github.com/vanjs-org/van) JSX and virtual DOM.](https://github.com/jaandrle/dollar_dom_component)
- [vanjs-org/van: 🍦 VanJS: World's smallest reactive UI framework. Incredibly Powerful, Insanely Small -
Everyone can build a useful UI app in an hour.](https://github.com/vanjs-org/van)
- [hyperhype/hyperscript: Create HyperText with JavaScript.](https://github.com/hyperhype/hyperscript) - [hyperhype/hyperscript: Create HyperText with JavaScript.](https://github.com/hyperhype/hyperscript)
- [adamhaile/S: S.js - Simple, Clean, Fast Reactive Programming in Javascript](https://github.com/adamhaile/S) ([adamhaile/surplus: High performance JSX web views for S.js applications](https://github.com/adamhaile/surplus)) - [adamhaile/S: S.js - Simple, Clean, Fast Reactive Programming in Javascript](https://github.com/adamhaile/S)
([adamhaile/surplus: High performance JSX web views for S.js applications](https://github.com/adamhaile/surplus))
- [potch/signals: a small reactive signals library](https://github.com/potch/signals) - [potch/signals: a small reactive signals library](https://github.com/potch/signals)
## Why an another one? ## Why an another one?
This library falls somewhere between van/hyperscript and [solid-js](https://github.com/solidjs/solid) in terms of size, complexity, This library falls somewhere between van/hyperscript and [solid-js](https://github.com/solidjs/solid) in terms of size,
and usability. complexity, and usability.
Another goal is to proceed in the best spirit of functional programming. This involves starting with Another goal is to proceed in the best spirit of functional programming. This involves starting with
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
@ -74,7 +86,10 @@ To balance these requirements, numerous compromises have been made. To summarize
- [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
- [Signals — whats going on behind the scenes | by Ryan Hoffnan | ITNEXT](https://itnext.io/signals-whats-going-on-behind-the-scenes-ec858589ea63) - [Signals — whats going on behind the scenes | by Ryan Hoffnan | ITNEXT](https://itnext.io/
- [The Evolution of Signals in JavaScript - DEV Community](https://dev.to/this-is-learning/the-evolution-of-signals-in-javascript-8ob) signals-whats-going-on-behind-the-scenes-ec858589ea63)
- there is also [tc39/proposal-signals: A proposal to add signals to JavaScript.](https://github.com/tc39/proposal-signals) - [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) - [Observer pattern - Wikipedia](https://en.wikipedia.org/wiki/Observer_pattern)

View File

@ -1 +1,16 @@
[jaandrle/bs: The simplest possible build system using executables](https://github.com/jaandrle/bs/) ## bs: Build system based on executables
This project uses [jaandrle/bs: The simplest possible build system using executable/bash scripts](
https://github.com/jaandrle/bs).
#### bs/build.js [--minify|--help]
Generates alternative versions of the project (other than native ESM code).
Also generates typescript definitions.
#### bs/docs.js
Generates documentation, from `docs/`. Uses “SSR” technique, using deka-dom-el itself.
#### bs/lint.sh
Lints size of the project, jshint. See configs:
- `package.json`: key `size-limit`
- `package.json`: key `jshintConfig`

View File

@ -46,7 +46,10 @@ $.api("", true)
let content= s.cat(file).toString().split(/export ?{/); let content= s.cat(file).toString().split(/export ?{/);
content.splice(1, 0, `\nglobalThis.${name}= {`); content.splice(1, 0, `\nglobalThis.${name}= {`);
content[2]= content[2].replace(/,(?!\n)/g, ",\n").replace(/(?<!\n)}/, "\n}").replace(/^(\t*)(.*) as ([^,\n]*)(,?)$/mg, "$1$3: $2$4"); content[2]= content[2]
.replace(/,(?!\n)/g, ",\n")
.replace(/(?<!\n)}/, "\n}")
.replace(/^(\t*)(.*) as ([^,\n]*)(,?)$/mg, "$1$3: $2$4");
s.echo([ s.echo([
`//deka-dom-el library is available via global namespace \`${name}\``, `//deka-dom-el library is available via global namespace \`${name}\``,
"(()=> {", "(()=> {",

View File

@ -1,5 +1,5 @@
#!/usr/bin/env -S npx nodejsscript #!/usr/bin/env -S npx nodejsscript
/* 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 */// editorconfig-checker-disable-line
echo("Building static documentation files…"); echo("Building static documentation files…");
echo("Preparing…"); echo("Preparing…");
import { path_target, pages as pages_registered, styles, dispatchEvent, t } from "../docs/ssr.js"; import { path_target, pages as pages_registered, styles, dispatchEvent, t } from "../docs/ssr.js";

View File

@ -1,3 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -eou pipefail
npx editorconfig-checker -format gcc
npx size-limit npx size-limit
npx jshint index.js src npx jshint index.js src

File diff suppressed because it is too large Load Diff

457
dist/dde.js vendored
View File

@ -1,7 +1,7 @@
//deka-dom-el library is available via global namespace `dde` //deka-dom-el library is available via global namespace `dde`
(()=> { (()=> {
// src/signals-common.js // src/signals-common.js
var C = { var A = {
isSignal(t) { isSignal(t) {
return !1; return !1;
}, },
@ -9,19 +9,18 @@ var C = {
return n; return n;
} }
}; };
function V(t, e = !0) { function Z(t, e = !0) {
return e ? Object.assign(C, t) : (Object.setPrototypeOf(t, C), t); return e ? Object.assign(A, t) : (Object.setPrototypeOf(t, A), t);
} }
function L(t) { function S(t) {
return C.isPrototypeOf(t) && t !== C ? t : C; return A.isPrototypeOf(t) && t !== A ? t : A;
} }
// src/helpers.js // src/helpers.js
var q = (...t) => Object.prototype.hasOwnProperty.call(...t); function m(t) {
function E(t) {
return typeof t > "u"; return typeof t > "u";
} }
function N(t, e) { function L(t, e) {
if (!t || !(t instanceof AbortSignal)) if (!t || !(t instanceof AbortSignal))
return !0; return !0;
if (!t.aborted) if (!t.aborted)
@ -29,46 +28,50 @@ function N(t, e) {
t.removeEventListener("abort", e); t.removeEventListener("abort", e);
}; };
} }
function F(t, e) { function W(t, e) {
let { observedAttributes: n = [] } = t.constructor; let { observedAttributes: n = [] } = t.constructor;
return n.reduce(function(r, o) { return n.reduce(function(r, o) {
return r[J(o)] = e(t, o), r; return r[G(o)] = e(t, o), r;
}, {}); }, {});
} }
function J(t) { function G(t) {
return t.replace(/-./g, (e) => e[1].toUpperCase()); return t.replace(/-./g, (e) => e[1].toUpperCase());
} }
// src/dom-common.js // src/dom-common.js
var a = { var f = {
setDeleteAttr: K, setDeleteAttr: V,
ssr: "", ssr: "",
D: globalThis.document, D: globalThis.document,
F: globalThis.DocumentFragment, F: globalThis.DocumentFragment,
H: globalThis.HTMLElement, H: globalThis.HTMLElement,
S: globalThis.SVGElement, S: globalThis.SVGElement,
M: globalThis.MutationObserver M: globalThis.MutationObserver,
q: (t) => t || Promise.resolve()
}; };
function K(t, e, n) { function V(t, e, n) {
if (Reflect.set(t, e, n), !!E(n)) { if (Reflect.set(t, e, n), !!m(n)) {
if (Reflect.deleteProperty(t, e), t instanceof a.H && t.getAttribute(e) === "undefined") if (Reflect.deleteProperty(t, e), t instanceof f.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 x = "__dde_lifecyclesToEvents", g = "dde:connected", y = "dde:disconnected", D = "dde:attributeChanged"; var x = "__dde_lifecyclesToEvents", v = "dde:connected", w = "dde:disconnected", C = "dde:attributeChanged";
// src/dom.js // src/dom.js
var v = [{ function dt(t) {
return f.q(t);
}
var g = [{
get scope() { get scope() {
return a.D.body; return f.D.body;
}, },
host: (t) => t ? t(a.D.body) : a.D.body, host: (t) => t ? t(f.D.body) : f.D.body,
prevent: !0 prevent: !0
}], S = { }], O = {
get current() { get current() {
return v[v.length - 1]; return g[g.length - 1];
}, },
get host() { get host() {
return this.current.host; return this.current.host;
@ -78,166 +81,158 @@ var v = [{
return t.prevent = !0, t; return t.prevent = !0, t;
}, },
get state() { get state() {
return [...v]; return [...g];
}, },
push(t = {}) { push(t = {}) {
return v.push(Object.assign({}, this.current, { prevent: !1 }, t)); return g.push(Object.assign({}, this.current, { prevent: !1 }, t));
}, },
pushRoot() { pushRoot() {
return v.push(v[0]); return g.push(g[0]);
}, },
pop() { pop() {
if (v.length !== 1) if (g.length !== 1)
return v.pop(); return g.pop();
} }
}; };
function $(...t) { function q(...t) {
return this.appendOriginal(...t), this; return this.appendOriginal(...t), this;
} }
function Q(t) { function J(t) {
return t.append === $ || (t.appendOriginal = t.append, t.append = $), t; return t.append === q || (t.appendOriginal = t.append, t.append = q), t;
} }
var T; var T;
function j(t, e, ...n) { function P(t, e, ...n) {
let r = L(this), o = 0, c, f; let r = S(this), o = 0, c, a;
switch ((Object(e) !== e || r.isSignal(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, S.push({ scope: t, host: (...b) => b.length ? (o === 1 ? n.unshift(...b) : b.forEach((h) => h(f)), void 0) : f }), c = t(e || void 0); o = 1;
let d = c instanceof a.F; let d = (...l) => l.length ? (o === 1 ? n.unshift(...l) : l.forEach((E) => E(a)), void 0) : a;
O.push({ scope: t, host: d }), c = t(e || void 0);
let p = c instanceof f.F;
if (c.nodeName === "#comment") break; if (c.nodeName === "#comment") break;
let p = j.mark({ let b = P.mark({
type: "component", type: "component",
name: t.name, name: t.name,
host: d ? "this" : "parentElement" host: p ? "this" : "parentElement"
}); });
c.prepend(p), d && (f = p); c.prepend(b), p && (a = b);
break; break;
} }
case t === "#text": case t === "#text":
c = O.call(this, a.D.createTextNode(""), e); c = R.call(this, f.D.createTextNode(""), e);
break; break;
case (t === "<>" || !t): case (t === "<>" || !t):
c = O.call(this, a.D.createDocumentFragment(), e); c = R.call(this, f.D.createDocumentFragment(), e);
break; break;
case !!T: case !!T:
c = O.call(this, a.D.createElementNS(T, t), e); c = R.call(this, f.D.createElementNS(T, t), e);
break; break;
case !c: case !c:
c = O.call(this, a.D.createElement(t), e); c = R.call(this, f.D.createElement(t), e);
} }
return Q(c), f || (f = c), n.forEach((d) => d(f)), o && S.pop(), o = 2, c; return J(c), a || (a = c), n.forEach((d) => d(a)), o && O.pop(), o = 2, c;
} }
function bt(t, e, n) { P.mark = function(t, e = !1) {
typeof e != "object" && (n = e, e = t);
let r = Symbol.for("default"), o = Array.from(e.querySelectorAll("slot")).reduce((f, d) => Reflect.set(f, d.name || r, d) && f, {}), c = q(o, r);
if (t.append = new Proxy(t.append, {
apply(f, d, p) {
if (p[0] === e) return f.apply(t, p);
if (!p.length) return t;
let b = a.D.createDocumentFragment();
for (let h of p) {
if (!h || !h.slot) {
c && b.append(h);
continue;
}
let A = h.slot, _ = o[A];
tt(h, "remove", "slot"), _ && (X(_, h, n), Reflect.deleteProperty(o, A));
}
return c && (o[r].replaceWith(b), Reflect.deleteProperty(o, r)), t.append = f, t;
}
}), t !== e) {
let f = Array.from(t.childNodes);
f.forEach((d) => d.remove()), t.append(...f);
}
return e;
}
function X(t, e, n) {
n && n(t, e);
try {
t.replaceWith(O(e, { className: [e.className, t.className], dataset: { ...t.dataset } }));
} catch {
t.replaceWith(e);
}
}
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 = a.D.createComment(`<dde:mark ${t}${a.ssr}${n}>`); let n = e ? "" : "/", r = f.D.createComment(`<dde:mark ${t}${f.ssr}${n}>`);
return e && (r.end = a.D.createComment("</dde:mark>")), r; return e && (r.end = f.D.createComment("</dde:mark>")), r;
}; };
function gt(t) { function pt(t) {
let e = this; let e = this;
return function(...r) { return function(...r) {
T = t; T = t;
let o = j.call(e, ...r); let o = P.call(e, ...r);
return T = void 0, o; return T = void 0, o;
}; };
} }
var P = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: U } = a; function lt(t, e = t) {
function O(t, ...e) { let n = "\xB9\u2070", r = "\u2713", o = Object.fromEntries(
if (!e.length) return t; Array.from(e.querySelectorAll("slot")).filter((c) => !c.name.endsWith(n)).map((c) => [c.name += n, c])
P.set(t, B(t, this)); );
for (let [n, r] of Object.entries(Object.assign({}, ...e))) if (t.append = new Proxy(t.append, {
z.call(this, t, n, r); apply(c, a, d) {
return P.delete(t), t; if (d[0] === e) return c.apply(t, d);
for (let p of d) {
let b = (p.slot || "") + n;
try {
Q(p, "remove", "slot");
} catch {
} }
function z(t, e, n) { let l = o[b];
let { setRemoveAttr: r, s: o } = B(t, this), c = this; if (!l) return;
l.name.startsWith(r) || (l.childNodes.forEach((E) => E.remove()), l.name = r + b), l.append(p);
}
return t.append = c, t;
}
}), t !== e) {
let c = Array.from(t.childNodes);
t.append(...c);
}
return e;
}
var N = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: $ } = f;
function R(t, ...e) {
if (!e.length) return t;
N.set(t, H(t, this));
for (let [n, r] of Object.entries(Object.assign({}, ...e)))
U.call(this, t, n, r);
return N.delete(t), t;
}
function U(t, e, n) {
let { setRemoveAttr: r, s: o } = H(t, this), c = this;
n = o.processReactiveAttribute( n = o.processReactiveAttribute(
t, t,
e, e,
n, n,
(d, p) => z.call(c, t, d, p) (d, p) => U.call(c, t, d, p)
); );
let [f] = e; let [a] = e;
if (f === "=") return r(e.slice(1), n); if (a === "=") return r(e.slice(1), n);
if (f === ".") return H(t, e.slice(1), n); if (a === ".") return F(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);
switch (e === "className" && (e = "class"), e) { switch (e === "className" && (e = "class"), e) {
case "xlink:href": case "xlink:href":
return r(e, n, "http://www.w3.org/1999/xlink"); return r(e, n, "http://www.w3.org/1999/xlink");
case "textContent": case "textContent":
return U(t, e, n); return $(t, e, n);
case "style": case "style":
if (typeof n != "object") break; if (typeof n != "object") break;
/* falls through */ /* falls through */
case "dataset": case "dataset":
return M(o, n, H.bind(null, t[e])); return M(o, n, F.bind(null, t[e]));
case "ariaset": case "ariaset":
return M(o, n, (d, p) => r("aria-" + d, p)); return M(o, n, (d, p) => r("aria-" + d, p));
case "classList": case "classList":
return Y.call(c, t, n); return K.call(c, t, n);
} }
return et(t, e) ? U(t, e, n) : r(e, n); return X(t, e) ? $(t, e, n) : r(e, n);
} }
function B(t, e) { function H(t, e) {
if (P.has(t)) return P.get(t); if (N.has(t)) return N.get(t);
let r = (t instanceof a.S ? rt : nt).bind(null, t, "Attribute"), o = L(e); let r = (t instanceof f.S ? tt : Y).bind(null, t, "Attribute"), o = S(e);
return { setRemoveAttr: r, s: o }; return { setRemoveAttr: r, s: o };
} }
function Y(t, e) { function K(t, e) {
let n = L(this); let n = S(this);
return M( return M(
n, n,
e, 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 vt(t) { function Q(t, e, n, r) {
return Array.from(t.children).forEach((e) => e.remove()), t; return t instanceof f.H ? t[e + "Attribute"](n, r) : t[e + "AttributeNS"](null, n, r);
} }
function tt(t, e, n, r) { function X(t, e) {
return t instanceof a.H ? t[e + "Attribute"](n, r) : t[e + "AttributeNS"](null, n, r);
}
function et(t, e) {
if (!(e in t)) return !1; if (!(e in t)) return !1;
let n = I(t, e); let n = z(t, e);
return !E(n.set); return !m(n.set);
} }
function I(t, e) { function z(t, e) {
if (t = Object.getPrototypeOf(t), !t) return {}; if (t = Object.getPrototypeOf(t), !t) return {};
let n = Object.getOwnPropertyDescriptor(t, e); let n = Object.getOwnPropertyDescriptor(t, e);
return n || I(t, e); return n || z(t, e);
} }
function M(t, e, n) { function M(t, e, n) {
if (!(typeof e != "object" || e === null)) if (!(typeof e != "object" || e === null))
@ -245,140 +240,138 @@ function M(t, e, n) {
o && (c = t.processReactiveAttribute(e, o, c, n), n(o, c)); o && (c = t.processReactiveAttribute(e, o, c, n), n(o, c));
}); });
} }
function Z(t) { function Y(t, e, n, r) {
return Array.isArray(t) ? t.filter(Boolean).join(" ") : t; return t[(m(r) ? "remove" : "set") + e](n, r);
} }
function nt(t, e, n, r) { function tt(t, e, n, r, o = null) {
return t[(E(r) ? "remove" : "set") + e](n, Z(r)); return t[(m(r) ? "remove" : "set") + e + "NS"](o, n, r);
} }
function rt(t, e, n, r, o = null) { function F(t, e, n) {
return t[(E(r) ? "remove" : "set") + e + "NS"](o, n, Z(r)); if (Reflect.set(t, e, n), !!m(n))
}
function H(t, e, n) {
if (Reflect.set(t, e, n), !!E(n))
return Reflect.deleteProperty(t, e); return Reflect.deleteProperty(t, e);
} }
// src/events-observer.js // src/events-observer.js
var w = a.M ? ot() : new Proxy({}, { var y = f.M ? et() : new Proxy({}, {
get() { get() {
return () => { return () => {
}; };
} }
}); });
function ot() { function et() {
let t = /* @__PURE__ */ new Map(), e = !1, n = (i) => function(u) { let t = /* @__PURE__ */ new Map(), e = !1, n = (s) => function(u) {
for (let s of u) for (let i of u)
if (s.type === "childList") { if (i.type === "childList") {
if (h(s.addedNodes, !0)) { if (l(i.addedNodes, !0)) {
i(); s();
continue; continue;
} }
A(s.removedNodes, !0) && i(); E(i.removedNodes, !0) && s();
} }
}, r = new a.M(n(d)); }, r = new f.M(n(d));
return { return {
observe(i) { observe(s) {
let u = new a.M(n(() => { let u = new f.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) {
f(); a();
let s = c(i); let i = c(s);
s.connected.has(u) || (s.connected.add(u), s.length_c += 1); i.connected.has(u) || (i.connected.add(u), i.length_c += 1);
}, },
offConnected(i, u) { offConnected(s, u) {
if (!t.has(i)) return; if (!t.has(s)) return;
let s = t.get(i); let i = t.get(s);
s.connected.has(u) && (s.connected.delete(u), s.length_c -= 1, o(i, s)); i.connected.has(u) && (i.connected.delete(u), i.length_c -= 1, o(s, i));
}, },
onDisconnected(i, u) { onDisconnected(s, u) {
f(); a();
let s = c(i); let i = c(s);
s.disconnected.has(u) || (s.disconnected.add(u), s.length_d += 1); i.disconnected.has(u) || (i.disconnected.add(u), i.length_d += 1);
}, },
offDisconnected(i, u) { offDisconnected(s, u) {
if (!t.has(i)) return; if (!t.has(s)) return;
let s = t.get(i); let i = t.get(s);
s.disconnected.has(u) && (s.disconnected.delete(u), s.length_d -= 1, o(i, s)); i.disconnected.has(u) && (i.disconnected.delete(u), i.length_d -= 1, o(s, i));
} }
}; };
function o(i, u) { function o(s, u) {
u.length_c || u.length_d || (t.delete(i), d()); u.length_c || u.length_d || (t.delete(s), d());
} }
function c(i) { function c(s) {
if (t.has(i)) return t.get(i); if (t.has(s)) 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 f() { function a() {
e || (e = !0, r.observe(a.D.body, { childList: !0, subtree: !0 })); e || (e = !0, r.observe(f.D.body, { childList: !0, subtree: !0 }));
} }
function d() { function d() {
!e || t.size || (e = !1, r.disconnect()); !e || t.size || (e = !1, r.disconnect());
} }
function p() { function p() {
return new Promise(function(i) { return new Promise(function(s) {
(requestIdleCallback || requestAnimationFrame)(i); (requestIdleCallback || requestAnimationFrame)(s);
}); });
} }
async function b(i) { async function b(s) {
t.size > 30 && await p(); t.size > 30 && await p();
let u = []; let u = [];
if (!(i instanceof Node)) return u; if (!(s instanceof Node)) return u;
for (let s of t.keys()) for (let i of t.keys())
s === i || !(s instanceof Node) || i.contains(s) && u.push(s); i === s || !(i instanceof Node) || s.contains(i) && u.push(i);
return u; return u;
} }
function h(i, u) { function l(s, u) {
let s = !1; let i = !1;
for (let l of i) { for (let h of s) {
if (u && b(l).then(h), !t.has(l)) continue; if (u && b(h).then(l), !t.has(h)) continue;
let m = t.get(l); let _ = t.get(h);
m.length_c && (l.dispatchEvent(new Event(g)), m.connected = /* @__PURE__ */ new WeakSet(), m.length_c = 0, m.length_d || t.delete(l), s = !0); _.length_c && (h.dispatchEvent(new Event(v)), _.connected = /* @__PURE__ */ new WeakSet(), _.length_c = 0, _.length_d || t.delete(h), i = !0);
} }
return s; return i;
} }
function A(i, u) { function E(s, u) {
let s = !1; let i = !1;
for (let l of i) for (let h of s)
u && b(l).then(A), !(!t.has(l) || !t.get(l).length_d) && ((globalThis.queueMicrotask || setTimeout)(_(l)), s = !0); u && b(h).then(E), !(!t.has(h) || !t.get(h).length_d) && ((globalThis.queueMicrotask || setTimeout)(I(h)), i = !0);
return s; return i;
} }
function _(i) { function I(s) {
return () => { return () => {
i.isConnected || (i.dispatchEvent(new Event(y)), t.delete(i)); s.isConnected || (s.dispatchEvent(new Event(w)), t.delete(s));
}; };
} }
} }
// src/customElement.js // src/customElement.js
function Dt(t, e, n, r = it) { function wt(t, e, n = rt) {
S.push({ let r = t.host || t;
scope: t, O.push({
host: (...f) => f.length ? f.forEach((d) => d(t)) : t scope: r,
}), typeof r == "function" && (r = r.call(t, t)); host: (...a) => a.length ? a.forEach((d) => d(r)) : r
let o = t[x]; }), typeof n == "function" && (n = n.call(r, r));
o || ct(t); let o = r[x];
let c = n.call(t, r); o || nt(r);
return o || t.dispatchEvent(new Event(g)), e.nodeType === 11 && typeof e.mode == "string" && t.addEventListener(y, w.observe(e), { once: !0 }), S.pop(), e.append(c); let c = e.call(r, n);
return o || r.dispatchEvent(new Event(v)), t.nodeType === 11 && typeof t.mode == "string" && r.addEventListener(w, y.observe(t), { once: !0 }), O.pop(), t.append(c);
} }
function ct(t) { function nt(t) {
return k(t.prototype, "connectedCallback", function(e, n, r) { return k(t.prototype, "connectedCallback", function(e, n, r) {
e.apply(n, r), n.dispatchEvent(new Event(g)); e.apply(n, r), n.dispatchEvent(new Event(v));
}), k(t.prototype, "disconnectedCallback", function(e, n, r) { }), k(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(w))
); );
}), k(t.prototype, "attributeChangedCallback", function(e, n, r) { }), k(t.prototype, "attributeChangedCallback", function(e, n, r) {
let [o, , c] = r; let [o, , c] = r;
n.dispatchEvent(new CustomEvent(D, { n.dispatchEvent(new CustomEvent(C, {
detail: [o, c] detail: [o, c]
})), e.apply(n, r); })), e.apply(n, r);
}), t.prototype[x] = !0, t; }), t.prototype[x] = !0, t;
@ -387,74 +380,74 @@ function k(t, e, n) {
t[e] = new Proxy(t[e] || (() => { t[e] = new Proxy(t[e] || (() => {
}), { apply: n }); }), { apply: n });
} }
function it(t) { function rt(t) {
return F(t, (e, n) => e.getAttribute(n)); return W(t, (e, n) => e.getAttribute(n));
} }
// src/events.js // src/events.js
function _t(t, e, n) { function Ct(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 f = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e); let a = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e);
return o.dispatchEvent(f); return o.dispatchEvent(a);
}; };
} }
function R(t, e, n) { function D(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 B = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 });
R.connected = function(t, e) { D.connected = function(t, e) {
return e = G(e), function(r) { return e = B(e), function(r) {
return r.addEventListener(g, t, e), r[x] ? r : r.isConnected ? (r.dispatchEvent(new Event(g)), r) : (N(e.signal, () => w.offConnected(r, t)) && w.onConnected(r, t), r); return r.addEventListener(v, t, e), r[x] ? r : r.isConnected ? (r.dispatchEvent(new Event(v)), r) : (L(e.signal, () => y.offConnected(r, t)) && y.onConnected(r, t), r);
}; };
}; };
R.disconnected = function(t, e) { D.disconnected = function(t, e) {
return e = G(e), function(r) { return e = B(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(w, t, e), r[x] || L(e.signal, () => y.offDisconnected(r, t)) && y.onDisconnected(r, t), r;
}; };
}; };
var W = /* @__PURE__ */ new WeakMap(); var j = /* @__PURE__ */ new WeakMap();
R.disconnectedAsAbort = function(t) { D.disconnectedAsAbort = function(t) {
if (W.has(t)) return W.get(t); if (j.has(t)) return j.get(t);
let e = new AbortController(); let e = new AbortController();
return W.set(t, e), t(R.disconnected(() => e.abort())), e; return j.set(t, e), t(D.disconnected(() => e.abort())), e;
}; };
var st = /* @__PURE__ */ new WeakSet(); var ot = /* @__PURE__ */ new WeakSet();
R.attributeChanged = function(t, e) { D.attributeChanged = function(t, e) {
return typeof e != "object" && (e = {}), function(r) { return typeof e != "object" && (e = {}), function(r) {
if (r.addEventListener(D, t, e), r[x] || st.has(r) || !a.M) return r; if (r.addEventListener(C, t, e), r[x] || ot.has(r) || !f.M) return r;
let o = new a.M(function(f) { let o = new f.M(function(a) {
for (let { attributeName: d, target: p } of f) for (let { attributeName: d, target: p } of a)
p.dispatchEvent( p.dispatchEvent(
new CustomEvent(D, { detail: [d, p.getAttribute(d)] }) new CustomEvent(C, { detail: [d, p.getAttribute(d)] })
); );
}); });
return N(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r; return L(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r;
}; };
}; };
globalThis.dde= { globalThis.dde= {
assign: O, assign: R,
assignAttribute: z, assignAttribute: U,
chainableAppend: Q, chainableAppend: J,
classListDeclarative: Y, classListDeclarative: K,
createElement: j, createElement: P,
createElementNS: gt, createElementNS: pt,
customElementRender: Dt, customElementRender: wt,
customElementWithDDE: ct, customElementWithDDE: nt,
dispatchEvent: _t, dispatchEvent: Ct,
el: j, el: P,
elNS: gt, elNS: pt,
elementAttribute: tt, elementAttribute: Q,
empty: vt, lifecyclesToEvents: nt,
lifecyclesToEvents: ct, observedAttributes: rt,
observedAttributes: it, on: D,
on: R, queue: dt,
registerReactivity: V, registerReactivity: Z,
scope: S, scope: O,
simulateSlots: bt simulateSlots: lt
}; };
})(); })();

View File

@ -7,7 +7,9 @@ type SupportedElement=
| CustomElementTagNameMap[keyof CustomElementTagNameMap] | CustomElementTagNameMap[keyof CustomElementTagNameMap]
declare global { declare global {
type ddeComponentAttributes= Record<any, any> | undefined; type ddeComponentAttributes= Record<any, any> | undefined;
type ddeElementAddon<El extends SupportedElement | DocumentFragment>= (element: El)=> El | void; type ddeElementAddon<El extends SupportedElement | DocumentFragment | Node>= (element: El)=> any;
type ddeString= string | ddeSignal<string>
type ddeStringable= ddeString | number | ddeSignal<number>
} }
type PascalCase= type PascalCase=
`${Capitalize<string>}${string}`; `${Capitalize<string>}${string}`;
@ -15,50 +17,72 @@ 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> | ddeSignal<string> | Partial<{ [K in keyof CSSStyleDeclaration]: ddeSignal<CSSStyleDeclaration[K]> }> style: Partial<CSSStyleDeclaration> | ddeString
| Partial<{ [K in keyof CSSStyleDeclaration]: ddeSignal<CSSStyleDeclaration[K]> }>
/** /**
* 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|ddeSignal<-1|0|1|boolean>>, classList: Record<string,-1|0|1|boolean|ddeSignal<-1|0|1|boolean>>,
/** /**
* By default simiral to `className`, but also supports `string[]` * Used by the dataset HTML attribute to represent data for custom attributes added to elements.
* Values are converted to string (see {@link DOMStringMap}).
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMStringMap)
* */ * */
className: string | (string|boolean|undefined|ddeSignal<string|boolean|undefined>)[]; dataset: Record<string, ddeStringable>,
/** /**
* Sets `aria-*` simiraly to `dataset` * Sets `aria-*` simiraly to `dataset`
* */ * */
ariaset: Record<string,string|ddeSignal<string>>, ariaset: Record<string, ddeString>,
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|ddeSignal<string>> & Record<`.${string}`, any> } & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, ddeString>
& Record<`.${string}`, any>
type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>; type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>;
type IsReadonly<T, K extends keyof T> =
T extends { readonly [P in K]: T[K] } ? true : false;
/** /**
* Just element attributtes * Just element attributtes
* *
* In most cases, you can use native propertie such as [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)). * In most cases, you can use native propertie such as
* [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on
* (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)).
* *
* 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<{ [K in keyof _fromElsInterfaces<T>]: _fromElsInterfaces<T>[K] | ddeSignal<_fromElsInterfaces<T>[K]> } & AttrsModified> & Record<string, any>; type ElementAttributes<T extends SupportedElement>= Partial<{
export function classListDeclarative<El extends SupportedElement>(element: El, classList: AttrsModified["classList"]): El [K in keyof _fromElsInterfaces<T>]: IsReadonly<_fromElsInterfaces<T>, K> extends false
? _fromElsInterfaces<T>[K] | ddeSignal<_fromElsInterfaces<T>[K]>
: ddeStringable
} & AttrsModified> & Record<string, any>;
export function classListDeclarative<El extends SupportedElement>(
element: El,
classList: AttrsModified["classList"]
): El
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El export function 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 | ddeSignal<string>;
export function el< export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap, TAG extends keyof ExtendedHTMLElementTagNameMap,
EL extends ExtendedHTMLElementTagNameMap[TAG]
>( >(
tag_name: TAG, tag_name: TAG,
attrs?: ElementAttributes<EL> | textContent, attrs?: ElementAttributes<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]> | ddeStringable,
...addons: ddeElementAddon<EL>[] ...addons: ddeElementAddon<
ExtendedHTMLElementTagNameMap[NoInfer<TAG>]
>[], // TODO: for now addons must have the same element
): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement ): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement
export function el( export function el(
tag_name?: "<>", tag_name?: "<>",
): ddeDocumentFragment ): ddeDocumentFragment
export function el( export function el(
tag_name: string, tag_name: string,
attrs?: ElementAttributes<HTMLElement> | textContent, attrs?: ElementAttributes<HTMLElement> | ddeStringable,
...addons: ddeElementAddon<HTMLElement>[] ...addons: ddeElementAddon<HTMLElement>[]
): ddeHTMLElement ): ddeHTMLElement
@ -66,9 +90,11 @@ export function el<
C extends (attr: ddeComponentAttributes)=> SupportedElement | ddeDocumentFragment C extends (attr: ddeComponentAttributes)=> SupportedElement | ddeDocumentFragment
>( >(
component: C, component: C,
attrs?: Parameters<C>[0] | textContent, attrs?: Parameters<C>[0] | ddeStringable,
...addons: ddeElementAddon<ReturnType<C>>[] ...addons: ddeElementAddon<ReturnType<C>>[]
): ReturnType<C> extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? ReturnType<C> : ( ReturnType<C> extends ddeDocumentFragment ? ReturnType<C> : ddeHTMLElement ) ): ReturnType<C> extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap]
? ReturnType<C>
: ( ReturnType<C> extends ddeDocumentFragment ? ReturnType<C> : ddeHTMLElement )
export { el as createElement } export { el as createElement }
export function elNS( export function elNS(
@ -78,8 +104,8 @@ export function elNS(
EL extends ( TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement ), EL extends ( TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement ),
>( >(
tag_name: TAG, tag_name: TAG,
attrs?: ElementAttributes<EL> | textContent, attrs?: ElementAttributes<NoInfer<EL>> | ddeStringable,
...addons: ddeElementAddon<EL>[] ...addons: ddeElementAddon<NoInfer<EL>>[]
)=> TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement )=> TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement
export function elNS( export function elNS(
namespace: "http://www.w3.org/1998/Math/MathML" namespace: "http://www.w3.org/1998/Math/MathML"
@ -88,54 +114,60 @@ 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] | ddeSignal<EL[key]> | string | number | boolean }>, attrs?: ddeStringable | Partial<{
...addons: ddeElementAddon<EL>[] [key in keyof EL]: EL[key] | ddeSignal<EL[key]> | string | number | boolean
}>,
...addons: ddeElementAddon<NoInfer<EL>>[]
)=> ddeMathMLElement )=> ddeMathMLElement
export function elNS( export function elNS(
namespace: string namespace: string
): ( ): (
tag_name: string, tag_name: string,
attrs?: string | textContent | Record<string, any>, attrs?: string | ddeStringable | Record<string, any>,
...addons: ddeElementAddon<SupportedElement>[] ...addons: ddeElementAddon<SupportedElement>[]
)=> SupportedElement )=> SupportedElement
export { elNS as createElementNS } export { elNS as createElementNS }
export function chainableAppend<EL extends SupportedElement>(el: EL): EL; export function chainableAppend<EL extends SupportedElement>(el: EL): EL;
/**
* Mapper function (optional). Pass for coppying attributes, this is NOT implemented by {@link simulateSlots} itself!
* */
type simulateSlotsMapper= (body: HTMLSlotElement, el: HTMLElement)=> void;
/** Simulate slots for ddeComponents */ /** Simulate slots for ddeComponents */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(root: EL, mapper?: simulateSlotsMapper): EL export function simulateSlots<EL extends SupportedElement | DocumentFragment>(
root: EL,
): EL
/** /**
* Simulate slots in Custom Elements without using `shadowRoot`. * Simulate slots in Custom Elements without using `shadowRoot`.
* @param el Custom Element root element * @param el Custom Element root element
* @param body Body of the custom element * @param body Body of the custom element
* */ * */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(el: HTMLElement, body: EL, mapper?: simulateSlotsMapper): EL export function simulateSlots<EL extends SupportedElement | DocumentFragment>(
el: HTMLElement,
body: EL,
): EL
export function dispatchEvent(name: keyof DocumentEventMap | string, options?: EventInit): export function dispatchEvent(name: keyof DocumentEventMap | string, options?: EventInit):
(element: SupportedElement, data?: any)=> void; (element: SupportedElement, data?: any)=> void;
export function dispatchEvent(name: keyof DocumentEventMap | string, options: EventInit | null, element: SupportedElement | (()=> SupportedElement)): export function dispatchEvent(
(data?: any)=> void; name: keyof DocumentEventMap | string,
options: EventInit | null,
element: SupportedElement | (()=> SupportedElement)
): (data?: any)=> void;
interface On{ interface On{
/** Listens to the DOM event. See {@link Document.addEventListener} */ /** Listens to the DOM event. See {@link Document.addEventListener} */
< <
EE extends ddeElementAddon<SupportedElement>, Event extends keyof DocumentEventMap,
El extends ( EE extends ddeElementAddon<infer El> ? El : never ), EE extends ddeElementAddon<SupportedElement>= ddeElementAddon<HTMLElement>,
Event extends keyof DocumentEventMap>( >(
type: Event, type: Event,
listener: (this: El, ev: DocumentEventMap[Event]) => any, listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: DocumentEventMap[Event]) => any,
options?: AddEventListenerOptions options?: AddEventListenerOptions
) : EE; ) : EE;
< <
EE extends ddeElementAddon<SupportedElement>, EE extends ddeElementAddon<SupportedElement>= ddeElementAddon<HTMLElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never )>( >(
type: string, type: string,
listener: (this: El, ev: Event | CustomEvent ) => any, listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: Event | CustomEvent ) => any,
options?: AddEventListenerOptions options?: AddEventListenerOptions
) : EE; ) : EE;
/** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ /** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */// editorconfig-checker-disable-line
connected< connected<
EE extends ddeElementAddon<SupportedElement>, EE extends ddeElementAddon<SupportedElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never ) El extends ( EE extends ddeElementAddon<infer El> ? El : never )
@ -143,7 +175,7 @@ interface On{
listener: (this: El, event: CustomEvent<El>) => any, listener: (this: El, event: CustomEvent<El>) => any,
options?: AddEventListenerOptions options?: AddEventListenerOptions
) : EE; ) : EE;
/** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ /** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */// editorconfig-checker-disable-line
disconnected< disconnected<
EE extends ddeElementAddon<SupportedElement>, EE extends ddeElementAddon<SupportedElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never ) El extends ( EE extends ddeElementAddon<infer El> ? El : never )
@ -151,7 +183,7 @@ interface On{
listener: (this: El, event: CustomEvent<void>) => any, listener: (this: El, event: CustomEvent<void>) => any,
options?: AddEventListenerOptions options?: AddEventListenerOptions
) : EE; ) : EE;
/** Listens to the element attribute changes. In case of custom elements uses [`attributeChangedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ /** Listens to the element attribute changes. In case of custom elements uses [`attributeChangedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */// editorconfig-checker-disable-line
attributeChanged< attributeChanged<
EE extends ddeElementAddon<SupportedElement>, EE extends ddeElementAddon<SupportedElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never ) El extends ( EE extends ddeElementAddon<infer El> ? El : never )
@ -162,7 +194,12 @@ interface On{
} }
export const on: On; export const on: On;
type Scope= { scope: Node | Function | Object, host: ddeElementAddon<any>, custom_element: false | HTMLElement, prevent: boolean } type Scope= {
scope: Node | Function | Object,
host: ddeElementAddon<any>,
custom_element: false | HTMLElement,
prevent: boolean
};
/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */ /** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */
export const scope: { export const scope: {
current: Scope, current: Scope,
@ -190,7 +227,6 @@ export function customElementRender<
EL extends HTMLElement, EL extends HTMLElement,
P extends any = Record<string, string | ddeSignal<string>> P extends any = Record<string, string | ddeSignal<string>>
>( >(
custom_element: EL,
target: ShadowRoot | EL, target: ShadowRoot | EL,
render: (props: P)=> SupportedElement | DocumentFragment, render: (props: P)=> SupportedElement | DocumentFragment,
props?: P | ((el: EL)=> P) props?: P | ((el: EL)=> P)
@ -350,6 +386,7 @@ declare global{
} }
} }
// editorconfig-checker-disable
interface ddeHTMLAnchorElement extends HTMLAnchorElement{ append: ddeAppend<ddeHTMLAnchorElement>; } interface ddeHTMLAnchorElement extends HTMLAnchorElement{ append: ddeAppend<ddeHTMLAnchorElement>; }
interface ddeHTMLAreaElement extends HTMLAreaElement{ append: ddeAppend<ddeHTMLAreaElement>; } interface ddeHTMLAreaElement extends HTMLAreaElement{ append: ddeAppend<ddeHTMLAreaElement>; }
interface ddeHTMLAudioElement extends HTMLAudioElement{ append: ddeAppend<ddeHTMLAudioElement>; } interface ddeHTMLAudioElement extends HTMLAudioElement{ append: ddeAppend<ddeHTMLAudioElement>; }
@ -477,6 +514,7 @@ interface ddeSVGTitleElement extends SVGTitleElement{ append: ddeAppend<ddeSVGTi
interface ddeSVGTSpanElement extends SVGTSpanElement{ append: ddeAppend<ddeSVGTSpanElement>; } interface ddeSVGTSpanElement extends SVGTSpanElement{ append: ddeAppend<ddeSVGTSpanElement>; }
interface ddeSVGUseElement extends SVGUseElement{ append: ddeAppend<ddeSVGUseElement>; } interface ddeSVGUseElement extends SVGUseElement{ append: ddeAppend<ddeSVGUseElement>; }
interface ddeSVGViewElement extends SVGViewElement{ append: ddeAppend<ddeSVGViewElement>; } interface ddeSVGViewElement extends SVGViewElement{ append: ddeAppend<ddeSVGViewElement>; }
// editorconfig-checker-enable
export type Signal<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 signal._ | void; type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typeof signal._ | void;
//type SymbolSignal= Symbol; //type SymbolSignal= Symbol;

File diff suppressed because it is too large Load Diff

124
dist/esm.d.ts vendored
View File

@ -7,7 +7,9 @@ type SupportedElement=
| CustomElementTagNameMap[keyof CustomElementTagNameMap] | CustomElementTagNameMap[keyof CustomElementTagNameMap]
declare global { declare global {
type ddeComponentAttributes= Record<any, any> | undefined; type ddeComponentAttributes= Record<any, any> | undefined;
type ddeElementAddon<El extends SupportedElement | DocumentFragment>= (element: El)=> El | void; type ddeElementAddon<El extends SupportedElement | DocumentFragment | Node>= (element: El)=> any;
type ddeString= string | ddeSignal<string>
type ddeStringable= ddeString | number | ddeSignal<number>
} }
type PascalCase= type PascalCase=
`${Capitalize<string>}${string}`; `${Capitalize<string>}${string}`;
@ -15,50 +17,72 @@ 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> | ddeSignal<string> | Partial<{ [K in keyof CSSStyleDeclaration]: ddeSignal<CSSStyleDeclaration[K]> }> style: Partial<CSSStyleDeclaration> | ddeString
| Partial<{ [K in keyof CSSStyleDeclaration]: ddeSignal<CSSStyleDeclaration[K]> }>
/** /**
* 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|ddeSignal<-1|0|1|boolean>>, classList: Record<string,-1|0|1|boolean|ddeSignal<-1|0|1|boolean>>,
/** /**
* By default simiral to `className`, but also supports `string[]` * Used by the dataset HTML attribute to represent data for custom attributes added to elements.
* Values are converted to string (see {@link DOMStringMap}).
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMStringMap)
* */ * */
className: string | (string|boolean|undefined|ddeSignal<string|boolean|undefined>)[]; dataset: Record<string, ddeStringable>,
/** /**
* Sets `aria-*` simiraly to `dataset` * Sets `aria-*` simiraly to `dataset`
* */ * */
ariaset: Record<string,string|ddeSignal<string>>, ariaset: Record<string, ddeString>,
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|ddeSignal<string>> & Record<`.${string}`, any> } & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, ddeString>
& Record<`.${string}`, any>
type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>; type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>;
type IsReadonly<T, K extends keyof T> =
T extends { readonly [P in K]: T[K] } ? true : false;
/** /**
* Just element attributtes * Just element attributtes
* *
* In most cases, you can use native propertie such as [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)). * In most cases, you can use native propertie such as
* [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on
* (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)).
* *
* 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<{ [K in keyof _fromElsInterfaces<T>]: _fromElsInterfaces<T>[K] | ddeSignal<_fromElsInterfaces<T>[K]> } & AttrsModified> & Record<string, any>; type ElementAttributes<T extends SupportedElement>= Partial<{
export function classListDeclarative<El extends SupportedElement>(element: El, classList: AttrsModified["classList"]): El [K in keyof _fromElsInterfaces<T>]: IsReadonly<_fromElsInterfaces<T>, K> extends false
? _fromElsInterfaces<T>[K] | ddeSignal<_fromElsInterfaces<T>[K]>
: ddeStringable
} & AttrsModified> & Record<string, any>;
export function classListDeclarative<El extends SupportedElement>(
element: El,
classList: AttrsModified["classList"]
): El
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El export function 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 | ddeSignal<string>;
export function el< export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap, TAG extends keyof ExtendedHTMLElementTagNameMap,
EL extends ExtendedHTMLElementTagNameMap[TAG]
>( >(
tag_name: TAG, tag_name: TAG,
attrs?: ElementAttributes<EL> | textContent, attrs?: ElementAttributes<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]> | ddeStringable,
...addons: ddeElementAddon<EL>[] ...addons: ddeElementAddon<
ExtendedHTMLElementTagNameMap[NoInfer<TAG>]
>[], // TODO: for now addons must have the same element
): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement ): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement
export function el( export function el(
tag_name?: "<>", tag_name?: "<>",
): ddeDocumentFragment ): ddeDocumentFragment
export function el( export function el(
tag_name: string, tag_name: string,
attrs?: ElementAttributes<HTMLElement> | textContent, attrs?: ElementAttributes<HTMLElement> | ddeStringable,
...addons: ddeElementAddon<HTMLElement>[] ...addons: ddeElementAddon<HTMLElement>[]
): ddeHTMLElement ): ddeHTMLElement
@ -66,9 +90,11 @@ export function el<
C extends (attr: ddeComponentAttributes)=> SupportedElement | ddeDocumentFragment C extends (attr: ddeComponentAttributes)=> SupportedElement | ddeDocumentFragment
>( >(
component: C, component: C,
attrs?: Parameters<C>[0] | textContent, attrs?: Parameters<C>[0] | ddeStringable,
...addons: ddeElementAddon<ReturnType<C>>[] ...addons: ddeElementAddon<ReturnType<C>>[]
): ReturnType<C> extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? ReturnType<C> : ( ReturnType<C> extends ddeDocumentFragment ? ReturnType<C> : ddeHTMLElement ) ): ReturnType<C> extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap]
? ReturnType<C>
: ( ReturnType<C> extends ddeDocumentFragment ? ReturnType<C> : ddeHTMLElement )
export { el as createElement } export { el as createElement }
export function elNS( export function elNS(
@ -78,8 +104,8 @@ export function elNS(
EL extends ( TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement ), EL extends ( TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement ),
>( >(
tag_name: TAG, tag_name: TAG,
attrs?: ElementAttributes<EL> | textContent, attrs?: ElementAttributes<NoInfer<EL>> | ddeStringable,
...addons: ddeElementAddon<EL>[] ...addons: ddeElementAddon<NoInfer<EL>>[]
)=> TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement )=> TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement
export function elNS( export function elNS(
namespace: "http://www.w3.org/1998/Math/MathML" namespace: "http://www.w3.org/1998/Math/MathML"
@ -88,54 +114,60 @@ 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] | ddeSignal<EL[key]> | string | number | boolean }>, attrs?: ddeStringable | Partial<{
...addons: ddeElementAddon<EL>[] [key in keyof EL]: EL[key] | ddeSignal<EL[key]> | string | number | boolean
}>,
...addons: ddeElementAddon<NoInfer<EL>>[]
)=> ddeMathMLElement )=> ddeMathMLElement
export function elNS( export function elNS(
namespace: string namespace: string
): ( ): (
tag_name: string, tag_name: string,
attrs?: string | textContent | Record<string, any>, attrs?: string | ddeStringable | Record<string, any>,
...addons: ddeElementAddon<SupportedElement>[] ...addons: ddeElementAddon<SupportedElement>[]
)=> SupportedElement )=> SupportedElement
export { elNS as createElementNS } export { elNS as createElementNS }
export function chainableAppend<EL extends SupportedElement>(el: EL): EL; export function chainableAppend<EL extends SupportedElement>(el: EL): EL;
/**
* Mapper function (optional). Pass for coppying attributes, this is NOT implemented by {@link simulateSlots} itself!
* */
type simulateSlotsMapper= (body: HTMLSlotElement, el: HTMLElement)=> void;
/** Simulate slots for ddeComponents */ /** Simulate slots for ddeComponents */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(root: EL, mapper?: simulateSlotsMapper): EL export function simulateSlots<EL extends SupportedElement | DocumentFragment>(
root: EL,
): EL
/** /**
* Simulate slots in Custom Elements without using `shadowRoot`. * Simulate slots in Custom Elements without using `shadowRoot`.
* @param el Custom Element root element * @param el Custom Element root element
* @param body Body of the custom element * @param body Body of the custom element
* */ * */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(el: HTMLElement, body: EL, mapper?: simulateSlotsMapper): EL export function simulateSlots<EL extends SupportedElement | DocumentFragment>(
el: HTMLElement,
body: EL,
): EL
export function dispatchEvent(name: keyof DocumentEventMap | string, options?: EventInit): export function dispatchEvent(name: keyof DocumentEventMap | string, options?: EventInit):
(element: SupportedElement, data?: any)=> void; (element: SupportedElement, data?: any)=> void;
export function dispatchEvent(name: keyof DocumentEventMap | string, options: EventInit | null, element: SupportedElement | (()=> SupportedElement)): export function dispatchEvent(
(data?: any)=> void; name: keyof DocumentEventMap | string,
options: EventInit | null,
element: SupportedElement | (()=> SupportedElement)
): (data?: any)=> void;
interface On{ interface On{
/** Listens to the DOM event. See {@link Document.addEventListener} */ /** Listens to the DOM event. See {@link Document.addEventListener} */
< <
EE extends ddeElementAddon<SupportedElement>, Event extends keyof DocumentEventMap,
El extends ( EE extends ddeElementAddon<infer El> ? El : never ), EE extends ddeElementAddon<SupportedElement>= ddeElementAddon<HTMLElement>,
Event extends keyof DocumentEventMap>( >(
type: Event, type: Event,
listener: (this: El, ev: DocumentEventMap[Event]) => any, listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: DocumentEventMap[Event]) => any,
options?: AddEventListenerOptions options?: AddEventListenerOptions
) : EE; ) : EE;
< <
EE extends ddeElementAddon<SupportedElement>, EE extends ddeElementAddon<SupportedElement>= ddeElementAddon<HTMLElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never )>( >(
type: string, type: string,
listener: (this: El, ev: Event | CustomEvent ) => any, listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: Event | CustomEvent ) => any,
options?: AddEventListenerOptions options?: AddEventListenerOptions
) : EE; ) : EE;
/** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ /** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */// editorconfig-checker-disable-line
connected< connected<
EE extends ddeElementAddon<SupportedElement>, EE extends ddeElementAddon<SupportedElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never ) El extends ( EE extends ddeElementAddon<infer El> ? El : never )
@ -143,7 +175,7 @@ interface On{
listener: (this: El, event: CustomEvent<El>) => any, listener: (this: El, event: CustomEvent<El>) => any,
options?: AddEventListenerOptions options?: AddEventListenerOptions
) : EE; ) : EE;
/** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ /** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */// editorconfig-checker-disable-line
disconnected< disconnected<
EE extends ddeElementAddon<SupportedElement>, EE extends ddeElementAddon<SupportedElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never ) El extends ( EE extends ddeElementAddon<infer El> ? El : never )
@ -151,7 +183,7 @@ interface On{
listener: (this: El, event: CustomEvent<void>) => any, listener: (this: El, event: CustomEvent<void>) => any,
options?: AddEventListenerOptions options?: AddEventListenerOptions
) : EE; ) : EE;
/** Listens to the element attribute changes. In case of custom elements uses [`attributeChangedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ /** Listens to the element attribute changes. In case of custom elements uses [`attributeChangedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */// editorconfig-checker-disable-line
attributeChanged< attributeChanged<
EE extends ddeElementAddon<SupportedElement>, EE extends ddeElementAddon<SupportedElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never ) El extends ( EE extends ddeElementAddon<infer El> ? El : never )
@ -162,7 +194,12 @@ interface On{
} }
export const on: On; export const on: On;
type Scope= { scope: Node | Function | Object, host: ddeElementAddon<any>, custom_element: false | HTMLElement, prevent: boolean } type Scope= {
scope: Node | Function | Object,
host: ddeElementAddon<any>,
custom_element: false | HTMLElement,
prevent: boolean
};
/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */ /** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */
export const scope: { export const scope: {
current: Scope, current: Scope,
@ -190,7 +227,6 @@ export function customElementRender<
EL extends HTMLElement, EL extends HTMLElement,
P extends any = Record<string, string | ddeSignal<string>> P extends any = Record<string, string | ddeSignal<string>>
>( >(
custom_element: EL,
target: ShadowRoot | EL, target: ShadowRoot | EL,
render: (props: P)=> SupportedElement | DocumentFragment, render: (props: P)=> SupportedElement | DocumentFragment,
props?: P | ((el: EL)=> P) props?: P | ((el: EL)=> P)
@ -350,6 +386,7 @@ declare global{
} }
} }
// editorconfig-checker-disable
interface ddeHTMLAnchorElement extends HTMLAnchorElement{ append: ddeAppend<ddeHTMLAnchorElement>; } interface ddeHTMLAnchorElement extends HTMLAnchorElement{ append: ddeAppend<ddeHTMLAnchorElement>; }
interface ddeHTMLAreaElement extends HTMLAreaElement{ append: ddeAppend<ddeHTMLAreaElement>; } interface ddeHTMLAreaElement extends HTMLAreaElement{ append: ddeAppend<ddeHTMLAreaElement>; }
interface ddeHTMLAudioElement extends HTMLAudioElement{ append: ddeAppend<ddeHTMLAudioElement>; } interface ddeHTMLAudioElement extends HTMLAudioElement{ append: ddeAppend<ddeHTMLAudioElement>; }
@ -477,3 +514,4 @@ interface ddeSVGTitleElement extends SVGTitleElement{ append: ddeAppend<ddeSVGTi
interface ddeSVGTSpanElement extends SVGTSpanElement{ append: ddeAppend<ddeSVGTSpanElement>; } interface ddeSVGTSpanElement extends SVGTSpanElement{ append: ddeAppend<ddeSVGTSpanElement>; }
interface ddeSVGUseElement extends SVGUseElement{ append: ddeAppend<ddeSVGUseElement>; } interface ddeSVGUseElement extends SVGUseElement{ append: ddeAppend<ddeSVGUseElement>; }
interface ddeSVGViewElement extends SVGViewElement{ append: ddeAppend<ddeSVGViewElement>; } interface ddeSVGViewElement extends SVGViewElement{ append: ddeAppend<ddeSVGViewElement>; }
// editorconfig-checker-enable

457
dist/esm.js vendored
View File

@ -1,5 +1,5 @@
// src/signals-common.js // src/signals-common.js
var C = { var A = {
isSignal(t) { isSignal(t) {
return !1; return !1;
}, },
@ -7,19 +7,18 @@ var C = {
return n; return n;
} }
}; };
function V(t, e = !0) { function Z(t, e = !0) {
return e ? Object.assign(C, t) : (Object.setPrototypeOf(t, C), t); return e ? Object.assign(A, t) : (Object.setPrototypeOf(t, A), t);
} }
function L(t) { function S(t) {
return C.isPrototypeOf(t) && t !== C ? t : C; return A.isPrototypeOf(t) && t !== A ? t : A;
} }
// src/helpers.js // src/helpers.js
var q = (...t) => Object.prototype.hasOwnProperty.call(...t); function m(t) {
function E(t) {
return typeof t > "u"; return typeof t > "u";
} }
function N(t, e) { function L(t, e) {
if (!t || !(t instanceof AbortSignal)) if (!t || !(t instanceof AbortSignal))
return !0; return !0;
if (!t.aborted) if (!t.aborted)
@ -27,46 +26,50 @@ function N(t, e) {
t.removeEventListener("abort", e); t.removeEventListener("abort", e);
}; };
} }
function F(t, e) { function W(t, e) {
let { observedAttributes: n = [] } = t.constructor; let { observedAttributes: n = [] } = t.constructor;
return n.reduce(function(r, o) { return n.reduce(function(r, o) {
return r[J(o)] = e(t, o), r; return r[G(o)] = e(t, o), r;
}, {}); }, {});
} }
function J(t) { function G(t) {
return t.replace(/-./g, (e) => e[1].toUpperCase()); return t.replace(/-./g, (e) => e[1].toUpperCase());
} }
// src/dom-common.js // src/dom-common.js
var a = { var f = {
setDeleteAttr: K, setDeleteAttr: V,
ssr: "", ssr: "",
D: globalThis.document, D: globalThis.document,
F: globalThis.DocumentFragment, F: globalThis.DocumentFragment,
H: globalThis.HTMLElement, H: globalThis.HTMLElement,
S: globalThis.SVGElement, S: globalThis.SVGElement,
M: globalThis.MutationObserver M: globalThis.MutationObserver,
q: (t) => t || Promise.resolve()
}; };
function K(t, e, n) { function V(t, e, n) {
if (Reflect.set(t, e, n), !!E(n)) { if (Reflect.set(t, e, n), !!m(n)) {
if (Reflect.deleteProperty(t, e), t instanceof a.H && t.getAttribute(e) === "undefined") if (Reflect.deleteProperty(t, e), t instanceof f.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 x = "__dde_lifecyclesToEvents", g = "dde:connected", y = "dde:disconnected", D = "dde:attributeChanged"; var x = "__dde_lifecyclesToEvents", v = "dde:connected", w = "dde:disconnected", C = "dde:attributeChanged";
// src/dom.js // src/dom.js
var v = [{ function dt(t) {
return f.q(t);
}
var g = [{
get scope() { get scope() {
return a.D.body; return f.D.body;
}, },
host: (t) => t ? t(a.D.body) : a.D.body, host: (t) => t ? t(f.D.body) : f.D.body,
prevent: !0 prevent: !0
}], S = { }], O = {
get current() { get current() {
return v[v.length - 1]; return g[g.length - 1];
}, },
get host() { get host() {
return this.current.host; return this.current.host;
@ -76,166 +79,158 @@ var v = [{
return t.prevent = !0, t; return t.prevent = !0, t;
}, },
get state() { get state() {
return [...v]; return [...g];
}, },
push(t = {}) { push(t = {}) {
return v.push(Object.assign({}, this.current, { prevent: !1 }, t)); return g.push(Object.assign({}, this.current, { prevent: !1 }, t));
}, },
pushRoot() { pushRoot() {
return v.push(v[0]); return g.push(g[0]);
}, },
pop() { pop() {
if (v.length !== 1) if (g.length !== 1)
return v.pop(); return g.pop();
} }
}; };
function $(...t) { function q(...t) {
return this.appendOriginal(...t), this; return this.appendOriginal(...t), this;
} }
function Q(t) { function J(t) {
return t.append === $ || (t.appendOriginal = t.append, t.append = $), t; return t.append === q || (t.appendOriginal = t.append, t.append = q), t;
} }
var T; var T;
function j(t, e, ...n) { function P(t, e, ...n) {
let r = L(this), o = 0, c, f; let r = S(this), o = 0, c, a;
switch ((Object(e) !== e || r.isSignal(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, S.push({ scope: t, host: (...b) => b.length ? (o === 1 ? n.unshift(...b) : b.forEach((h) => h(f)), void 0) : f }), c = t(e || void 0); o = 1;
let d = c instanceof a.F; let d = (...l) => l.length ? (o === 1 ? n.unshift(...l) : l.forEach((E) => E(a)), void 0) : a;
O.push({ scope: t, host: d }), c = t(e || void 0);
let p = c instanceof f.F;
if (c.nodeName === "#comment") break; if (c.nodeName === "#comment") break;
let p = j.mark({ let b = P.mark({
type: "component", type: "component",
name: t.name, name: t.name,
host: d ? "this" : "parentElement" host: p ? "this" : "parentElement"
}); });
c.prepend(p), d && (f = p); c.prepend(b), p && (a = b);
break; break;
} }
case t === "#text": case t === "#text":
c = O.call(this, a.D.createTextNode(""), e); c = R.call(this, f.D.createTextNode(""), e);
break; break;
case (t === "<>" || !t): case (t === "<>" || !t):
c = O.call(this, a.D.createDocumentFragment(), e); c = R.call(this, f.D.createDocumentFragment(), e);
break; break;
case !!T: case !!T:
c = O.call(this, a.D.createElementNS(T, t), e); c = R.call(this, f.D.createElementNS(T, t), e);
break; break;
case !c: case !c:
c = O.call(this, a.D.createElement(t), e); c = R.call(this, f.D.createElement(t), e);
} }
return Q(c), f || (f = c), n.forEach((d) => d(f)), o && S.pop(), o = 2, c; return J(c), a || (a = c), n.forEach((d) => d(a)), o && O.pop(), o = 2, c;
} }
function bt(t, e, n) { P.mark = function(t, e = !1) {
typeof e != "object" && (n = e, e = t);
let r = Symbol.for("default"), o = Array.from(e.querySelectorAll("slot")).reduce((f, d) => Reflect.set(f, d.name || r, d) && f, {}), c = q(o, r);
if (t.append = new Proxy(t.append, {
apply(f, d, p) {
if (p[0] === e) return f.apply(t, p);
if (!p.length) return t;
let b = a.D.createDocumentFragment();
for (let h of p) {
if (!h || !h.slot) {
c && b.append(h);
continue;
}
let A = h.slot, _ = o[A];
tt(h, "remove", "slot"), _ && (X(_, h, n), Reflect.deleteProperty(o, A));
}
return c && (o[r].replaceWith(b), Reflect.deleteProperty(o, r)), t.append = f, t;
}
}), t !== e) {
let f = Array.from(t.childNodes);
f.forEach((d) => d.remove()), t.append(...f);
}
return e;
}
function X(t, e, n) {
n && n(t, e);
try {
t.replaceWith(O(e, { className: [e.className, t.className], dataset: { ...t.dataset } }));
} catch {
t.replaceWith(e);
}
}
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 = a.D.createComment(`<dde:mark ${t}${a.ssr}${n}>`); let n = e ? "" : "/", r = f.D.createComment(`<dde:mark ${t}${f.ssr}${n}>`);
return e && (r.end = a.D.createComment("</dde:mark>")), r; return e && (r.end = f.D.createComment("</dde:mark>")), r;
}; };
function gt(t) { function pt(t) {
let e = this; let e = this;
return function(...r) { return function(...r) {
T = t; T = t;
let o = j.call(e, ...r); let o = P.call(e, ...r);
return T = void 0, o; return T = void 0, o;
}; };
} }
var P = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: U } = a; function lt(t, e = t) {
function O(t, ...e) { let n = "\xB9\u2070", r = "\u2713", o = Object.fromEntries(
if (!e.length) return t; Array.from(e.querySelectorAll("slot")).filter((c) => !c.name.endsWith(n)).map((c) => [c.name += n, c])
P.set(t, B(t, this)); );
for (let [n, r] of Object.entries(Object.assign({}, ...e))) if (t.append = new Proxy(t.append, {
z.call(this, t, n, r); apply(c, a, d) {
return P.delete(t), t; if (d[0] === e) return c.apply(t, d);
for (let p of d) {
let b = (p.slot || "") + n;
try {
Q(p, "remove", "slot");
} catch {
} }
function z(t, e, n) { let l = o[b];
let { setRemoveAttr: r, s: o } = B(t, this), c = this; if (!l) return;
l.name.startsWith(r) || (l.childNodes.forEach((E) => E.remove()), l.name = r + b), l.append(p);
}
return t.append = c, t;
}
}), t !== e) {
let c = Array.from(t.childNodes);
t.append(...c);
}
return e;
}
var N = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: $ } = f;
function R(t, ...e) {
if (!e.length) return t;
N.set(t, H(t, this));
for (let [n, r] of Object.entries(Object.assign({}, ...e)))
U.call(this, t, n, r);
return N.delete(t), t;
}
function U(t, e, n) {
let { setRemoveAttr: r, s: o } = H(t, this), c = this;
n = o.processReactiveAttribute( n = o.processReactiveAttribute(
t, t,
e, e,
n, n,
(d, p) => z.call(c, t, d, p) (d, p) => U.call(c, t, d, p)
); );
let [f] = e; let [a] = e;
if (f === "=") return r(e.slice(1), n); if (a === "=") return r(e.slice(1), n);
if (f === ".") return H(t, e.slice(1), n); if (a === ".") return F(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);
switch (e === "className" && (e = "class"), e) { switch (e === "className" && (e = "class"), e) {
case "xlink:href": case "xlink:href":
return r(e, n, "http://www.w3.org/1999/xlink"); return r(e, n, "http://www.w3.org/1999/xlink");
case "textContent": case "textContent":
return U(t, e, n); return $(t, e, n);
case "style": case "style":
if (typeof n != "object") break; if (typeof n != "object") break;
/* falls through */ /* falls through */
case "dataset": case "dataset":
return M(o, n, H.bind(null, t[e])); return M(o, n, F.bind(null, t[e]));
case "ariaset": case "ariaset":
return M(o, n, (d, p) => r("aria-" + d, p)); return M(o, n, (d, p) => r("aria-" + d, p));
case "classList": case "classList":
return Y.call(c, t, n); return K.call(c, t, n);
} }
return et(t, e) ? U(t, e, n) : r(e, n); return X(t, e) ? $(t, e, n) : r(e, n);
} }
function B(t, e) { function H(t, e) {
if (P.has(t)) return P.get(t); if (N.has(t)) return N.get(t);
let r = (t instanceof a.S ? rt : nt).bind(null, t, "Attribute"), o = L(e); let r = (t instanceof f.S ? tt : Y).bind(null, t, "Attribute"), o = S(e);
return { setRemoveAttr: r, s: o }; return { setRemoveAttr: r, s: o };
} }
function Y(t, e) { function K(t, e) {
let n = L(this); let n = S(this);
return M( return M(
n, n,
e, 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 vt(t) { function Q(t, e, n, r) {
return Array.from(t.children).forEach((e) => e.remove()), t; return t instanceof f.H ? t[e + "Attribute"](n, r) : t[e + "AttributeNS"](null, n, r);
} }
function tt(t, e, n, r) { function X(t, e) {
return t instanceof a.H ? t[e + "Attribute"](n, r) : t[e + "AttributeNS"](null, n, r);
}
function et(t, e) {
if (!(e in t)) return !1; if (!(e in t)) return !1;
let n = I(t, e); let n = z(t, e);
return !E(n.set); return !m(n.set);
} }
function I(t, e) { function z(t, e) {
if (t = Object.getPrototypeOf(t), !t) return {}; if (t = Object.getPrototypeOf(t), !t) return {};
let n = Object.getOwnPropertyDescriptor(t, e); let n = Object.getOwnPropertyDescriptor(t, e);
return n || I(t, e); return n || z(t, e);
} }
function M(t, e, n) { function M(t, e, n) {
if (!(typeof e != "object" || e === null)) if (!(typeof e != "object" || e === null))
@ -243,140 +238,138 @@ function M(t, e, n) {
o && (c = t.processReactiveAttribute(e, o, c, n), n(o, c)); o && (c = t.processReactiveAttribute(e, o, c, n), n(o, c));
}); });
} }
function Z(t) { function Y(t, e, n, r) {
return Array.isArray(t) ? t.filter(Boolean).join(" ") : t; return t[(m(r) ? "remove" : "set") + e](n, r);
} }
function nt(t, e, n, r) { function tt(t, e, n, r, o = null) {
return t[(E(r) ? "remove" : "set") + e](n, Z(r)); return t[(m(r) ? "remove" : "set") + e + "NS"](o, n, r);
} }
function rt(t, e, n, r, o = null) { function F(t, e, n) {
return t[(E(r) ? "remove" : "set") + e + "NS"](o, n, Z(r)); if (Reflect.set(t, e, n), !!m(n))
}
function H(t, e, n) {
if (Reflect.set(t, e, n), !!E(n))
return Reflect.deleteProperty(t, e); return Reflect.deleteProperty(t, e);
} }
// src/events-observer.js // src/events-observer.js
var w = a.M ? ot() : new Proxy({}, { var y = f.M ? et() : new Proxy({}, {
get() { get() {
return () => { return () => {
}; };
} }
}); });
function ot() { function et() {
let t = /* @__PURE__ */ new Map(), e = !1, n = (i) => function(u) { let t = /* @__PURE__ */ new Map(), e = !1, n = (s) => function(u) {
for (let s of u) for (let i of u)
if (s.type === "childList") { if (i.type === "childList") {
if (h(s.addedNodes, !0)) { if (l(i.addedNodes, !0)) {
i(); s();
continue; continue;
} }
A(s.removedNodes, !0) && i(); E(i.removedNodes, !0) && s();
} }
}, r = new a.M(n(d)); }, r = new f.M(n(d));
return { return {
observe(i) { observe(s) {
let u = new a.M(n(() => { let u = new f.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) {
f(); a();
let s = c(i); let i = c(s);
s.connected.has(u) || (s.connected.add(u), s.length_c += 1); i.connected.has(u) || (i.connected.add(u), i.length_c += 1);
}, },
offConnected(i, u) { offConnected(s, u) {
if (!t.has(i)) return; if (!t.has(s)) return;
let s = t.get(i); let i = t.get(s);
s.connected.has(u) && (s.connected.delete(u), s.length_c -= 1, o(i, s)); i.connected.has(u) && (i.connected.delete(u), i.length_c -= 1, o(s, i));
}, },
onDisconnected(i, u) { onDisconnected(s, u) {
f(); a();
let s = c(i); let i = c(s);
s.disconnected.has(u) || (s.disconnected.add(u), s.length_d += 1); i.disconnected.has(u) || (i.disconnected.add(u), i.length_d += 1);
}, },
offDisconnected(i, u) { offDisconnected(s, u) {
if (!t.has(i)) return; if (!t.has(s)) return;
let s = t.get(i); let i = t.get(s);
s.disconnected.has(u) && (s.disconnected.delete(u), s.length_d -= 1, o(i, s)); i.disconnected.has(u) && (i.disconnected.delete(u), i.length_d -= 1, o(s, i));
} }
}; };
function o(i, u) { function o(s, u) {
u.length_c || u.length_d || (t.delete(i), d()); u.length_c || u.length_d || (t.delete(s), d());
} }
function c(i) { function c(s) {
if (t.has(i)) return t.get(i); if (t.has(s)) 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 f() { function a() {
e || (e = !0, r.observe(a.D.body, { childList: !0, subtree: !0 })); e || (e = !0, r.observe(f.D.body, { childList: !0, subtree: !0 }));
} }
function d() { function d() {
!e || t.size || (e = !1, r.disconnect()); !e || t.size || (e = !1, r.disconnect());
} }
function p() { function p() {
return new Promise(function(i) { return new Promise(function(s) {
(requestIdleCallback || requestAnimationFrame)(i); (requestIdleCallback || requestAnimationFrame)(s);
}); });
} }
async function b(i) { async function b(s) {
t.size > 30 && await p(); t.size > 30 && await p();
let u = []; let u = [];
if (!(i instanceof Node)) return u; if (!(s instanceof Node)) return u;
for (let s of t.keys()) for (let i of t.keys())
s === i || !(s instanceof Node) || i.contains(s) && u.push(s); i === s || !(i instanceof Node) || s.contains(i) && u.push(i);
return u; return u;
} }
function h(i, u) { function l(s, u) {
let s = !1; let i = !1;
for (let l of i) { for (let h of s) {
if (u && b(l).then(h), !t.has(l)) continue; if (u && b(h).then(l), !t.has(h)) continue;
let m = t.get(l); let _ = t.get(h);
m.length_c && (l.dispatchEvent(new Event(g)), m.connected = /* @__PURE__ */ new WeakSet(), m.length_c = 0, m.length_d || t.delete(l), s = !0); _.length_c && (h.dispatchEvent(new Event(v)), _.connected = /* @__PURE__ */ new WeakSet(), _.length_c = 0, _.length_d || t.delete(h), i = !0);
} }
return s; return i;
} }
function A(i, u) { function E(s, u) {
let s = !1; let i = !1;
for (let l of i) for (let h of s)
u && b(l).then(A), !(!t.has(l) || !t.get(l).length_d) && ((globalThis.queueMicrotask || setTimeout)(_(l)), s = !0); u && b(h).then(E), !(!t.has(h) || !t.get(h).length_d) && ((globalThis.queueMicrotask || setTimeout)(I(h)), i = !0);
return s; return i;
} }
function _(i) { function I(s) {
return () => { return () => {
i.isConnected || (i.dispatchEvent(new Event(y)), t.delete(i)); s.isConnected || (s.dispatchEvent(new Event(w)), t.delete(s));
}; };
} }
} }
// src/customElement.js // src/customElement.js
function Dt(t, e, n, r = it) { function wt(t, e, n = rt) {
S.push({ let r = t.host || t;
scope: t, O.push({
host: (...f) => f.length ? f.forEach((d) => d(t)) : t scope: r,
}), typeof r == "function" && (r = r.call(t, t)); host: (...a) => a.length ? a.forEach((d) => d(r)) : r
let o = t[x]; }), typeof n == "function" && (n = n.call(r, r));
o || ct(t); let o = r[x];
let c = n.call(t, r); o || nt(r);
return o || t.dispatchEvent(new Event(g)), e.nodeType === 11 && typeof e.mode == "string" && t.addEventListener(y, w.observe(e), { once: !0 }), S.pop(), e.append(c); let c = e.call(r, n);
return o || r.dispatchEvent(new Event(v)), t.nodeType === 11 && typeof t.mode == "string" && r.addEventListener(w, y.observe(t), { once: !0 }), O.pop(), t.append(c);
} }
function ct(t) { function nt(t) {
return k(t.prototype, "connectedCallback", function(e, n, r) { return k(t.prototype, "connectedCallback", function(e, n, r) {
e.apply(n, r), n.dispatchEvent(new Event(g)); e.apply(n, r), n.dispatchEvent(new Event(v));
}), k(t.prototype, "disconnectedCallback", function(e, n, r) { }), k(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(w))
); );
}), k(t.prototype, "attributeChangedCallback", function(e, n, r) { }), k(t.prototype, "attributeChangedCallback", function(e, n, r) {
let [o, , c] = r; let [o, , c] = r;
n.dispatchEvent(new CustomEvent(D, { n.dispatchEvent(new CustomEvent(C, {
detail: [o, c] detail: [o, c]
})), e.apply(n, r); })), e.apply(n, r);
}), t.prototype[x] = !0, t; }), t.prototype[x] = !0, t;
@ -385,71 +378,71 @@ function k(t, e, n) {
t[e] = new Proxy(t[e] || (() => { t[e] = new Proxy(t[e] || (() => {
}), { apply: n }); }), { apply: n });
} }
function it(t) { function rt(t) {
return F(t, (e, n) => e.getAttribute(n)); return W(t, (e, n) => e.getAttribute(n));
} }
// src/events.js // src/events.js
function _t(t, e, n) { function Ct(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 f = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e); let a = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e);
return o.dispatchEvent(f); return o.dispatchEvent(a);
}; };
} }
function R(t, e, n) { function D(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 B = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 });
R.connected = function(t, e) { D.connected = function(t, e) {
return e = G(e), function(r) { return e = B(e), function(r) {
return r.addEventListener(g, t, e), r[x] ? r : r.isConnected ? (r.dispatchEvent(new Event(g)), r) : (N(e.signal, () => w.offConnected(r, t)) && w.onConnected(r, t), r); return r.addEventListener(v, t, e), r[x] ? r : r.isConnected ? (r.dispatchEvent(new Event(v)), r) : (L(e.signal, () => y.offConnected(r, t)) && y.onConnected(r, t), r);
}; };
}; };
R.disconnected = function(t, e) { D.disconnected = function(t, e) {
return e = G(e), function(r) { return e = B(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(w, t, e), r[x] || L(e.signal, () => y.offDisconnected(r, t)) && y.onDisconnected(r, t), r;
}; };
}; };
var W = /* @__PURE__ */ new WeakMap(); var j = /* @__PURE__ */ new WeakMap();
R.disconnectedAsAbort = function(t) { D.disconnectedAsAbort = function(t) {
if (W.has(t)) return W.get(t); if (j.has(t)) return j.get(t);
let e = new AbortController(); let e = new AbortController();
return W.set(t, e), t(R.disconnected(() => e.abort())), e; return j.set(t, e), t(D.disconnected(() => e.abort())), e;
}; };
var st = /* @__PURE__ */ new WeakSet(); var ot = /* @__PURE__ */ new WeakSet();
R.attributeChanged = function(t, e) { D.attributeChanged = function(t, e) {
return typeof e != "object" && (e = {}), function(r) { return typeof e != "object" && (e = {}), function(r) {
if (r.addEventListener(D, t, e), r[x] || st.has(r) || !a.M) return r; if (r.addEventListener(C, t, e), r[x] || ot.has(r) || !f.M) return r;
let o = new a.M(function(f) { let o = new f.M(function(a) {
for (let { attributeName: d, target: p } of f) for (let { attributeName: d, target: p } of a)
p.dispatchEvent( p.dispatchEvent(
new CustomEvent(D, { detail: [d, p.getAttribute(d)] }) new CustomEvent(C, { detail: [d, p.getAttribute(d)] })
); );
}); });
return N(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r; return L(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r;
}; };
}; };
export { export {
O as assign, R as assign,
z as assignAttribute, U as assignAttribute,
Q as chainableAppend, J as chainableAppend,
Y as classListDeclarative, K as classListDeclarative,
j as createElement, P as createElement,
gt as createElementNS, pt as createElementNS,
Dt as customElementRender, wt as customElementRender,
ct as customElementWithDDE, nt as customElementWithDDE,
_t as dispatchEvent, Ct as dispatchEvent,
j as el, P as el,
gt as elNS, pt as elNS,
tt as elementAttribute, Q as elementAttribute,
vt as empty, nt as lifecyclesToEvents,
ct as lifecyclesToEvents, rt as observedAttributes,
it as observedAttributes, D as on,
R as on, dt as queue,
V as registerReactivity, Z as registerReactivity,
S as scope, O as scope,
bt as simulateSlots lt as simulateSlots
}; };

View File

@ -7,7 +7,6 @@ export class HTMLCustomElement extends HTMLElement{
static observedAttributes= [ "attr" ]; static observedAttributes= [ "attr" ];
connectedCallback(){ connectedCallback(){
customElementRender( customElementRender(
this,
this.attachShadow({ mode: "open" }), this.attachShadow({ mode: "open" }),
ddeComponent ddeComponent
); );

View File

@ -2,7 +2,7 @@ import {
customElementRender, customElementRender,
customElementWithDDE, customElementWithDDE,
observedAttributes, observedAttributes,
el, on, scope el, on, scope,
} from "deka-dom-el"; } from "deka-dom-el";
import { S } from "deka-dom-el/signals"; import { S } from "deka-dom-el/signals";
export class HTMLCustomElement extends HTMLElement{ export class HTMLCustomElement extends HTMLElement{
@ -11,7 +11,6 @@ export class HTMLCustomElement extends HTMLElement{
connectedCallback(){ connectedCallback(){
console.log(observedAttributes(this)); console.log(observedAttributes(this));
customElementRender( customElementRender(
this,
this.attachShadow({ mode: "open" }), this.attachShadow({ mode: "open" }),
ddeComponent, ddeComponent,
S.observedAttributes S.observedAttributes
@ -24,7 +23,7 @@ export class HTMLCustomElement extends HTMLElement{
/** @param {{ attr: ddeSignal<string, {}> }} props */ /** @param {{ attr: ddeSignal<string, {}> }} props */
function ddeComponent({ attr }){ function ddeComponent({ attr }){
scope.host( scope.host(
on.connected(e=> console.log(e.target.outerHTML)), on.connected(e=> console.log(( /** @type {HTMLParagraphElement} */ (e.target)).outerHTML)),
); );
return el().append( return el().append(
el("p", S(()=> `Hello from Custom Element with attribute '${attr()}'`)) el("p", S(()=> `Hello from Custom Element with attribute '${attr()}'`))

View File

@ -17,11 +17,7 @@ function ddeComponent(){
export class A extends HTMLElement{ export class A extends HTMLElement{
static tagName= "custom-element-without"; static tagName= "custom-element-without";
connectedCallback(){ connectedCallback(){
customElementRender( customElementRender(this, ddeComponent);
this,
this,
ddeComponent
);
} }
} }
customElementWithDDE(A); customElementWithDDE(A);
@ -30,7 +26,6 @@ export class B extends HTMLElement{
static tagName= "custom-element-open"; static tagName= "custom-element-open";
connectedCallback(){ connectedCallback(){
customElementRender( customElementRender(
this,
this.attachShadow({ mode: "open" }), this.attachShadow({ mode: "open" }),
ddeComponent ddeComponent
); );
@ -42,7 +37,6 @@ export class C extends HTMLElement{
static tagName= "custom-element-closed"; static tagName= "custom-element-closed";
connectedCallback(){ connectedCallback(){
customElementRender( customElementRender(
this,
this.attachShadow({ mode: "closed" }), this.attachShadow({ mode: "closed" }),
ddeComponent ddeComponent
); );

View File

@ -8,7 +8,7 @@ export class HTMLCustomElement extends HTMLElement{
static tagName= "custom-slotting"; static tagName= "custom-slotting";
connectedCallback(){ connectedCallback(){
const c= ()=> simulateSlots(this, ddeComponent()); const c= ()=> simulateSlots(this, ddeComponent());
customElementRender(this, this, c); customElementRender(this, c);
} }
} }
customElementWithDDE(HTMLCustomElement); customElementWithDDE(HTMLCustomElement);

View File

@ -11,7 +11,7 @@ document.body.append(
); );
function component({ className, textContent }){ function component({ className, textContent }){
return el("div", { className: [ "class1", className ] }).append( return el("div", { className: [ "class1", className ].join(" ") }).append(
el("p", textContent) el("p", textContent)
); );
} }

View File

@ -2,8 +2,8 @@ import { elNS, assign } from "deka-dom-el";
const elSVG= elNS("http://www.w3.org/2000/svg"); const elSVG= elNS("http://www.w3.org/2000/svg");
const elMath= elNS("http://www.w3.org/1998/Math/MathML"); const elMath= elNS("http://www.w3.org/1998/Math/MathML");
document.body.append( document.body.append(
elSVG("svg"), // see https://developer.mozilla.org/en-US/docs/Web/SVG and https://developer.mozilla.org/en-US/docs/Web/API/SVGElement elSVG("svg"), // see https://developer.mozilla.org/en-US/docs/Web/SVG and https://developer.mozilla.org/en-US/docs/Web/API/SVGElement // editorconfig-checker-disable-line
elMath("math") // see https://developer.mozilla.org/en-US/docs/Web/MathML and https://developer.mozilla.org/en-US/docs/Web/MathML/Global_attributes elMath("math") // see https://developer.mozilla.org/en-US/docs/Web/MathML and https://developer.mozilla.org/en-US/docs/Web/MathML/Global_attributes // editorconfig-checker-disable-line
); );
console.log( console.log(

View File

@ -1,15 +1,36 @@
import { el } from "deka-dom-el"; import { el, on } from "deka-dom-el";
import { S } from "deka-dom-el/signals"; import { S } from "deka-dom-el/signals";
const clicks= S(0); // A
document.body.append( document.body.append(
el().append( el(HelloWorldComponent, { initial: "🚀" })
el("p", S(()=> );
"Hello World "+"🎉".repeat(clicks()) // B /** @typedef {"🎉" | "🚀"} Emoji */
)), /** @param {{ initial: Emoji }} attrs */
el("button", { function HelloWorldComponent({ initial }){
type: "button", const clicks= S(0);
onclick: ()=> clicks(clicks()+1), // C const emoji= S(initial);
textContent: "Fire", /** @param {HTMLOptionElement} el */
}) const isSelected= el=> (el.selected= el.value===initial);
// @ts-expect-error 2339: The <select> has only two options with {@link Emoji}
const onChange= on("change", event=> emoji(event.target.value));
return el().append(
el("p", {
textContent: S(() => `Hello World ${emoji().repeat(clicks())}`),
className: "example",
ariaLive: "polite", //OR ariaset: { live: "polite" },
dataset: { example: "Example" }, //OR dataExample: "Example",
}),
el("button",
{ textContent: "Fire", type: "button" },
on("click", ()=> clicks(clicks() + 1)),
on("keyup", ()=> clicks(clicks() - 2)),
),
el("select", null, onChange).append(
el(OptionComponent, "🎉", isSelected),//OR { textContent: "🎉" }
el(OptionComponent, "🚀", isSelected),//OR { textContent: "🚀" }
) )
); );
}
function OptionComponent({ textContent }){
return el("option", { value: textContent, textContent })
}

View File

@ -1,4 +1,6 @@
import { el, empty, on } from "deka-dom-el"; import { el, on } from "deka-dom-el";
/** @param {HTMLElement} el */
const empty= el=> Array.from(el.children).forEach(c=> c.remove());
document.body.append( document.body.append(
el(component), el(component),
el("button", { el("button", {

View File

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

View File

@ -4,25 +4,32 @@ import { mnemonicUl } from "../mnemonicUl.html.js";
export function mnemonic(){ export function mnemonic(){
return mnemonicUl().append( return mnemonicUl().append(
el("li").append( el("li").append(
el("code", "customElementRender(<custom-element>, <connect-target>, <render-function>[, <properties>])"), " — use function to render DOM structure for given <custom-element>", el("code", "customElementRender(<connect-target>, <render-function>[, <properties>])"),
" — use function to render DOM structure for given custom element (or its Shadow DOM)",
), ),
el("li").append( el("li").append(
el("code", "customElementWithDDE(<custom-element>)"), " — register <custom-element> to DDE library, see also `lifecyclesToEvents`, can be also used as decorator", el("code", "customElementWithDDE(<custom-element>)"),
" — register <custom-element> to DDE library, see also `lifecyclesToEvents`, can be also used as decorator",
), ),
el("li").append( el("li").append(
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", "S.observedAttributes(<custom-element>)"), " — returns record of observed attributes (keys uses camelCase and values are signals)", 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",
), ),
el("li").append( el("li").append(
el("code", "simulateSlots(<class-instance>, <body>[, <mapper>])"), " — simulate slots for Custom Elements without shadow DOM", el("code", "simulateSlots(<class-instance>, <body>)"),
" — simulate slots for Custom Elements without shadow DOM",
), ),
el("li").append( el("li").append(
el("code", "simulateSlots(<dde-component>[, <mapper>])"), " — simulate slots for “dde”/functional components", el("code", "simulateSlots(<dde-component>[, <mapper>])"),
" — simulate slots for “dde”/functional components",
), ),
); );
} }

View File

@ -4,22 +4,28 @@ import { mnemonicUl } from "../mnemonicUl.html.js";
export function mnemonic(){ export function mnemonic(){
return mnemonicUl().append( return mnemonicUl().append(
el("li").append( el("li").append(
el("code", "assign(<element>, ...<idl-objects>): <element>"), " — assign properties to the element", el("code", "assign(<element>, ...<objects>): <element>"),
" — assign properties (prefered, or attributes) to the element",
), ),
el("li").append( el("li").append(
el("code", "el(<tag-name>, <primitive>)[.append(...)]: <element-from-tag-name>"), " — simple element containing only text", el("code", "el(<tag-name>, <primitive>)[.append(...)]: <element-from-tag-name>"),
" — simple element containing only text",
), ),
el("li").append( el("li").append(
el("code", "el(<tag-name>, <idl-object>)[.append(...)]: <element-from-tag-name>"), " — element with more properties", el("code", "el(<tag-name>, <object>)[.append(...)]: <element-from-tag-name>"),
" — element with more properties (prefered, or attributes)",
), ),
el("li").append( el("li").append(
el("code", "el(<function>, <function-argument(s)>)[.append(...)]: <element-returned-by-function>"), " — using component represented by function", el("code", "el(<function>, <function-argument(s)>)[.append(...)]: <element-returned-by-function>"),
" — using component represented by function (must accept object like for <tag-name>)",
), ),
el("li").append( el("li").append(
el("code", "el(<...>, <...>, ...<addons>)"), " — see following page" el("code", "el(<...>, <...>, ...<addons>)"),
" — see following section of documentation",
), ),
el("li").append( el("li").append(
el("code", "elNS(<namespace>)(<as-el-see-above>)[.append(...)]: <element-based-on-arguments>"), " — typically SVG elements", el("code", "elNS(<namespace>)(<as-el-see-above>)[.append(...)]: <element-based-on-arguments>"),
" — typically SVG elements",
) )
); );
} }

View File

@ -4,17 +4,21 @@ import { mnemonicUl } from "../mnemonicUl.html.js";
export function mnemonic(){ export function mnemonic(){
return mnemonicUl().append( return mnemonicUl().append(
el("li").append( el("li").append(
el("code", "on(<event>, <listener>[, <options>])(<element>)"), " — just ", el("code", "<element>.addEventListener(<event>, <listener>[, <options>])") el("code", "on(<event>, <listener>[, <options>])(<element>)"),
" — just ", el("code", "<element>.addEventListener(<event>, <listener>[, <options>])")
), ),
el("li").append( el("li").append(
el("code", "on.<live-cycle>(<event>, <listener>[, <options>])(<element>)"), " — corresponds to custom elemnets callbacks ", el("code", "<live-cycle>Callback(...){...}"), el("code", "on.<live-cycle>(<event>, <listener>[, <options>])(<element>)"),
" — corresponds to custom elemnets callbacks ", el("code", "<live-cycle>Callback(...){...}"),
". To connect to custom element see following page, else it is simulated by MutationObserver." ". To connect to custom element see following page, else it is simulated by MutationObserver."
), ),
el("li").append( el("li").append(
el("code", "dispatchEvent(<event>[, <options>])(element)"), " — just ", el("code", "<element>.dispatchEvent(new Event(<event>[, <options>]))") el("code", "dispatchEvent(<event>[, <options>])(element)"),
" — just ", el("code", "<element>.dispatchEvent(new Event(<event>[, <options>]))")
), ),
el("li").append( el("li").append(
el("code", "dispatchEvent(<event>[, <options>])(element, detail)"), " — just ", el("code", "<element>.dispatchEvent(new CustomEvent(<event>, { detail, ...<options> }))") el("code", "dispatchEvent(<event>[, <options>])(element, detail)"),
" — just ", el("code", "<element>.dispatchEvent(new CustomEvent(<event>, { detail, ...<options> }))")
), ),
); );
} }

View File

@ -4,13 +4,16 @@ import { mnemonicUl } from "../mnemonicUl.html.js";
export function mnemonic(){ export function mnemonic(){
return mnemonicUl().append( return mnemonicUl().append(
el("li").append( el("li").append(
el("code", "el(<function>, <function-argument(s)>)[.append(...)]: <element-returned-by-function>"), " — using component represented by function", el("code", "el(<function>, <function-argument(s)>)[.append(...)]: <element-returned-by-function>"),
" — using component represented by function",
), ),
el("li").append( el("li").append(
el("code", "scope.host()"), " — get current component reference" el("code", "scope.host()"),
" — get current component reference"
), ),
el("li").append( el("li").append(
el("code", "scope.host(...<addons>)"), " — use addons to current component", el("code", "scope.host(...<addons>)"),
" — use addons to current component",
) )
); );
} }

View File

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

View File

@ -103,7 +103,7 @@ main{
[main-start] min(var(--body-max-width), 90%) [main-end] [main-start] min(var(--body-max-width), 90%) [main-end]
1fr [full-main-end]; 1fr [full-main-end];
} }
main > *{ main > *, main slot > *{
grid-column: main; grid-column: main;
} }
.icon { .icon {

View File

@ -36,7 +36,7 @@ export function page({ pkg, info }){
el(code, { src: fileURL("./components/examples/introducing/3ps.js"), page_id }), el(code, { src: fileURL("./components/examples/introducing/3ps.js"), page_id }),
el("p").append(...T` el("p").append(...T`
As we can see, in the code at location A we define ${el("em", t`how to react`)} when the function As we can see, in the code at location A we define ${el("em", t`how to react`)} when the function
is called with any event as an argument. At that moment, we ${el("em", t`don't care who/why/how`)} is called with any event as an argument. At that moment, we ${el("em", t`dont care who/why/how`)}
the function was called. Similarly, at point B, we reference to a function to be called on the event the function was called. Similarly, at point B, we reference to a function to be called on the event
${el("em", t`without caring`)} what the function will do at that time. Finally, at point C, we tell ${el("em", t`without caring`)} what the function will do at that time. Finally, at point C, we tell
the application that a change has occurred, in the input, and we ${el("em", t`don't care if/how someone`)} the application that a change has occurred, in the input, and we ${el("em", t`don't care if/how someone`)}

View File

@ -69,7 +69,7 @@ function metaFacebook({ name, description, homepage }){
function iconGitHub(){ function iconGitHub(){
const el= elNS("http://www.w3.org/2000/svg"); const el= elNS("http://www.w3.org/2000/svg");
return el("svg", { className: "icon", viewBox: "0 0 32 32" }).append( return el("svg", { className: "icon", viewBox: "0 0 32 32" }).append(
el("path", { d: [ //see https://svg-path-visualizer.netlify.app/#M16%200.395c-8.836%200-16%207.163-16%2016%200%207.069%204.585%2013.067%2010.942%2015.182%200.8%200.148%201.094-0.347%201.094-0.77%200-0.381-0.015-1.642-0.022-2.979-4.452%200.968-5.391-1.888-5.391-1.888-0.728-1.849-1.776-2.341-1.776-2.341-1.452-0.993%200.11-0.973%200.11-0.973%201.606%200.113%202.452%201.649%202.452%201.649%201.427%202.446%203.743%201.739%204.656%201.33%200.143-1.034%200.558-1.74%201.016-2.14-3.554-0.404-7.29-1.777-7.29-7.907%200-1.747%200.625-3.174%201.649-4.295-0.166-0.403-0.714-2.030%200.155-4.234%200%200%201.344-0.43%204.401%201.64%201.276-0.355%202.645-0.532%204.005-0.539%201.359%200.006%202.729%200.184%204.008%200.539%203.054-2.070%204.395-1.64%204.395-1.64%200.871%202.204%200.323%203.831%200.157%204.234%201.026%201.12%201.647%202.548%201.647%204.295%200%206.145-3.743%207.498-7.306%207.895%200.574%200.497%201.085%201.47%201.085%202.963%200%202.141-0.019%203.864-0.019%204.391%200%200.426%200.288%200.925%201.099%200.768%206.354-2.118%2010.933-8.113%2010.933-15.18%200-8.837-7.164-16-16-16z el("path", { d: [ //see https://svg-path-visualizer.netlify.app/#M16%200.395c-8.836%200-16%207.163-16%2016%200%207.069%204.585%2013.067%2010.942%2015.182%200.8%200.148%201.094-0.347%201.094-0.77%200-0.381-0.015-1.642-0.022-2.979-4.452%200.968-5.391-1.888-5.391-1.888-0.728-1.849-1.776-2.341-1.776-2.341-1.452-0.993%200.11-0.973%200.11-0.973%201.606%200.113%202.452%201.649%202.452%201.649%201.427%202.446%203.743%201.739%204.656%201.33%200.143-1.034%200.558-1.74%201.016-2.14-3.554-0.404-7.29-1.777-7.29-7.907%200-1.747%200.625-3.174%201.649-4.295-0.166-0.403-0.714-2.030%200.155-4.234%200%200%201.344-0.43%204.401%201.64%201.276-0.355%202.645-0.532%204.005-0.539%201.359%200.006%202.729%200.184%204.008%200.539%203.054-2.070%204.395-1.64%204.395-1.64%200.871%202.204%200.323%203.831%200.157%204.234%201.026%201.12%201.647%202.548%201.647%204.295%200%206.145-3.743%207.498-7.306%207.895%200.574%200.497%201.085%201.47%201.085%202.963%200%202.141-0.019%203.864-0.019%204.391%200%200.426%200.288%200.925%201.099%200.768%206.354-2.118%2010.933-8.113%2010.933-15.18%200-8.837-7.164-16-16-16z // editorconfig-checker-disable-line
"M 16,0.395", "M 16,0.395",
"c -8.836,0 -16,7.163 -16,16", "c -8.836,0 -16,7.163 -16,16",
"c 0,7.069 4.585,13.067 10.942,15.182", "c 0,7.069 4.585,13.067 10.942,15.182",

View File

@ -77,8 +77,9 @@ export function page({ pkg, info }){
el("p").append(...T` el("p").append(...T`
You can study all JavaScript elements interfaces to the corresponding HTML elements. All HTML elements You can study all JavaScript elements interfaces to the corresponding HTML elements. All HTML elements
inherits from ${el("a", { textContent: "HTMLElement", ...references.mdn_el })}. To see inherits from ${el("a", { textContent: "HTMLElement", ...references.mdn_el })}. To see
all available IDLs for example for paragraphs, see ${el("a", { textContent: "HTMLParagraphElement", ...references.mdn_p })}. all available IDLs for example for paragraphs, see ${el("a", { textContent: "HTMLParagraphElement",
Moreover, the ${el("code", "assign")} provides a way to sets declaratively some convenient properties: ...references.mdn_p })}. Moreover, the ${el("code", "assign")} provides a way to sets declaratively
some convenient properties:
`), `),
el("ul").append( el("ul").append(
el("li").append(...T` el("li").append(...T`
@ -91,8 +92,7 @@ export function page({ pkg, info }){
el("li").append(...T`You can use string or object as a value for ${el("code", "style")} property.`), el("li").append(...T`You can use string or object as a value for ${el("code", "style")} property.`),
el("li").append(...T` el("li").append(...T`
${el("code", "className")} (IDL preffered)/${el("code", "class")} are ways to add CSS classes ${el("code", "className")} (IDL preffered)/${el("code", "class")} are ways to add CSS classes
to the element. You can use string (similarly to ${el("code", "class=\"…\"")} syntax in HTML) or to the element. You can use string (similarly to ${el("code", "class=\"…\"")} syntax in HTML).
array of strings. This is handy to concat conditional classes.
`), `),
el("li").append(...T` el("li").append(...T`
Use ${el("code", "classList")} to toggle specific classes. This will be handy later when Use ${el("code", "classList")} to toggle specific classes. This will be handy later when

View File

@ -29,7 +29,7 @@ const references= {
}, },
/** Custom Element lifecycle callbacks */ /** Custom Element lifecycle callbacks */
mdn_customElement: { mdn_customElement: {
href: "https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks" href: "https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks" // editorconfig-checker-disable-line
}, },
/** MutationObserver */ /** MutationObserver */
mdn_mutation: { mdn_mutation: {
@ -105,17 +105,20 @@ export function page({ pkg, info }){
`), `),
el("p").append(...T` el("p").append(...T`
The library provide three additional live-cycle events corresponding to how they are named in a case of The library provide three additional live-cycle events corresponding to how they are named in a case of
custom elements: ${el("code", "on.connected")}, ${el("code", "on.disconnected")} and ${el("code", "on.attributeChanged")}. custom elements: ${el("code", "on.connected")}, ${el("code", "on.disconnected")} and ${el("code",
"on.attributeChanged")}.
`), `),
el(example, { src: fileURL("./components/examples/events/live-cycle.js"), page_id }), el(example, { src: fileURL("./components/examples/events/live-cycle.js"), page_id }),
el("p").append(...T` el("p").append(...T`
For Custom elements, we will later introduce a way to replace ${el("code", "*Callback")} syntax with For Custom elements, we will later introduce a way to replace ${el("code", "*Callback")} syntax with
${el("code", "dde:*")} events. The ${el("code", "on.*")} functions then listen to the appropriate ${el("code", "dde:*")} events. The ${el("code", "on.*")} functions then listen to the appropriate
Custom Elements events (see ${el("a", { textContent: t`Custom element lifecycle callbacks | MDN`, ...references.mdn_customElement })}). Custom Elements events (see ${el("a", { textContent: t`Custom element lifecycle callbacks | MDN`,
...references.mdn_customElement })}).
`), `),
el("p").append(...T` el("p").append(...T`
But, in case of regular elemnets the ${el("a", references.mdn_mutation).append(el("code", "MutationObserver"), " | MDN")} But, in case of regular elemnets the ${el("a", references.mdn_mutation).append(el("code",
is internaly used to track these events. Therefore, there are some drawbacks: "MutationObserver"), " | MDN")} is internaly used to track these events. Therefore, there are some
drawbacks:
`), `),
el("ul").append( el("ul").append(
el("li").append(...T` el("li").append(...T`
@ -136,7 +139,8 @@ export function page({ pkg, info }){
native behaviour re-(dis)connecting element, use: native behaviour re-(dis)connecting element, use:
`), `),
el("ul").append( el("ul").append(
el("li").append(...T`custom ${el("code", "MutationObserver")} or logic in (dis)${el("code", "connectedCallback")} or…`), el("li").append(...T`custom ${el("code", "MutationObserver")} or logic in (dis)${el("code",
"connectedCallback")} or`),
el("li").append(...T`re-add ${el("code", "on.connected")} or ${el("code", "on.disconnected")} listeners.`) el("li").append(...T`re-add ${el("code", "on.connected")} or ${el("code", "on.disconnected")} listeners.`)
), ),

View File

@ -21,7 +21,7 @@ const references= {
/** observedAttributes on MDN */ /** observedAttributes on MDN */
mdn_observedAttributes: { mdn_observedAttributes: {
title: t`MDN documentation page for observedAttributes`, title: t`MDN documentation page for observedAttributes`,
href: "https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#responding_to_attribute_changes", href: "https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#responding_to_attribute_changes", // editorconfig-checker-disable-line
}, },
/** Custom Elements on MDN */ /** Custom Elements on MDN */
mdn_custom_elements: { mdn_custom_elements: {
@ -55,10 +55,10 @@ export function page({ pkg, info }){
return el(simplePage, { info, pkg }).append( return el(simplePage, { info, pkg }).append(
el("h2", t`Using web components in combinantion with DDE`), el("h2", t`Using web components in combinantion with DDE`),
el("p").append(...T` el("p").append(...T`
The DDE library allows for use within ${el("a", references.mdn_web_components).append( el("strong", t`Web Components`) )} The DDE library allows for use within ${el("a", references.mdn_web_components).append( el("strong",
for dom-tree generation. However, in order to be able to use signals (possibly mapping to registered t`Web Components`) )} for dom-tree generation. However, in order to be able to use signals (possibly
${el("a", references.mdn_observedAttributes).append( el("code", "observedAttributes") )}) and additional mapping to registered ${el("a", references.mdn_observedAttributes).append( el("code", "observedAttributes")
functionality is (unfortunately) required to use helpers provided by the library. )}) and additional functionality is (unfortunately) required to use helpers provided by the library.
`), `),
el(code, { src: fileURL("./components/examples/customElement/intro.js"), page_id }), el(code, { src: fileURL("./components/examples/customElement/intro.js"), page_id }),
@ -82,8 +82,8 @@ export function page({ pkg, info }){
`), `),
el("p").append(...T` el("p").append(...T`
Also see the Life Cycle Events sections, very similarly we would like to use Also see the Life Cycle Events sections, very similarly we would like to use
${el("a", { textContent: t`DDE events`, href: "./p03-events.html", title: t`See events part of the library documentation` })}. ${el("a", { textContent: t`DDE events`, href: "./p03-events.html", title: t`See events part of the library
To do it, the library provides function ${el("code", "customElementWithDDE")} documentation` })}. To do it, the library provides function ${el("code", "customElementWithDDE")}
`), `),
el(example, { src: fileURL("./components/examples/customElement/customElementWithDDE.js"), page_id }), el(example, { src: fileURL("./components/examples/customElement/customElementWithDDE.js"), page_id }),

View File

@ -21,7 +21,8 @@ export function thirdParty(){
}); });
// Array.from((new URL(location)).searchParams.entries()) // Array.from((new URL(location)).searchParams.entries())
// .forEach(([ key, value ])=> S.action(store, "set", key, value)); // .forEach(([ key, value ])=> S.action(store, "set", key, value));
// S.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())

View File

@ -15,6 +15,7 @@ export function fullNameComponent(){
on.disconnected(()=> console.log(fullNameComponent)) on.disconnected(()=> console.log(fullNameComponent))
); );
const style= { height: "80px", display: "block", fill: "currentColor" };
const elSVG= elNS("http://www.w3.org/2000/svg"); const elSVG= elNS("http://www.w3.org/2000/svg");
return el("div", { className }).append( return el("div", { className }).append(
el("h2", "Simple form:"), el("h2", "Simple form:"),
@ -29,7 +30,7 @@ export function fullNameComponent(){
": ", ": ",
el("#text", full_name) el("#text", full_name)
), ),
elSVG("svg", { viewBox: "0 0 240 80", style: { height: "80px", display: "block" } }).append( elSVG("svg", { viewBox: "0 0 240 80", style }).append(
//elSVG("style", { }) //elSVG("style", { })
elSVG("text", { x: 20, y: 35, textContent: "Text" }), elSVG("text", { x: 20, y: 35, textContent: "Text" }),
) )

View File

@ -17,21 +17,24 @@ 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= S(new Map(), { const todosO= S( /** @type {Map<number, ddeSignal<string>>} */ (new Map()), {
/** @param {string} v */
add(v){ this.value.set(key++, S(v)); }, add(v){ this.value.set(key++, S(v)); },
/** @param {number} key */
remove(key){ S.clear(this.value.get(key)); this.value.delete(key); } remove(key){ S.clear(this.value.get(key)); this.value.delete(key); }
}); });
todos.forEach(text=> S.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_form= /** @type {HTMLFormElement} */ (event.target);
const el= /** @type {HTMLInputElement} */ (el_form.elements[name]);
event.preventDefault(); event.preventDefault();
S.action(todosO, "add", el.value); S.action(todosO, "add", el.value);
el.value= ""; el.value= "";
}); });
const onremove= on("remove", event=> const onremove= on("remove", /** @param {CustomEvent<number>} event */
S.action(todosO, "remove", event.detail)); event=> S.action(todosO, "remove", event.detail));
return el("div", { className }).append( return el("div", { className }).append(
el("div").append( el("div").append(
@ -60,19 +63,24 @@ export function todosComponent({ todos= [ "Task A" ] }= {}){
) )
} }
/** /**
* @param {{ textContent: ddeSignal<string>, value: ddeSignal<number> }} attrs
* @dispatchs {number} remove * @dispatchs {number} remove
* */ * */
function todoComponent({ textContent, value }){ function todoComponent({ textContent, value }){
const { host }= scope; const { host }= scope;
const dispatchRemove= /** @type {(data: number) => void} */
(dispatchEvent("remove", null, host));
const onclick= on("click", event=> { const onclick= on("click", event=> {
const value= Number(event.target.value); const el= /** @type {HTMLButtonElement} */ (event.target);
const value= Number(el.value);
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
dispatchEvent("remove")(host(), value); dispatchRemove(value);
}); });
const is_editable= S(false); const is_editable= S(false);
const onedited= on("change", ev=> { const onedited= on("change", ev=> {
textContent(ev.target.value); const el= /** @type {HTMLInputElement} */ (ev.target);
textContent(el.value);
is_editable(false); is_editable(false);
}); });
return el("li").append( return el("li").append(

View File

@ -12,7 +12,7 @@ export class CustomHTMLTestElement extends HTMLElement{
} }
connectedCallback(){ connectedCallback(){
if(!this.hasAttribute("pre-name")) this.setAttribute("pre-name", "default"); if(!this.hasAttribute("pre-name")) this.setAttribute("pre-name", "default");
customElementRender(this, this.attachShadow({ mode: "open" }), this.render, this.attributes) customElementRender(this.attachShadow({ mode: "open" }), this.render, this.attributes)
} }
attributes(element){ attributes(element){
@ -22,7 +22,7 @@ export class CustomHTMLTestElement extends HTMLElement{
render({ name, preName, test }){ render({ name, preName, test }){
console.log(scope.state); console.log(scope.state);
scope.host( scope.host(
on.connected(()=> console.log(CustomHTMLTestElement)), on.connected(console.log),
on.attributeChanged(e=> console.log(e)), on.attributeChanged(e=> console.log(e)),
on.disconnected(()=> console.log(CustomHTMLTestElement)) on.disconnected(()=> console.log(CustomHTMLTestElement))
); );
@ -34,7 +34,9 @@ export class CustomHTMLTestElement extends HTMLElement{
text(test), text(test),
text(name), text(name),
text(preName), text(preName),
el("button", { type: "button", textContent: "pre-name", onclick: ()=> preName("Ahoj") }) el("button", { type: "button", textContent: "pre-name", onclick: ()=> preName("Ahoj") }),
" | ",
el("slot", { className: "test", name: "test" }),
); );
} }
test= "A"; test= "A";
@ -61,7 +63,7 @@ export class CustomSlottingHTMLElement extends HTMLElement{
)); ));
} }
connectedCallback(){ connectedCallback(){
customElementRender(this, this, this.render); customElementRender(this, this.render);
} }
} }
customElementWithDDE(CustomSlottingHTMLElement); customElementWithDDE(CustomSlottingHTMLElement);

View File

@ -1,4 +1,9 @@
import { style, el, S } from './exports.js'; import { style, el, S } from './exports.js';
style.css`
:root{
color-scheme: dark light;
}
`;
document.head.append(style.element); document.head.append(style.element);
import { fullNameComponent } from './components/fullNameComponent.js'; import { fullNameComponent } from './components/fullNameComponent.js';
import { todosComponent } from './components/todosComponent.js'; import { todosComponent } from './components/todosComponent.js';
@ -10,7 +15,10 @@ document.body.append(
el("h1", "Experiments:"), el("h1", "Experiments:"),
el(fullNameComponent), el(fullNameComponent),
el(todosComponent), el(todosComponent),
el(CustomHTMLTestElement.tagName, { name: "attr" }), el(CustomHTMLTestElement.tagName, { name: "attr" }).append(
el("span", { textContent: "test", slot: "test" }),
el("span", { textContent: "test", slot: "test" }),
),
el(thirdParty), el(thirdParty),
el(CustomSlottingHTMLElement.tagName, { onclick: ()=> toggle(!toggle()) }).append( el(CustomSlottingHTMLElement.tagName, { onclick: ()=> toggle(!toggle()) }).append(
el("strong", { slot: "name", textContent: "Honzo" }), el("strong", { slot: "name", textContent: "Honzo" }),

124
index.d.ts vendored
View File

@ -8,57 +8,81 @@ type SupportedElement=
| CustomElementTagNameMap[keyof CustomElementTagNameMap] | CustomElementTagNameMap[keyof CustomElementTagNameMap]
declare global { declare global {
type ddeComponentAttributes= Record<any, any> | undefined; type ddeComponentAttributes= Record<any, any> | undefined;
type ddeElementAddon<El extends SupportedElement | DocumentFragment>= (element: El)=> El | void; type ddeElementAddon<El extends SupportedElement | DocumentFragment | Node>= (element: El)=> any;
type ddeString= string | ddeSignal<string>
type ddeStringable= ddeString | number | ddeSignal<number>
} }
type PascalCase= `${Capitalize<string>}${string}`; type PascalCase= `${Capitalize<string>}${string}`;
type AttrsModified= { 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> | ddeSignal<string> | Partial<{ [K in keyof CSSStyleDeclaration]: ddeSignal<CSSStyleDeclaration[K]> }> style: Partial<CSSStyleDeclaration> | ddeString
| Partial<{ [K in keyof CSSStyleDeclaration]: ddeSignal<CSSStyleDeclaration[K]> }>
/** /**
* 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|ddeSignal<-1|0|1|boolean>>, classList: Record<string,-1|0|1|boolean|ddeSignal<-1|0|1|boolean>>,
/** /**
* By default simiral to `className`, but also supports `string[]` * Used by the dataset HTML attribute to represent data for custom attributes added to elements.
* Values are converted to string (see {@link DOMStringMap}).
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMStringMap)
* */ * */
className: string | (string|boolean|undefined|ddeSignal<string|boolean|undefined>)[]; dataset: Record<string, ddeStringable>,
/** /**
* Sets `aria-*` simiraly to `dataset` * Sets `aria-*` simiraly to `dataset`
* */ * */
ariaset: Record<string,string|ddeSignal<string>>, ariaset: Record<string, ddeString>,
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|ddeSignal<string>> & Record<`.${string}`, any> } & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, ddeString>
& Record<`.${string}`, any>
type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>; type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>;
type IsReadonly<T, K extends keyof T> =
T extends { readonly [P in K]: T[K] } ? true : false;
/** /**
* Just element attributtes * Just element attributtes
* *
* In most cases, you can use native propertie such as [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)). * In most cases, you can use native propertie such as
* [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on
* (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)).
* *
* 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<{ [K in keyof _fromElsInterfaces<T>]: _fromElsInterfaces<T>[K] | ddeSignal<_fromElsInterfaces<T>[K]> } & AttrsModified> & Record<string, any>; type ElementAttributes<T extends SupportedElement>= Partial<{
export function classListDeclarative<El extends SupportedElement>(element: El, classList: AttrsModified["classList"]): El [K in keyof _fromElsInterfaces<T>]: IsReadonly<_fromElsInterfaces<T>, K> extends false
? _fromElsInterfaces<T>[K] | ddeSignal<_fromElsInterfaces<T>[K]>
: ddeStringable
} & AttrsModified> & Record<string, any>;
export function classListDeclarative<El extends SupportedElement>(
element: El,
classList: AttrsModified["classList"]
): El
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El export function 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 | ddeSignal<string>;
export function el< export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap, TAG extends keyof ExtendedHTMLElementTagNameMap,
EL extends ExtendedHTMLElementTagNameMap[TAG]
>( >(
tag_name: TAG, tag_name: TAG,
attrs?: ElementAttributes<EL> | textContent, attrs?: ElementAttributes<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]> | ddeStringable,
...addons: ddeElementAddon<EL>[] ...addons: ddeElementAddon<
ExtendedHTMLElementTagNameMap[NoInfer<TAG>]
>[], // TODO: for now addons must have the same element
): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement ): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement
export function el( export function el(
tag_name?: "<>", tag_name?: "<>",
): ddeDocumentFragment ): ddeDocumentFragment
export function el( export function el(
tag_name: string, tag_name: string,
attrs?: ElementAttributes<HTMLElement> | textContent, attrs?: ElementAttributes<HTMLElement> | ddeStringable,
...addons: ddeElementAddon<HTMLElement>[] ...addons: ddeElementAddon<HTMLElement>[]
): ddeHTMLElement ): ddeHTMLElement
@ -66,9 +90,11 @@ export function el<
C extends (attr: ddeComponentAttributes)=> SupportedElement | ddeDocumentFragment C extends (attr: ddeComponentAttributes)=> SupportedElement | ddeDocumentFragment
>( >(
component: C, component: C,
attrs?: Parameters<C>[0] | textContent, attrs?: Parameters<C>[0] | ddeStringable,
...addons: ddeElementAddon<ReturnType<C>>[] ...addons: ddeElementAddon<ReturnType<C>>[]
): ReturnType<C> extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? ReturnType<C> : ( ReturnType<C> extends ddeDocumentFragment ? ReturnType<C> : ddeHTMLElement ) ): ReturnType<C> extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap]
? ReturnType<C>
: ( ReturnType<C> extends ddeDocumentFragment ? ReturnType<C> : ddeHTMLElement )
export { el as createElement } export { el as createElement }
export function elNS( export function elNS(
@ -78,8 +104,8 @@ export function elNS(
EL extends ( TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement ), EL extends ( TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement ),
>( >(
tag_name: TAG, tag_name: TAG,
attrs?: ElementAttributes<EL> | textContent, attrs?: ElementAttributes<NoInfer<EL>> | ddeStringable,
...addons: ddeElementAddon<EL>[] ...addons: ddeElementAddon<NoInfer<EL>>[]
)=> TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement )=> TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement
export function elNS( export function elNS(
namespace: "http://www.w3.org/1998/Math/MathML" namespace: "http://www.w3.org/1998/Math/MathML"
@ -88,54 +114,60 @@ 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] | ddeSignal<EL[key]> | string | number | boolean }>, attrs?: ddeStringable | Partial<{
...addons: ddeElementAddon<EL>[] [key in keyof EL]: EL[key] | ddeSignal<EL[key]> | string | number | boolean
}>,
...addons: ddeElementAddon<NoInfer<EL>>[]
)=> ddeMathMLElement )=> ddeMathMLElement
export function elNS( export function elNS(
namespace: string namespace: string
): ( ): (
tag_name: string, tag_name: string,
attrs?: string | textContent | Record<string, any>, attrs?: string | ddeStringable | Record<string, any>,
...addons: ddeElementAddon<SupportedElement>[] ...addons: ddeElementAddon<SupportedElement>[]
)=> SupportedElement )=> SupportedElement
export { elNS as createElementNS } export { elNS as createElementNS }
export function chainableAppend<EL extends SupportedElement>(el: EL): EL; export function chainableAppend<EL extends SupportedElement>(el: EL): EL;
/**
* Mapper function (optional). Pass for coppying attributes, this is NOT implemented by {@link simulateSlots} itself!
* */
type simulateSlotsMapper= (body: HTMLSlotElement, el: HTMLElement)=> void;
/** Simulate slots for ddeComponents */ /** Simulate slots for ddeComponents */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(root: EL, mapper?: simulateSlotsMapper): EL export function simulateSlots<EL extends SupportedElement | DocumentFragment>(
root: EL,
): EL
/** /**
* Simulate slots in Custom Elements without using `shadowRoot`. * Simulate slots in Custom Elements without using `shadowRoot`.
* @param el Custom Element root element * @param el Custom Element root element
* @param body Body of the custom element * @param body Body of the custom element
* */ * */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(el: HTMLElement, body: EL, mapper?: simulateSlotsMapper): EL export function simulateSlots<EL extends SupportedElement | DocumentFragment>(
el: HTMLElement,
body: EL,
): EL
export function dispatchEvent(name: keyof DocumentEventMap | string, options?: EventInit): export function dispatchEvent(name: keyof DocumentEventMap | string, options?: EventInit):
(element: SupportedElement, data?: any)=> void; (element: SupportedElement, data?: any)=> void;
export function dispatchEvent(name: keyof DocumentEventMap | string, options: EventInit | null, element: SupportedElement | (()=> SupportedElement)): export function dispatchEvent(
(data?: any)=> void; name: keyof DocumentEventMap | string,
options: EventInit | null,
element: SupportedElement | (()=> SupportedElement)
): (data?: any)=> void;
interface On{ interface On{
/** Listens to the DOM event. See {@link Document.addEventListener} */ /** Listens to the DOM event. See {@link Document.addEventListener} */
< <
EE extends ddeElementAddon<SupportedElement>, Event extends keyof DocumentEventMap,
El extends ( EE extends ddeElementAddon<infer El> ? El : never ), EE extends ddeElementAddon<SupportedElement>= ddeElementAddon<HTMLElement>,
Event extends keyof DocumentEventMap>( >(
type: Event, type: Event,
listener: (this: El, ev: DocumentEventMap[Event]) => any, listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: DocumentEventMap[Event]) => any,
options?: AddEventListenerOptions options?: AddEventListenerOptions
) : EE; ) : EE;
< <
EE extends ddeElementAddon<SupportedElement>, EE extends ddeElementAddon<SupportedElement>= ddeElementAddon<HTMLElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never )>( >(
type: string, type: string,
listener: (this: El, ev: Event | CustomEvent ) => any, listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: Event | CustomEvent ) => any,
options?: AddEventListenerOptions options?: AddEventListenerOptions
) : EE; ) : EE;
/** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ /** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */// editorconfig-checker-disable-line
connected< connected<
EE extends ddeElementAddon<SupportedElement>, EE extends ddeElementAddon<SupportedElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never ) El extends ( EE extends ddeElementAddon<infer El> ? El : never )
@ -143,7 +175,7 @@ interface On{
listener: (this: El, event: CustomEvent<El>) => any, listener: (this: El, event: CustomEvent<El>) => any,
options?: AddEventListenerOptions options?: AddEventListenerOptions
) : EE; ) : EE;
/** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ /** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */// editorconfig-checker-disable-line
disconnected< disconnected<
EE extends ddeElementAddon<SupportedElement>, EE extends ddeElementAddon<SupportedElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never ) El extends ( EE extends ddeElementAddon<infer El> ? El : never )
@ -151,7 +183,7 @@ interface On{
listener: (this: El, event: CustomEvent<void>) => any, listener: (this: El, event: CustomEvent<void>) => any,
options?: AddEventListenerOptions options?: AddEventListenerOptions
) : EE; ) : EE;
/** Listens to the element attribute changes. In case of custom elements uses [`attributeChangedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ /** Listens to the element attribute changes. In case of custom elements uses [`attributeChangedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */// editorconfig-checker-disable-line
attributeChanged< attributeChanged<
EE extends ddeElementAddon<SupportedElement>, EE extends ddeElementAddon<SupportedElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never ) El extends ( EE extends ddeElementAddon<infer El> ? El : never )
@ -162,7 +194,12 @@ interface On{
} }
export const on: On; export const on: On;
type Scope= { scope: Node | Function | Object, host: ddeElementAddon<any>, custom_element: false | HTMLElement, prevent: boolean } type Scope= {
scope: Node | Function | Object,
host: ddeElementAddon<any>,
custom_element: false | HTMLElement,
prevent: boolean
};
/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */ /** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */
export const scope: { export const scope: {
current: Scope, current: Scope,
@ -190,7 +227,6 @@ export function customElementRender<
EL extends HTMLElement, EL extends HTMLElement,
P extends any = Record<string, string | ddeSignal<string>> P extends any = Record<string, string | ddeSignal<string>>
>( >(
custom_element: EL,
target: ShadowRoot | EL, target: ShadowRoot | EL,
render: (props: P)=> SupportedElement | DocumentFragment, render: (props: P)=> SupportedElement | DocumentFragment,
props?: P | ((el: EL)=> P) props?: P | ((el: EL)=> P)
@ -350,6 +386,7 @@ declare global{
} }
} }
// editorconfig-checker-disable
interface ddeHTMLAnchorElement extends HTMLAnchorElement{ append: ddeAppend<ddeHTMLAnchorElement>; } interface ddeHTMLAnchorElement extends HTMLAnchorElement{ append: ddeAppend<ddeHTMLAnchorElement>; }
interface ddeHTMLAreaElement extends HTMLAreaElement{ append: ddeAppend<ddeHTMLAreaElement>; } interface ddeHTMLAreaElement extends HTMLAreaElement{ append: ddeAppend<ddeHTMLAreaElement>; }
interface ddeHTMLAudioElement extends HTMLAudioElement{ append: ddeAppend<ddeHTMLAudioElement>; } interface ddeHTMLAudioElement extends HTMLAudioElement{ append: ddeAppend<ddeHTMLAudioElement>; }
@ -477,3 +514,4 @@ interface ddeSVGTitleElement extends SVGTitleElement{ append: ddeAppend<ddeSVGTi
interface ddeSVGTSpanElement extends SVGTSpanElement{ append: ddeAppend<ddeSVGTSpanElement>; } interface ddeSVGTSpanElement extends SVGTSpanElement{ append: ddeAppend<ddeSVGTSpanElement>; }
interface ddeSVGUseElement extends SVGUseElement{ append: ddeAppend<ddeSVGUseElement>; } interface ddeSVGUseElement extends SVGUseElement{ append: ddeAppend<ddeSVGUseElement>; }
interface ddeSVGViewElement extends SVGViewElement{ append: ddeAppend<ddeSVGViewElement>; } interface ddeSVGViewElement extends SVGViewElement{ append: ddeAppend<ddeSVGViewElement>; }
// editorconfig-checker-enable

View File

@ -1,6 +1,14 @@
//TODO: https://www.npmjs.com/package/html-element //TODO: https://www.npmjs.com/package/html-element
import { enviroment as env } from './src/dom-common.js'; import { enviroment as env } from './src/dom-common.js';
env.ssr= " ssr"; env.ssr= " ssr";
const q_store= new Set();
env.q= function(promise){
if(promise) return ( q_store.add(promise), promise );
return Promise.allSettled(Array.from(q_store)).then(()=> q_store.clear());
};
export const queue= env.q;
const { setDeleteAttr }= env; const { setDeleteAttr }= env;
/** @param {HTMLElement} obj */ /** @param {HTMLElement} obj */
env.setDeleteAttr= function(obj, prop, value){ env.setDeleteAttr= function(obj, prop, value){

19
package-lock.json generated
View File

@ -11,6 +11,7 @@
"devDependencies": { "devDependencies": {
"@size-limit/preset-small-lib": "~11.0", "@size-limit/preset-small-lib": "~11.0",
"dts-bundler": "~0.1", "dts-bundler": "~0.1",
"editorconfig-checker": "^6.0.0",
"esbuild": "~0.24", "esbuild": "~0.24",
"jsdom": "~25.0", "jsdom": "~25.0",
"jshint": "~2.13", "jshint": "~2.13",
@ -1424,6 +1425,24 @@
"typescript": "^2.4.0" "typescript": "^2.4.0"
} }
}, },
"node_modules/editorconfig-checker": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/editorconfig-checker/-/editorconfig-checker-6.0.0.tgz",
"integrity": "sha512-uyTOwLJzR/k7ugiu7ITjCzkLKBhXeirQZ8hGlUkt1u/hq2Qu1E8EslgFZDN+lxZoQc97eiI87sUFgVILK4P+YQ==",
"dev": true,
"license": "MIT",
"bin": {
"ec": "dist/index.js",
"editorconfig-checker": "dist/index.js"
},
"engines": {
"node": ">=20.11.0"
},
"funding": {
"type": "buymeacoffee",
"url": "https://www.buymeacoffee.com/mstruebing"
}
},
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",

View File

@ -83,9 +83,7 @@
"modifyEsbuildConfig": { "modifyEsbuildConfig": {
"platform": "browser" "platform": "browser"
}, },
"scripts": { "scripts": {},
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [ "keywords": [
"dom", "dom",
"javascript", "javascript",
@ -95,6 +93,7 @@
"devDependencies": { "devDependencies": {
"@size-limit/preset-small-lib": "~11.0", "@size-limit/preset-small-lib": "~11.0",
"dts-bundler": "~0.1", "dts-bundler": "~0.1",
"editorconfig-checker": "~6.0",
"esbuild": "~0.24", "esbuild": "~0.24",
"jsdom": "~25.0", "jsdom": "~25.0",
"jshint": "~2.13", "jshint": "~2.13",

View File

@ -1,7 +1,8 @@
import { keyLTE, evc, evd, eva } from "./dom-common.js"; import { keyLTE, evc, evd, eva } from "./dom-common.js";
import { scope } from "./dom.js"; import { scope } from "./dom.js";
import { c_ch_o } from "./events-observer.js"; import { c_ch_o } from "./events-observer.js";
export function customElementRender(custom_element, target, render, props= observedAttributes){ export function customElementRender(target, render, props= observedAttributes){
const custom_element= target.host || target;
scope.push({ scope.push({
scope: custom_element, scope: custom_element,
host: (...c)=> c.length ? c.forEach(c=> c(custom_element)) : custom_element host: (...c)=> c.length ? c.forEach(c=> c(custom_element)) : custom_element

View File

@ -6,6 +6,7 @@ export const enviroment= {
H: globalThis.HTMLElement, H: globalThis.HTMLElement,
S: globalThis.SVGElement, S: globalThis.SVGElement,
M: globalThis.MutationObserver, M: globalThis.MutationObserver,
q: p=> p || Promise.resolve(),
}; };
import { isUndef } from './helpers.js'; import { isUndef } from './helpers.js';
function setDeleteAttr(obj, prop, val){ function setDeleteAttr(obj, prop, val){
@ -15,7 +16,7 @@ function setDeleteAttr(obj, prop, val){
Options: Options:
1. Leave it, as it is native behaviour 1. Leave it, as it is native behaviour
2. Sets as empty string and removes the corresponding attribute when also has empty string 2. Sets as empty string and removes the corresponding attribute when also has empty string
*3. Sets as undefined and removes the corresponding attribute when "undefined" string discovered 3. (*) Sets as undefined and removes the corresponding attribute when "undefined" string discovered
4. Point 2. with checks for coincidence (e.g. use special string) 4. Point 2. with checks for coincidence (e.g. use special string)
*/ */
Reflect.set(obj, prop, val); Reflect.set(obj, prop, val);

View File

@ -1,6 +1,8 @@
import { signals } from "./signals-common.js"; import { signals } from "./signals-common.js";
import { enviroment as env } from './dom-common.js'; import { enviroment as env } from './dom-common.js';
//TODO: add type, docs ≡ make it public
export function queue(promise){ return env.q(promise); }
/** @type {{ scope: object, prevent: boolean, host: function }[]} */ /** @type {{ scope: object, prevent: boolean, host: function }[]} */
const scopes= [ { const scopes= [ {
get scope(){ return env.D.body; }, get scope(){ return env.D.body; },
@ -25,22 +27,25 @@ export const scope= {
return scopes.pop(); return scopes.pop();
}, },
}; };
// following chainableAppend implementation is OK as the ElementPrototype.append description already is { writable: true, enumerable: true, configurable: true } //NOTE: following chainableAppend implementation is OK as the ElementPrototype.append description already is { writable: true, enumerable: true, configurable: true } // editorconfig-checker-disable-line
function append(...els){ this.appendOriginal(...els); return this; } function append(...els){ this.appendOriginal(...els); return this; }
export function chainableAppend(el){ if(el.append===append) return el; el.appendOriginal= el.append; el.append= append; return el; } export function chainableAppend(el){
if(el.append===append) return el; el.appendOriginal= el.append; el.append= append; return el;
}
let namespace; let namespace;
export function createElement(tag, attributes, ...addons){ export function createElement(tag, attributes, ...addons){
/* jshint maxcomplexity: 15 */ /* jshint maxcomplexity: 15 */
const s= signals(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)
if(Object(attributes)!==attributes || s.isSignal(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": {
scoped= 1; scoped= 1;
scope.push({ scope: tag, host: (...c)=> c.length ? (scoped===1 ? addons.unshift(...c) : c.forEach(c=> c(el_host)), undefined) : el_host }); const host= (...c)=> !c.length ? el_host :
(scoped===1 ? addons.unshift(...c) : c.forEach(c=> c(el_host)), undefined);
scope.push({ scope: tag, host });
el= tag(attributes || undefined); el= tag(attributes || undefined);
const is_fragment= el instanceof env.F; const is_fragment= el instanceof env.F;
if(el.nodeName==="#comment") break; if(el.nodeName==="#comment") break;
@ -65,52 +70,6 @@ export function createElement(tag, attributes, ...addons){
scoped= 2; scoped= 2;
return el; return el;
} }
import { hasOwn } from "./helpers.js";
/** @param {HTMLElement} element @param {HTMLElement} [root] */
export function simulateSlots(element, root, mapper){
if(typeof root!=="object"){
mapper= root;
root= element;
}
const _default= Symbol.for("default");
const slots= Array.from(root.querySelectorAll("slot"))
.reduce((out, curr)=> Reflect.set(out, curr.name || _default, curr) && out, {});
const has_d= hasOwn(slots, _default);
element.append= new Proxy(element.append, {
apply(orig, _, els){
if(els[0]===root) return orig.apply(element, els);
if(!els.length) return element;
const d= env.D.createDocumentFragment();
for(const el of els){
if(!el || !el.slot){ if(has_d) d.append(el); continue; }
const name= el.slot;
const slot= slots[name];
elementAttribute(el, "remove", "slot");
if(!slot) continue;
simulateSlotReplace(slot, el, mapper);
Reflect.deleteProperty(slots, name);
}
if(has_d){
slots[_default].replaceWith(d);
Reflect.deleteProperty(slots, _default);
}
element.append= orig; //TODO: better memory management, but non-native behavior!
return element;
}
});
if(element!==root){
const els= Array.from(element.childNodes);
els.forEach(el=> el.remove());
element.append(...els);
}
return root;
}
function simulateSlotReplace(slot, element, mapper){
if(mapper) mapper(slot, element);
try{ slot.replaceWith(assign(element, { className: [ element.className, slot.className ], dataset: { ...slot.dataset } })); }
catch(_){ slot.replaceWith(element); }
}
/** /**
* @param { { type: "component", name: string, host: "this" | "parentElement" } | { type: "reactive" | "later" } } attrs * @param { { type: "component", name: string, host: "this" | "parentElement" } | { type: "reactive" | "later" } } attrs
* @param {boolean} [is_open=false] * @param {boolean} [is_open=false]
@ -123,8 +82,7 @@ createElement.mark= function(attrs, is_open= false){
return out; return out;
}; };
export { createElement as el }; export { createElement as el };
//TODO?: const namespaceHelper= ns=> ns==="svg" ? "http://www.w3.org/2000/svg" : ns;
//const namespaceHelper= ns=> ns==="svg" ? "http://www.w3.org/2000/svg" : ns;
export function createElementNS(ns){ export function createElementNS(ns){
const _this= this; const _this= this;
return function createElementNSCurried(...rest){ return function createElementNSCurried(...rest){
@ -136,6 +94,40 @@ export function createElementNS(ns){
} }
export { createElementNS as elNS }; export { createElementNS as elNS };
/** @param {HTMLElement} element @param {HTMLElement} [root] */
export function simulateSlots(element, root= element){
const mark_e= "¹⁰", mark_s= "✓"; //NOTE: Markers to identify slots processed by this function. Also “prevents” native behavior as it is unlikely to use these in names. // editorconfig-checker-disable-line
const slots= Object.fromEntries(
Array.from(root.querySelectorAll("slot"))
.filter(s => !s.name.endsWith(mark_e))
.map(s => [(s.name += mark_e), s]));
element.append= new Proxy(element.append, {
apply(orig, _, els){
if(els[0]===root) return orig.apply(element, els);
for(const el of els){
const name= (el.slot||"")+mark_e;
try{ elementAttribute(el, "remove", "slot"); } catch(_error){}
const slot= slots[name];
if(!slot) return;
if(!slot.name.startsWith(mark_s)){
slot.childNodes.forEach(c=> c.remove());
slot.name= mark_s+name;
}
slot.append(el);
//TODO?: el.dispatchEvent(new CustomEvent("dde:slotchange", { detail: slot }));
}
element.append= orig; //TODO?: better memory management, but non-native behavior!
return element;
}
});
if(element!==root){
const els= Array.from(element.childNodes);
//TODO?: els.forEach(el=> el.remove());
element.append(...els);
}
return root;
}
const assign_context= new WeakMap(); const assign_context= new WeakMap();
const { setDeleteAttr }= env; const { setDeleteAttr }= env;
export function assign(element, ...attributes){ export function assign(element, ...attributes){
@ -160,11 +152,11 @@ export function assignAttribute(element, key, value){
key= key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(); key= key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
return setRemoveAttr(key, value); return setRemoveAttr(key, value);
} }
if("className"===key) key= "class";//just optimalization, `isPropSetter` returns false immediately if("className"===key) key= "class";//NOTE: just optimalization, this makes `isPropSetter` returns false immediately // editorconfig-checker-disable-line
switch(key){ switch(key){
case "xlink:href": case "xlink:href":
return setRemoveAttr(key, value, "http://www.w3.org/1999/xlink"); return setRemoveAttr(key, value, "http://www.w3.org/1999/xlink");
case "textContent": //just optimalization, its part of Node ⇒ deep for `isPropSetter` case "textContent": //NOTE: just optimalization, this makes `isPropSetter` returns false immediately (as its part of Node ⇒ deep for `isPropSetter`) // editorconfig-checker-disable-line
return setDeleteAttr(element, key, value); return setDeleteAttr(element, key, value);
case "style": case "style":
if(typeof value!=="object") break; if(typeof value!=="object") break;
@ -192,17 +184,13 @@ export function classListDeclarative(element, toggle){
element.classList.toggle(class_name, val===-1 ? undefined : Boolean(val))); element.classList.toggle(class_name, val===-1 ? undefined : Boolean(val)));
return element; return element;
} }
export function empty(el){
Array.from(el.children).forEach(el=> el.remove());
return el;
}
export function elementAttribute(element, op, key, value){ export function elementAttribute(element, op, key, value){
if(element instanceof env.H) if(element instanceof env.H)
return element[op+"Attribute"](key, value); return element[op+"Attribute"](key, value);
return element[op+"AttributeNS"](null, key, value); return element[op+"AttributeNS"](null, key, value);
} }
import { isUndef } from "./helpers.js"; import { isUndef } from "./helpers.js";
//TODO add cache? `(Map/Set)<el.tagName+key,isUndef>` //TODO: add cache? `(Map/Set)<el.tagName+key,isUndef>`
function isPropSetter(el, key){ function isPropSetter(el, key){
if(!(key in el)) return false; if(!(key in el)) return false;
const des= getPropDescriptor(el, key); const des= getPropDescriptor(el, key);
@ -216,7 +204,9 @@ function getPropDescriptor(p, key){
return des; return des;
} }
/** @template {Record<any, any>} T @param {object} s @param {T} obj @param {(param: [ keyof T, T[keyof T] ])=> void} cb */ /**
* @template {Record<any, any>} T @param {object} s @param {T} obj @param {(param: [ keyof T, T[keyof T] ])=> void} cb
* */
function forEachEntries(s, obj, cb){ function forEachEntries(s, obj, cb){
if(typeof obj !== "object" || obj===null) return; if(typeof obj !== "object" || obj===null) return;
return Object.entries(obj).forEach(function process([ key, val ]){ return Object.entries(obj).forEach(function process([ key, val ]){
@ -226,7 +216,9 @@ function forEachEntries(s, obj, cb){
}); });
} }
function attrArrToStr(attr){ return Array.isArray(attr) ? attr.filter(Boolean).join(" ") : attr; } function setRemove(obj, prop, key, val){
function setRemove(obj, prop, key, val){ return obj[ (isUndef(val) ? "remove" : "set") + prop ](key, attrArrToStr(val)); } return obj[ (isUndef(val) ? "remove" : "set") + prop ](key, val); }
function setRemoveNS(obj, prop, key, val, ns= null){ return obj[ (isUndef(val) ? "remove" : "set") + prop + "NS" ](ns, key, attrArrToStr(val)); } function setRemoveNS(obj, prop, key, val, ns= null){
function setDelete(obj, key, val){ Reflect.set(obj, key, val); if(!isUndef(val)) return; return Reflect.deleteProperty(obj, key); } return obj[ (isUndef(val) ? "remove" : "set") + prop + "NS" ](ns, key, val); }
function setDelete(obj, key, val){
Reflect.set(obj, key, val); if(!isUndef(val)) return; return Reflect.deleteProperty(obj, key); }

View File

@ -82,12 +82,12 @@ function connectionsChangesObserverConstructor(){
is_observing= false; is_observing= false;
observer.disconnect(); observer.disconnect();
} }
//TODO remount support? //TODO: remount support?
function requestIdle(){ return new Promise(function(resolve){ function requestIdle(){ return new Promise(function(resolve){
(requestIdleCallback || requestAnimationFrame)(resolve); (requestIdleCallback || requestAnimationFrame)(resolve);
}); } }); }
async function collectChildren(element){ async function collectChildren(element){
if(store.size > 30)//TODO limit? if(store.size > 30)//TODO?: limit
await requestIdle(); await requestIdle();
const out= []; const out= [];
if(!(element instanceof Node)) return out; if(!(element instanceof Node)) return out;

View File

@ -8,6 +8,7 @@ export function dispatchEvent(name, options, host){
d.unshift(element); d.unshift(element);
element= typeof host==="function"? host() : host; element= typeof host==="function"? host() : host;
} }
//TODO: what about re-emmitting?
const event= d.length ? new CustomEvent(name, Object.assign({ detail: d[0] }, options)) : new Event(name, options); const event= d.length ? new CustomEvent(name, Object.assign({ detail: d[0] }, options)) : new Event(name, options);
return element.dispatchEvent(event); return element.dispatchEvent(event);
}; };

View File

@ -58,7 +58,7 @@ signal.on= function on(s, listener, options= {}){
if(Array.isArray(s)) return s.forEach(s=> on(s, listener, options)); if(Array.isArray(s)) return s.forEach(s=> on(s, listener, options));
addSignalListener(s, listener); addSignalListener(s, listener);
if(as) as.addEventListener("abort", ()=> removeSignalListener(s, listener)); if(as) as.addEventListener("abort", ()=> removeSignalListener(s, listener));
//TODO cleanup when signal removed //TODO: cleanup when signal removed
}; };
signal.symbols= { signal.symbols= {
//signal: mark, //signal: mark,

View File

@ -58,7 +58,7 @@ signal.on= function on(s, listener, options= {}){
if(Array.isArray(s)) return s.forEach(s=> on(s, listener, options)); if(Array.isArray(s)) return s.forEach(s=> on(s, listener, options));
addSignalListener(s, listener); addSignalListener(s, listener);
if(as) as.addEventListener("abort", ()=> removeSignalListener(s, listener)); if(as) as.addEventListener("abort", ()=> removeSignalListener(s, listener));
//TODO cleanup when signal removed //TODO: cleanup when signal removed
}; };
signal.symbols= { signal.symbols= {
//signal: mark, //signal: mark,