From 576b33921bc553a5babfa3cfd2f6f8214e98db81 Mon Sep 17 00:00:00 2001 From: Jan Andrle Date: Fri, 22 Nov 2024 16:26:42 +0100 Subject: [PATCH] :tada: :zap: --- README.md | 36 +++++--- bs/README.md | 17 +++- bs/build.js | 9 +- bs/docs.js | 2 +- bs/docs/jsdom.js | 2 +- bs/lint.sh | 2 + docs/components/code.html.js | 4 +- docs/components/example.html.js | 4 +- .../customElement/observedAttributes.js | 2 +- docs/components/examples/elements/dekaElNS.js | 4 +- .../examples/scopes/class-component.js | 2 +- docs/components/examples/scopes/imperative.js | 2 +- .../components/mnemonic/customElement-init.js | 21 +++-- docs/components/mnemonic/elements-init.js | 18 ++-- docs/components/mnemonic/events-init.js | 12 ++- docs/components/mnemonic/scopes-init.js | 9 +- docs/components/mnemonic/signals-init.js | 18 ++-- docs/index.html.js | 4 +- docs/layout/head.html.js | 2 +- docs/p02-elements.html.js | 15 ++-- docs/p03-events.html.js | 22 +++-- docs/p04-signals.html.js | 8 +- docs/p05-scopes.html.js | 2 +- docs/p06-customElement.html.js | 14 ++-- examples/components/3rd-party.js | 3 +- examples/components/fullNameComponent.js | 3 +- examples/components/todosComponent.js | 24 ++++-- examples/components/webComponent.js | 2 +- examples/index.js | 5 ++ index.d.ts | 83 +++++++++++++------ jsdom.js | 2 +- package-lock.json | 19 +++++ package.json | 5 +- signals.d.ts | 2 +- src/dom-common.js | 10 +-- src/dom.js | 35 +++++--- src/events-observer.js | 6 +- src/events.js | 6 +- src/observables-lib.js | 6 +- src/signals-lib.js | 6 +- tsconfig.json | 10 +-- 41 files changed, 300 insertions(+), 158 deletions(-) diff --git a/README.md b/README.md index e3bda25..6244bc4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -**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*** @@ -10,11 +12,14 @@ document.body.append( 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("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("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)) @@ -35,18 +40,22 @@ function component({ textContent, className }){ } ``` # Deka DOM Elements -Creating reactive elements, components and Web components using [IDL](https://developer.mozilla.org/en-US/docs/Glossary/IDL)/JavaScript DOM API and [**signals/observables**](#signals). +Creating reactive elements, components and Web components using [IDL](https://developer.mozilla.org/en-US/docs/ +Glossary/IDL)/JavaScript DOM API and [**signals/observables**](#signals). ## Inspiration and suggested alternatives -- my previous library (mostly used internaly): [jaandrle/dollar_dom_component: Functional DOM components without JSX and virtual DOM.](https://github.com/jaandrle/dollar_dom_component) -- [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) +- 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) +- [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) -- [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) ## Why an another one? -This library falls somewhere between van/hyperscript and [solid-js](https://github.com/solidjs/solid) in terms of size, complexity, -and usability. +This library falls somewhere between van/hyperscript and [solid-js](https://github.com/solidjs/solid) in terms of size, +complexity, and usability. 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 @@ -74,7 +83,10 @@ To balance these requirements, numerous compromises have been made. To summarize - [dist/](dist/) (`https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/`…) ## Signals -- [Signals — whats going on behind the scenes | by Ryan Hoffnan | ITNEXT](https://itnext.io/signals-whats-going-on-behind-the-scenes-ec858589ea63) -- [The Evolution of Signals in JavaScript - DEV Community](https://dev.to/this-is-learning/the-evolution-of-signals-in-javascript-8ob) -- there is also [tc39/proposal-signals: A proposal to add signals to JavaScript.](https://github.com/tc39/proposal-signals) +- [Signals — whats going on behind the scenes | by Ryan Hoffnan | ITNEXT](https://itnext.io/ +signals-whats-going-on-behind-the-scenes-ec858589ea63) +- [The Evolution of Signals in JavaScript - DEV Community](https://dev.to/this-is-learning/the-evolution-of-signals-in- +javascript-8ob) +- there is also [tc39/proposal-signals: A proposal to add signals to JavaScript.](https://github.com/tc39/proposal- +signals) - [Observer pattern - Wikipedia](https://en.wikipedia.org/wiki/Observer_pattern) diff --git a/bs/README.md b/bs/README.md index 117df92..33ac7c4 100644 --- a/bs/README.md +++ b/bs/README.md @@ -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` diff --git a/bs/build.js b/bs/build.js index 5b63582..332b7ba 100755 --- a/bs/build.js +++ b/bs/build.js @@ -34,7 +34,7 @@ $.api("", true) const file_dts_out= filesOut(file_dts); echoVariant(file_dts_out); s.echo(bundleDTS(file_dts)).to(file_dts_out); - + await toDDE(out, file_root); } $.exit(0); @@ -43,10 +43,13 @@ $.api("", true) const name= "dde"; const out= filesOut(file_root+".js", name); echoVariant(`${out} (${file} → globalThis.${name})`) - + let content= s.cat(file).toString().split(/export ?{/); content.splice(1, 0, `\nglobalThis.${name}= {`); - content[2]= content[2].replace(/,(?!\n)/g, ",\n").replace(/(? {", diff --git a/bs/docs.js b/bs/docs.js index 852210a..27b2822 100755 --- a/bs/docs.js +++ b/bs/docs.js @@ -1,5 +1,5 @@ #!/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("Preparing…"); import { path_target, pages as pages_registered, styles, dispatchEvent, t } from "../docs/ssr.js"; diff --git a/bs/docs/jsdom.js b/bs/docs/jsdom.js index bad3f28..dfdaf23 100644 --- a/bs/docs/jsdom.js +++ b/bs/docs/jsdom.js @@ -10,7 +10,7 @@ export function createHTMl(html, options= {}){ if(dom) cleanHTML(); // set a default url if we don't get one - otherwise things explode when we copy localstorage keys if (!('url' in options)) { Object.assign(options, { url: 'http://localhost:3000' }) } - + dom= new JSDOM(html, options); const window= dom.window; return { diff --git a/bs/lint.sh b/bs/lint.sh index 61ce1d1..91af7ce 100755 --- a/bs/lint.sh +++ b/bs/lint.sh @@ -1,3 +1,5 @@ #!/usr/bin/env bash +set -eou pipefail +npx editorconfig-checker -format gcc npx size-limit npx jshint index.js src diff --git a/docs/components/code.html.js b/docs/components/code.html.js index 19ca3b2..9e47c6a 100644 --- a/docs/components/code.html.js +++ b/docs/components/code.html.js @@ -51,9 +51,9 @@ let is_registered= {}; /** @param {string} page_id */ function registerClientPart(page_id){ if(is_registered[page_id]) return; - + document.head.append( - el("script", { src: "https://cdn.jsdelivr.net/npm/shiki@0.9", defer: true }), + el("script", { src: "https://cdn.jsdelivr.net/npm/shiki@0.9", defer: true }), ); registerClientFile( new URL("./code.js.js", import.meta.url), diff --git a/docs/components/example.html.js b/docs/components/example.html.js index c049edc..4eb8e6d 100644 --- a/docs/components/example.html.js +++ b/docs/components/example.html.js @@ -47,7 +47,7 @@ let is_registered= {}; /** @param {string} page_id */ function registerClientPart(page_id){ if(is_registered[page_id]) return; - + document.head.append( el("script", { src: "https://flems.io/flems.html", type: "text/javascript", charset: "utf-8" }), ); @@ -64,5 +64,5 @@ function generateCodeId(src){ .replace(/000+/g, ""); const count= 1 + ( store_prev.get(candidate) || 0 ); store_prev.set(candidate, count); - return count.toString()+"-"+candidate; + return count.toString()+"-"+candidate; } diff --git a/docs/components/examples/customElement/observedAttributes.js b/docs/components/examples/customElement/observedAttributes.js index 2b76280..bee6d3e 100644 --- a/docs/components/examples/customElement/observedAttributes.js +++ b/docs/components/examples/customElement/observedAttributes.js @@ -2,7 +2,7 @@ import { customElementRender, customElementWithDDE, observedAttributes, - el, on, scope + el, on, scope } from "deka-dom-el"; import { S } from "deka-dom-el/signals"; export class HTMLCustomElement extends HTMLElement{ diff --git a/docs/components/examples/elements/dekaElNS.js b/docs/components/examples/elements/dekaElNS.js index 45e7816..5181615 100644 --- a/docs/components/examples/elements/dekaElNS.js +++ b/docs/components/examples/elements/dekaElNS.js @@ -2,8 +2,8 @@ import { elNS, assign } from "deka-dom-el"; const elSVG= elNS("http://www.w3.org/2000/svg"); const elMath= elNS("http://www.w3.org/1998/Math/MathML"); 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 - elMath("math") // see https://developer.mozilla.org/en-US/docs/Web/MathML and https://developer.mozilla.org/en-US/docs/Web/MathML/Global_attributes + 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 // editorconfig-checker-disable-line ); console.log( diff --git a/docs/components/examples/scopes/class-component.js b/docs/components/examples/scopes/class-component.js index 45a1bfd..a525da4 100644 --- a/docs/components/examples/scopes/class-component.js +++ b/docs/components/examples/scopes/class-component.js @@ -36,7 +36,7 @@ function elClass(_class, attributes, ...addons){ }); element.prepend(el_mark); if(is_fragment) element_host= el_mark; - + chainableAppend(element); addons.forEach(c=> c(element_host)); scope.pop(); diff --git a/docs/components/examples/scopes/imperative.js b/docs/components/examples/scopes/imperative.js index 90f822e..012514c 100644 --- a/docs/components/examples/scopes/imperative.js +++ b/docs/components/examples/scopes/imperative.js @@ -23,7 +23,7 @@ function component(){ * const data= O("data"); * ul.append(el("li", data)); * }); - * + * * // THE HOST IS PROBABLY DIFFERENT THAN * // YOU EXPECT AND OBSERVABLES MAY BE * // UNEXPECTEDLY REMOVED!!! diff --git a/docs/components/mnemonic/customElement-init.js b/docs/components/mnemonic/customElement-init.js index 0454726..d8bd5af 100644 --- a/docs/components/mnemonic/customElement-init.js +++ b/docs/components/mnemonic/customElement-init.js @@ -4,25 +4,32 @@ import { mnemonicUl } from "../mnemonicUl.html.js"; export function mnemonic(){ return mnemonicUl().append( el("li").append( - el("code", "customElementRender(, , [, ])"), " — use function to render DOM structure for given ", + el("code", "customElementRender(, , [, ])"), + " — use function to render DOM structure for given ", ), el("li").append( - el("code", "customElementWithDDE()"), " — register to DDE library, see also `lifecyclesToEvents`, can be also used as decorator", + el("code", "customElementWithDDE()"), + " — register to DDE library, see also `lifecyclesToEvents`, can be also used as decorator", ), el("li").append( - el("code", "observedAttributes()"), " — returns record of observed attributes (keys uses camelCase)", + el("code", "observedAttributes()"), + " — returns record of observed attributes (keys uses camelCase)", ), el("li").append( - el("code", "S.observedAttributes()"), " — returns record of observed attributes (keys uses camelCase and values are signals)", + el("code", "S.observedAttributes()"), + " — returns record of observed attributes (keys uses camelCase and values are signals)", ), el("li").append( - el("code", "lifecyclesToEvents()"), " — convert lifecycle methods to events, can be also used as decorator", + el("code", "lifecyclesToEvents()"), + " — convert lifecycle methods to events, can be also used as decorator", ), el("li").append( - el("code", "simulateSlots(, [, ])"), " — simulate slots for Custom Elements without shadow DOM", + el("code", "simulateSlots(, [, ])"), + " — simulate slots for Custom Elements without shadow DOM", ), el("li").append( - el("code", "simulateSlots([, ])"), " — simulate slots for “dde”/functional components", + el("code", "simulateSlots([, ])"), + " — simulate slots for “dde”/functional components", ), ); } diff --git a/docs/components/mnemonic/elements-init.js b/docs/components/mnemonic/elements-init.js index 011f63d..94d5dea 100644 --- a/docs/components/mnemonic/elements-init.js +++ b/docs/components/mnemonic/elements-init.js @@ -4,22 +4,28 @@ import { mnemonicUl } from "../mnemonicUl.html.js"; export function mnemonic(){ return mnemonicUl().append( el("li").append( - el("code", "assign(, ...): "), " — assign properties to the element", + el("code", "assign(, ...): "), + " — assign properties to the element", ), el("li").append( - el("code", "el(, )[.append(...)]: "), " — simple element containing only text", + el("code", "el(, )[.append(...)]: "), + " — simple element containing only text", ), el("li").append( - el("code", "el(, )[.append(...)]: "), " — element with more properties", + el("code", "el(, )[.append(...)]: "), + " — element with more properties", ), el("li").append( - el("code", "el(, )[.append(...)]: "), " — using component represented by function", + el("code", "el(, )[.append(...)]: "), + " — using component represented by function", ), el("li").append( - el("code", "el(<...>, <...>, ...)"), " — see following page" + el("code", "el(<...>, <...>, ...)"), + " — see following page" ), el("li").append( - el("code", "elNS()()[.append(...)]: "), " — typically SVG elements", + el("code", "elNS()()[.append(...)]: "), + " — typically SVG elements", ) ); } diff --git a/docs/components/mnemonic/events-init.js b/docs/components/mnemonic/events-init.js index 77252b0..aae2f28 100644 --- a/docs/components/mnemonic/events-init.js +++ b/docs/components/mnemonic/events-init.js @@ -4,17 +4,21 @@ import { mnemonicUl } from "../mnemonicUl.html.js"; export function mnemonic(){ return mnemonicUl().append( el("li").append( - el("code", "on(, [, ])()"), " — just ", el("code", ".addEventListener(, [, ])") + el("code", "on(, [, ])()"), + " — just ", el("code", ".addEventListener(, [, ])") ), el("li").append( - el("code", "on.(, [, ])()"), " — corresponds to custom elemnets callbacks ", el("code", "Callback(...){...}"), + el("code", "on.(, [, ])()"), + " — corresponds to custom elemnets callbacks ", el("code", "Callback(...){...}"), ". To connect to custom element see following page, else it is simulated by MutationObserver." ), el("li").append( - el("code", "dispatchEvent([, ])(element)"), " — just ", el("code", ".dispatchEvent(new Event([, ]))") + el("code", "dispatchEvent([, ])(element)"), + " — just ", el("code", ".dispatchEvent(new Event([, ]))") ), el("li").append( - el("code", "dispatchEvent([, ])(element, detail)"), " — just ", el("code", ".dispatchEvent(new CustomEvent(, { detail, ... }))") + el("code", "dispatchEvent([, ])(element, detail)"), + " — just ", el("code", ".dispatchEvent(new CustomEvent(, { detail, ... }))") ), ); } diff --git a/docs/components/mnemonic/scopes-init.js b/docs/components/mnemonic/scopes-init.js index 61ff544..62dd70d 100644 --- a/docs/components/mnemonic/scopes-init.js +++ b/docs/components/mnemonic/scopes-init.js @@ -4,13 +4,16 @@ import { mnemonicUl } from "../mnemonicUl.html.js"; export function mnemonic(){ return mnemonicUl().append( el("li").append( - el("code", "el(, )[.append(...)]: "), " — using component represented by function", + el("code", "el(, )[.append(...)]: "), + " — using component represented by function", ), el("li").append( - el("code", "scope.host()"), " — get current component reference" + el("code", "scope.host()"), + " — get current component reference" ), el("li").append( - el("code", "scope.host(...)"), " — use addons to current component", + el("code", "scope.host(...)"), + " — use addons to current component", ) ); } diff --git a/docs/components/mnemonic/signals-init.js b/docs/components/mnemonic/signals-init.js index 7a6d0a6..2e29529 100644 --- a/docs/components/mnemonic/signals-init.js +++ b/docs/components/mnemonic/signals-init.js @@ -7,22 +7,28 @@ export function mnemonic(){ el("code", "S()"), " — signal: reactive value", ), el("li").append( - el("code", "S(()=> )"), " — read-only signal: reactive value dependent on calculation using other signals", + el("code", "S(()=> )"), + " — read-only signal: reactive value dependent on calculation using other signals", ), el("li").append( - el("code", "S.on(, [, ])"), " — listen to the signal value changes", + el("code", "S.on(, [, ])"), + " — listen to the signal value changes", ), el("li").append( - el("code", "S.clear(...)"), " — off and clear signals", + el("code", "S.clear(...)"), + " — off and clear signals", ), el("li").append( - el("code", "S(, )"), " — signal: pattern to create complex reactive objects/arrays", + el("code", "S(, )"), + " — signal: pattern to create complex reactive objects/arrays", ), el("li").append( - el("code", "S.action(, , ...)"), " — invoke an action for given signal" + el("code", "S.action(, , ...)"), + " — invoke an action for given signal" ), el("li").append( - el("code", "S.el(, )"), " — render partial dom structure (template) based on the current signal value", + el("code", "S.el(, )"), + " — render partial dom structure (template) based on the current signal value", ) ); } diff --git a/docs/index.html.js b/docs/index.html.js index fa8140a..ab7b91e 100644 --- a/docs/index.html.js +++ b/docs/index.html.js @@ -27,7 +27,7 @@ export function page({ pkg, info }){ const page_id= info.id; return el(simplePage, { info, pkg }).append( el("p", t`The library tries to provide pure JavaScript tool(s) to create reactive interfaces using …`), - + el(h3, t`Event-driven programming (3 parts separation ≡ 3PS)`), el("p").append(t` Let's introduce the basic principle on which the library is built. We'll use the JavaScript listener as @@ -56,7 +56,7 @@ export function page({ pkg, info }){ describe usage in specific situations, see for example ${el("a", { textContent: t`MVVM`, ...references.w_mvv })} or ${el("a", { textContent: t`MVC`, ...references.w_mvc })}. `), - + el(h3, t`Organization of the documentation`), ); } diff --git a/docs/layout/head.html.js b/docs/layout/head.html.js index e55b41e..cc8f7d8 100644 --- a/docs/layout/head.html.js +++ b/docs/layout/head.html.js @@ -69,7 +69,7 @@ function metaFacebook({ name, description, homepage }){ function iconGitHub(){ const el= elNS("http://www.w3.org/2000/svg"); 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", "c -8.836,0 -16,7.163 -16,16", "c 0,7.069 4.585,13.067 10.942,15.182", diff --git a/docs/p02-elements.html.js b/docs/p02-elements.html.js index b26a158..2c9367f 100644 --- a/docs/p02-elements.html.js +++ b/docs/p02-elements.html.js @@ -54,7 +54,7 @@ export function page({ pkg, info }){ `), el(code, { src: fileURL("./components/examples/elements/intro.js"), page_id }), - + el(h3, t`Creating element(s) (with custom attributes)`), el("p").append(...T` You can create a native DOM element by using the ${el("a", references.mdn_create).append( @@ -77,8 +77,9 @@ export function page({ pkg, info }){ el("p").append(...T` 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 - all available IDLs for example for paragraphs, see ${el("a", { textContent: "HTMLParagraphElement", ...references.mdn_p })}. - Moreover, the ${el("code", "assign")} provides a way to sets declaratively some convenient properties: + all available IDLs for example for paragraphs, see ${el("a", { textContent: "HTMLParagraphElement", + ...references.mdn_p })}. Moreover, the ${el("code", "assign")} provides a way to sets declaratively + some convenient properties: `), el("ul").append( el("li").append(...T` @@ -114,7 +115,7 @@ export function page({ pkg, info }){ ${el("code", "classListDeclarative")}. `), el(example, { src: fileURL("./components/examples/elements/dekaAssign.js"), page_id }), - + el(h3, t`Native JavaScript templating`), el("p", t`By default, the native JS has no good way to define HTML template using DOM API:`), el(example, { src: fileURL("./components/examples/elements/nativeAppend.js"), page_id }), @@ -123,8 +124,8 @@ export function page({ pkg, info }){ parent element. `), el(example, { src: fileURL("./components/examples/elements/dekaAppend.js"), page_id }), - - + + el(h3, t`Basic (state-less) components`), el("p").append(...T` You can use functions for encapsulation (repeating) logic. The ${el("code", "el")} accepts function @@ -149,7 +150,7 @@ export function page({ pkg, info }){ the ${el("code", "elNS")} function: `), el(example, { src: fileURL("./components/examples/elements/dekaElNS.js"), page_id }), - + el(mnemonic) ); } diff --git a/docs/p03-events.html.js b/docs/p03-events.html.js index 9652936..3ea0268 100644 --- a/docs/p03-events.html.js +++ b/docs/p03-events.html.js @@ -29,7 +29,7 @@ const references= { }, /** Custom Element lifecycle callbacks */ 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 */ mdn_mutation: { @@ -50,9 +50,9 @@ export function page({ pkg, info }){ We quickly introduce helper to listening to the native DOM events. And library syntax/pattern so-called ${el("em", t`Addon`)} to incorporate not only this in UI templates declaratively. `), - + el(code, { src: fileURL("./components/examples/events/intro.js"), page_id }), - + el(h3, t`Events and listenners`), el("p").append(...T` In JavaScript you can listen to the native DOM events of the given element by using @@ -105,17 +105,20 @@ export function page({ pkg, info }){ `), el("p").append(...T` 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("p").append(...T` 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 - 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` - But, in case of regular elemnets the ${el("a", references.mdn_mutation).append(el("code", "MutationObserver"), " | MDN")} - is internaly used to track these events. Therefore, there are some drawbacks: + But, in case of regular elemnets the ${el("a", references.mdn_mutation).append(el("code", + "MutationObserver"), " | MDN")} is internaly used to track these events. Therefore, there are some + drawbacks: `), el("ul").append( el("li").append(...T` @@ -136,14 +139,15 @@ export function page({ pkg, info }){ native behaviour re-(dis)connecting element, use: `), 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(h3, t`Final notes`), el("p", t`The library also provides a method to dispatch the events.`), el(example, { src: fileURL("./components/examples/events/compareDispatch.js"), page_id }), - + el(mnemonic) ); } diff --git a/docs/p04-signals.html.js b/docs/p04-signals.html.js index 6cfedc8..0f3a6b5 100644 --- a/docs/p04-signals.html.js +++ b/docs/p04-signals.html.js @@ -49,7 +49,7 @@ export function page({ pkg, info }){ programming. If we desire to solve the issue in a declarative manner, signals may be a viable approach. `), el(code, { src: fileURL("./components/examples/signals/intro.js"), page_id }), - + el(h3, t`Introducing signals`), el("p").append(...T` Let’s re-introduce @@ -62,7 +62,7 @@ export function page({ pkg, info }){ `), el(example, { src: fileURL("./components/examples/signals/signals.js"), page_id }), el("p").append(...T` - All this is just an example of + All this is just an example of ${el("a", { textContent: t`Event-driven programming`, ...references.wiki_event_driven })} and ${el("a", { textContent: t`Publish–subscribe pattern`, ...references.wiki_pubsub })} (compare for example with ${el("a", { textContent: t`fpubsub library`, ...references.fpubsub })}). All three parts can be in @@ -109,8 +109,8 @@ export function page({ pkg, info }){ el("p").append(...T` To derived attribute based on value of signal variable just use the signal as a value of the attribute (${el("code", "assign(element, { attribute: S('value') })")}). ${el("code", "assign")}/${el("code", "el")} - provides ways to glue reactive attributes/classes more granularly into the DOM. Just use dedicated build-in - attributes ${el("code", "dataset")}, ${el("code", "ariaset")} and ${el("code", "classList")}. + provides ways to glue reactive attributes/classes more granularly into the DOM. Just use dedicated build-in + attributes ${el("code", "dataset")}, ${el("code", "ariaset")} and ${el("code", "classList")}. `), el("p").append(...T` For computation, you can use the “derived signal” (see above) like diff --git a/docs/p05-scopes.html.js b/docs/p05-scopes.html.js index d104ffd..2572d40 100644 --- a/docs/p05-scopes.html.js +++ b/docs/p05-scopes.html.js @@ -36,7 +36,7 @@ export function page({ pkg, info }){ `), el(code, { src: fileURL("./components/examples/scopes/intro.js"), page_id }), el("p").append(...T`The library therefore use ${el("em", t`scopes`)} to provide these functionalities.`), - + el(h3, t`Scopes and hosts`), el("p").append(...T` The ${el("strong", "host")} is the name for the element representing the component. This is typically diff --git a/docs/p06-customElement.html.js b/docs/p06-customElement.html.js index 8d471f6..0bc4c81 100644 --- a/docs/p06-customElement.html.js +++ b/docs/p06-customElement.html.js @@ -21,7 +21,7 @@ const references= { /** observedAttributes on MDN */ mdn_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 */ mdn_custom_elements: { @@ -55,10 +55,10 @@ export function page({ pkg, info }){ return el(simplePage, { info, pkg }).append( el("h2", t`Using web components in combinantion with DDE`), el("p").append(...T` - The DDE library allows for use within ${el("a", references.mdn_web_components).append( el("strong", t`Web Components`) )} - for dom-tree generation. However, in order to be able to use signals (possibly mapping to registered - ${el("a", references.mdn_observedAttributes).append( el("code", "observedAttributes") )}) and additional - functionality is (unfortunately) required to use helpers provided by the library. + The DDE library allows for use within ${el("a", references.mdn_web_components).append( el("strong", + t`Web Components`) )} for dom-tree generation. However, in order to be able to use signals (possibly + mapping to registered ${el("a", references.mdn_observedAttributes).append( el("code", "observedAttributes") + )}) and additional functionality is (unfortunately) required to use helpers provided by the library. `), el(code, { src: fileURL("./components/examples/customElement/intro.js"), page_id }), @@ -82,8 +82,8 @@ export function page({ pkg, info }){ `), el("p").append(...T` 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` })}. - To do it, the library provides function ${el("code", "customElementWithDDE")}… + ${el("a", { textContent: t`DDE events`, href: "./p03-events.html", title: t`See events part of the library + documentation` })}. To do it, the library provides function ${el("code", "customElementWithDDE")}… `), el(example, { src: fileURL("./components/examples/customElement/customElementWithDDE.js"), page_id }), diff --git a/examples/components/3rd-party.js b/examples/components/3rd-party.js index 21db48c..01c819e 100644 --- a/examples/components/3rd-party.js +++ b/examples/components/3rd-party.js @@ -21,7 +21,8 @@ export function thirdParty(){ }); // Array.from((new URL(location)).searchParams.entries()) // .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, { onread(data){ Array.from(data.entries()) diff --git a/examples/components/fullNameComponent.js b/examples/components/fullNameComponent.js index 7dd28b1..cae66ab 100644 --- a/examples/components/fullNameComponent.js +++ b/examples/components/fullNameComponent.js @@ -15,6 +15,7 @@ export function fullNameComponent(){ on.disconnected(()=> console.log(fullNameComponent)) ); + const style= { height: "80px", display: "block", fill: "currentColor" }; const elSVG= elNS("http://www.w3.org/2000/svg"); return el("div", { className }).append( el("h2", "Simple form:"), @@ -29,7 +30,7 @@ export function fullNameComponent(){ ": ", 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("text", { x: 20, y: 35, textContent: "Text" }), ) diff --git a/examples/components/todosComponent.js b/examples/components/todosComponent.js index 65a750c..ab70675 100644 --- a/examples/components/todosComponent.js +++ b/examples/components/todosComponent.js @@ -17,22 +17,25 @@ const className= style.host(todosComponent).css` /** @param {{ todos: string[] }} */ export function todosComponent({ todos= [ "Task A" ] }= {}){ let key= 0; - const todosO= S(new Map(), { + const todosO= S( /** @type {Map>} */ (new Map()), { + /** @param {string} v */ add(v){ this.value.set(key++, S(v)); }, + /** @param {number} key */ remove(key){ S.clear(this.value.get(key)); this.value.delete(key); } }); todos.forEach(text=> S.action(todosO, "add", text)); const name= "todoName"; 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(); S.action(todosO, "add", el.value); el.value= ""; }); - const onremove= on("remove", event=> - S.action(todosO, "remove", event.detail)); - + const onremove= on("remove", /** @param {CustomEvent} event */ + event=> S.action(todosO, "remove", event.detail)); + return el("div", { className }).append( el("div").append( el("h2", "Todos:"), @@ -60,19 +63,24 @@ export function todosComponent({ todos= [ "Task A" ] }= {}){ ) } /** + * @param {{ textContent: ddeSignal, value: ddeSignal }} attrs * @dispatchs {number} remove * */ function todoComponent({ textContent, value }){ const { host }= scope; + const dispatchRemove= /** @type {(data: number) => void} */ + (dispatchEvent("remove", null, host)); 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.stopPropagation(); - dispatchEvent("remove")(host(), value); + dispatchRemove(value); }); const is_editable= S(false); const onedited= on("change", ev=> { - textContent(ev.target.value); + const el= /** @type {HTMLInputElement} */ (ev.target); + textContent(el.value); is_editable(false); }); return el("li").append( diff --git a/examples/components/webComponent.js b/examples/components/webComponent.js index e225dd4..75dc6bf 100644 --- a/examples/components/webComponent.js +++ b/examples/components/webComponent.js @@ -38,7 +38,7 @@ export class CustomHTMLTestElement extends HTMLElement{ ); } test= "A"; - + get name(){ return this.getAttribute("name"); } set name(value){ this.setAttribute("name", value); } /** @attr pre-name */ diff --git a/examples/index.js b/examples/index.js index 9fc6332..bcfdfa1 100644 --- a/examples/index.js +++ b/examples/index.js @@ -1,4 +1,9 @@ import { style, el, S } from './exports.js'; +style.css` +:root{ + color-scheme: dark light; +} +`; document.head.append(style.element); import { fullNameComponent } from './components/fullNameComponent.js'; import { todosComponent } from './components/todosComponent.js'; diff --git a/index.d.ts b/index.d.ts index 270cdf2..c5fc42b 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2,10 +2,10 @@ declare global{ /* ddeSignal */ } type CustomElementTagNameMap= { '#text': Text, '#comment': Comment } type SupportedElement= - HTMLElementTagNameMap[keyof HTMLElementTagNameMap] - | SVGElementTagNameMap[keyof SVGElementTagNameMap] - | MathMLElementTagNameMap[keyof MathMLElementTagNameMap] - | CustomElementTagNameMap[keyof CustomElementTagNameMap] + HTMLElementTagNameMap[keyof HTMLElementTagNameMap] + | SVGElementTagNameMap[keyof SVGElementTagNameMap] + | MathMLElementTagNameMap[keyof MathMLElementTagNameMap] + | CustomElementTagNameMap[keyof CustomElementTagNameMap] declare global { type ddeComponentAttributes= Record | undefined; type ddeElementAddon= (element: El)=> El | void; @@ -15,9 +15,12 @@ type AttrsModified= { /** * Use string like in HTML (internally uses `*.setAttribute("style", *)`), or object representation (like DOM API). */ - style: string | Partial | ddeSignal | Partial<{ [K in keyof CSSStyleDeclaration]: ddeSignal }> + style: string | Partial | ddeSignal + | Partial<{ [K in keyof CSSStyleDeclaration]: ddeSignal }> /** - * 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>, /** @@ -28,20 +31,32 @@ type AttrsModified= { * Sets `aria-*` simiraly to `dataset` * */ ariaset: Record>, -} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|ddeSignal> & Record<`.${string}`, any> +} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, string|ddeSignal> + & Record<`.${string}`, any> type _fromElsInterfaces= Omit; /** * 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. * @private */ -type ElementAttributes= Partial<{ [K in keyof _fromElsInterfaces]: _fromElsInterfaces[K] | ddeSignal<_fromElsInterfaces[K]> } & AttrsModified> & Record; -export function classListDeclarative(element: El, classList: AttrsModified["classList"]): El +type ElementAttributes= Partial<{ + [K in keyof _fromElsInterfaces]: _fromElsInterfaces[K] | ddeSignal<_fromElsInterfaces[K]> +} & AttrsModified> & Record; +export function classListDeclarative( + element: El, + classList: AttrsModified["classList"] +): El export function assign(element: El, ...attrs_array: ElementAttributes[]): El -export function assignAttribute>(element: El, attr: ATT, value: ElementAttributes[ATT]): ElementAttributes[ATT] +export function assignAttribute>( + element: El, + attr: ATT, + value: ElementAttributes[ATT] +): ElementAttributes[ATT] type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap; type textContent= string | ddeSignal; @@ -68,7 +83,9 @@ export function el< component: C, attrs?: Parameters[0] | textContent, ...addons: ddeElementAddon>[] -): ReturnType extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? ReturnType : ( ReturnType extends ddeDocumentFragment ? ReturnType : ddeHTMLElement ) +): ReturnType extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] + ? ReturnType + : ( ReturnType extends ddeDocumentFragment ? ReturnType : ddeHTMLElement ) export { el as createElement } export function elNS( @@ -88,7 +105,9 @@ export function elNS( EL extends ( TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement ), >( tag_name: TAG, - attrs?: string | textContent | Partial<{ [key in keyof EL]: EL[key] | ddeSignal | string | number | boolean }>, + attrs?: string | textContent | Partial<{ + [key in keyof EL]: EL[key] | ddeSignal | string | number | boolean + }>, ...addons: ddeElementAddon[] )=> ddeMathMLElement export function elNS( @@ -106,18 +125,27 @@ export function chainableAppend(el: EL): EL; * */ type simulateSlotsMapper= (body: HTMLSlotElement, el: HTMLElement)=> void; /** Simulate slots for ddeComponents */ -export function simulateSlots(root: EL, mapper?: simulateSlotsMapper): EL +export function simulateSlots( + root: EL, + mapper?: simulateSlotsMapper +): EL /** * Simulate slots in Custom Elements without using `shadowRoot`. * @param el Custom Element root element * @param body Body of the custom element * */ -export function simulateSlots(el: HTMLElement, body: EL, mapper?: simulateSlotsMapper): EL +export function simulateSlots( + el: HTMLElement, + body: EL, mapper?: simulateSlotsMapper +): EL export function dispatchEvent(name: keyof DocumentEventMap | string, options?: EventInit): (element: SupportedElement, data?: any)=> void; -export function dispatchEvent(name: keyof DocumentEventMap | string, options: EventInit | null, element: SupportedElement | (()=> SupportedElement)): - (data?: any)=> void; +export function dispatchEvent( + name: keyof DocumentEventMap | string, + options: EventInit | null, + element: SupportedElement | (()=> SupportedElement) +): (data?: any)=> void; interface On{ /** Listens to the DOM event. See {@link Document.addEventListener} */ < @@ -135,7 +163,7 @@ interface On{ listener: (this: El, ev: Event | CustomEvent ) => any, options?: AddEventListenerOptions ) : 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< EE extends ddeElementAddon, El extends ( EE extends ddeElementAddon ? El : never ) @@ -143,7 +171,7 @@ interface On{ listener: (this: El, event: CustomEvent) => any, options?: AddEventListenerOptions ) : 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< EE extends ddeElementAddon, El extends ( EE extends ddeElementAddon ? El : never ) @@ -151,7 +179,7 @@ interface On{ listener: (this: El, event: CustomEvent) => any, options?: AddEventListenerOptions ) : 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< EE extends ddeElementAddon, El extends ( EE extends ddeElementAddon ? El : never ) @@ -162,7 +190,12 @@ interface On{ } export const on: On; -type Scope= { scope: Node | Function | Object, host: ddeElementAddon, custom_element: false | HTMLElement, prevent: boolean } +type Scope= { + scope: Node | Function | Object, + host: ddeElementAddon, + custom_element: false | HTMLElement, + prevent: boolean +}; /** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */ export const scope: { current: Scope, @@ -176,7 +209,7 @@ export const scope: { * — `scope.host(on.connected(console.log))`. * */ host: (...addons: ddeElementAddon[])=> HTMLElement, - + state: Scope[], /** Adds new child scope. All attributes are inherited by default. */ push(scope: Partial): ReturnType["push"]>, @@ -202,12 +235,12 @@ export function observedAttributes(custom_element: HTMLElement): Record= (...nodes: (Node | string)[])=> el; - + interface ddeDocumentFragment extends DocumentFragment{ append: ddeAppend; } interface ddeHTMLElement extends HTMLElement{ append: ddeAppend; } interface ddeSVGElement extends SVGElement{ append: ddeAppend; } interface ddeMathMLElement extends MathMLElement{ append: ddeAppend; } - + interface ddeHTMLElementTagNameMap { "a": ddeHTMLAnchorElement; "area": ddeHTMLAreaElement; @@ -350,6 +383,7 @@ declare global{ } } +// editorconfig-checker-disable interface ddeHTMLAnchorElement extends HTMLAnchorElement{ append: ddeAppend; } interface ddeHTMLAreaElement extends HTMLAreaElement{ append: ddeAppend; } interface ddeHTMLAudioElement extends HTMLAudioElement{ append: ddeAppend; } @@ -477,3 +511,4 @@ interface ddeSVGTitleElement extends SVGTitleElement{ append: ddeAppend; } interface ddeSVGUseElement extends SVGUseElement{ append: ddeAppend; } interface ddeSVGViewElement extends SVGViewElement{ append: ddeAppend; } +// editorconfig-checker-enable diff --git a/jsdom.js b/jsdom.js index f71ab97..0e39a36 100644 --- a/jsdom.js +++ b/jsdom.js @@ -29,7 +29,7 @@ export function register(dom){ export function unregister(){ if(!dom_last) return false; - + Object.assign(env, env_bk); env_bk= {}; dom_last= undefined; diff --git a/package-lock.json b/package-lock.json index 022a4e9..4736753 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "devDependencies": { "@size-limit/preset-small-lib": "~11.0", "dts-bundler": "~0.1", + "editorconfig-checker": "^6.0.0", "esbuild": "~0.24", "jsdom": "~25.0", "jshint": "~2.13", @@ -1424,6 +1425,24 @@ "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": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", diff --git a/package.json b/package.json index fa01659..9018174 100644 --- a/package.json +++ b/package.json @@ -83,9 +83,7 @@ "modifyEsbuildConfig": { "platform": "browser" }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, + "scripts": {}, "keywords": [ "dom", "javascript", @@ -95,6 +93,7 @@ "devDependencies": { "@size-limit/preset-small-lib": "~11.0", "dts-bundler": "~0.1", + "editorconfig-checker": "~6.0", "esbuild": "~0.24", "jsdom": "~25.0", "jshint": "~2.13", diff --git a/signals.d.ts b/signals.d.ts index ebae316..2cd5d19 100644 --- a/signals.d.ts +++ b/signals.d.ts @@ -55,7 +55,7 @@ interface signal{ * */ el(signal: Signal, el: (v: S)=> Element | Element[] | DocumentFragment): DocumentFragment; - observedAttributes(custom_element: HTMLElement): Record>; + observedAttributes(custom_element: HTMLElement): Record>; } export const signal: signal; export const S: signal; diff --git a/src/dom-common.js b/src/dom-common.js index a2071fc..c9c1d22 100644 --- a/src/dom-common.js +++ b/src/dom-common.js @@ -13,11 +13,11 @@ function setDeleteAttr(obj, prop, val){ For some native attrs you can unset only to set empty string. This can be confusing as it is seen in inspector `<… id=""`. Options: - 1. Leave it, as it is native behaviour - 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 - 4. Point 2. with checks for coincidence (e.g. use special string) - */ + 1. Leave it, as it is native behaviour + 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 + 4. Point 2. with checks for coincidence (e.g. use special string) + */ Reflect.set(obj, prop, val); if(!isUndef(val)) return; Reflect.deleteProperty(obj, prop); diff --git a/src/dom.js b/src/dom.js index 4c5daa5..7ed8dc1 100644 --- a/src/dom.js +++ b/src/dom.js @@ -10,13 +10,13 @@ const scopes= [ { export const scope= { get current(){ return scopes[scopes.length-1]; }, get host(){ return this.current.host; }, - + preventDefault(){ const { current }= this; current.prevent= true; return current; }, - + get state(){ return [ ...scopes ]; }, push(s= {}){ return scopes.push(Object.assign({}, this.current, { prevent: false }, s)); }, pushRoot(){ return scopes.push(scopes[0]); }, @@ -25,9 +25,11 @@ export const scope= { return scopes.pop(); }, }; -// following chainableAppend implementation is OK as the ElementPrototype.append description already is { writable: true, enumerable: true, configurable: true } +// 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; } -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; export function createElement(tag, attributes, ...addons){ /* jshint maxcomplexity: 15 */ @@ -40,7 +42,9 @@ export function createElement(tag, attributes, ...addons){ switch(true){ case typeof tag==="function": { 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); const is_fragment= el instanceof env.F; if(el.nodeName==="#comment") break; @@ -108,7 +112,9 @@ export function simulateSlots(element, root, mapper){ } function simulateSlotReplace(slot, element, mapper){ if(mapper) mapper(slot, element); - try{ slot.replaceWith(assign(element, { className: [ element.className, slot.className ], dataset: { ...slot.dataset } })); } + try{ slot.replaceWith(assign(element, { + className: [ element.className, slot.className ], + dataset: { ...slot.dataset } })); } catch(_){ slot.replaceWith(element); } } /** @@ -141,7 +147,7 @@ const { setDeleteAttr }= env; export function assign(element, ...attributes){ if(!attributes.length) return element; assign_context.set(element, assignContext(element, this)); - + for(const [ key, value ] of Object.entries(Object.assign({}, ...attributes))) assignAttribute.call(this, element, key, value); assign_context.delete(element); @@ -150,7 +156,7 @@ export function assign(element, ...attributes){ export function assignAttribute(element, key, value){ const { setRemoveAttr, s }= assignContext(element, this); const _this= this; - + value= s.processReactiveAttribute(element, key, value, (key, value)=> assignAttribute.call(_this, element, key, value)); const [ k ]= key; @@ -216,7 +222,9 @@ function getPropDescriptor(p, key){ return des; } -/** @template {Record} T @param {object} s @param {T} obj @param {(param: [ keyof T, T[keyof T] ])=> void} cb */ +/** + * @template {Record} T @param {object} s @param {T} obj @param {(param: [ keyof T, T[keyof T] ])=> void} cb + * */ function forEachEntries(s, obj, cb){ if(typeof obj !== "object" || obj===null) return; return Object.entries(obj).forEach(function process([ key, val ]){ @@ -227,6 +235,9 @@ function forEachEntries(s, obj, cb){ } function attrArrToStr(attr){ return Array.isArray(attr) ? attr.filter(Boolean).join(" ") : attr; } -function setRemove(obj, prop, key, val){ return obj[ (isUndef(val) ? "remove" : "set") + prop ](key, attrArrToStr(val)); } -function setRemoveNS(obj, prop, key, val, ns= null){ return obj[ (isUndef(val) ? "remove" : "set") + prop + "NS" ](ns, key, attrArrToStr(val)); } -function setDelete(obj, key, val){ Reflect.set(obj, key, val); if(!isUndef(val)) return; return Reflect.deleteProperty(obj, key); } +function setRemove(obj, prop, key, val){ + return obj[ (isUndef(val) ? "remove" : "set") + prop ](key, attrArrToStr(val)); } +function setRemoveNS(obj, prop, key, val, ns= null){ + return obj[ (isUndef(val) ? "remove" : "set") + prop + "NS" ](ns, key, attrArrToStr(val)); } +function setDelete(obj, key, val){ + Reflect.set(obj, key, val); if(!isUndef(val)) return; return Reflect.deleteProperty(obj, key); } diff --git a/src/events-observer.js b/src/events-observer.js index 67966e7..5dc81a6 100644 --- a/src/events-observer.js +++ b/src/events-observer.js @@ -103,10 +103,10 @@ function connectionsChangesObserverConstructor(){ for(const element of addedNodes){ if(is_root) collectChildren(element).then(observerAdded); if(!store.has(element)) continue; - + const ls= store.get(element); if(!ls.length_c) continue; - + element.dispatchEvent(new Event(evc)); ls.connected= new WeakSet(); ls.length_c= 0; @@ -120,7 +120,7 @@ function connectionsChangesObserverConstructor(){ for(const element of removedNodes){ if(is_root) collectChildren(element).then(observerRemoved); if(!store.has(element)) continue; - + const ls= store.get(element); if(!ls.length_d) continue; (globalThis.queueMicrotask || setTimeout)(dispatchRemove(element)); diff --git a/src/events.js b/src/events.js index 8f08d0e..392caaf 100644 --- a/src/events.js +++ b/src/events.js @@ -64,9 +64,9 @@ on.attributeChanged= function(listener, options){ element.addEventListener(eva, listener, options); if(element[keyLTE] || els_attribute_store.has(element)) return element; - + if(!env.M) return element; - + const observer= new env.M(function(mutations){ for(const { attributeName, target } of mutations) target.dispatchEvent( @@ -77,4 +77,4 @@ on.attributeChanged= function(listener, options){ //TODO: clean up when element disconnected return element; }; -}; +}; diff --git a/src/observables-lib.js b/src/observables-lib.js index c86e51a..a019133 100644 --- a/src/observables-lib.js +++ b/src/observables-lib.js @@ -21,7 +21,7 @@ export function signal(value, actions){ if(typeof value!=="function") return create(false, value, actions); if(isSignal(value)) return value; - + const out= create(true); const contextReWatch= function(){ const [ origin, ...deps_old ]= deps.get(contextReWatch); @@ -77,11 +77,11 @@ signal.clear= function(...signals){ o.listeners.forEach(l=> { o.listeners.delete(l); if(!deps.has(l)) return; - + const ls= deps.get(l); ls.delete(s); if(ls.size>1) return; - + s.clear(...ls); deps.delete(l); }); diff --git a/src/signals-lib.js b/src/signals-lib.js index c86e51a..a019133 100644 --- a/src/signals-lib.js +++ b/src/signals-lib.js @@ -21,7 +21,7 @@ export function signal(value, actions){ if(typeof value!=="function") return create(false, value, actions); if(isSignal(value)) return value; - + const out= create(true); const contextReWatch= function(){ const [ origin, ...deps_old ]= deps.get(contextReWatch); @@ -77,11 +77,11 @@ signal.clear= function(...signals){ o.listeners.forEach(l=> { o.listeners.delete(l); if(!deps.has(l)) return; - + const ls= deps.get(l); ls.delete(s); if(ls.size>1) return; - + s.clear(...ls); deps.delete(l); }); diff --git a/tsconfig.json b/tsconfig.json index 8ac44d2..ce2ec17 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { - "compilerOptions": { - "emitDeclarationOnly": true, - "declaration": true, - "declarationDir": "dist" - } + "compilerOptions": { + "emitDeclarationOnly": true, + "declaration": true, + "declarationDir": "dist" + } }