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

Merge branch 'main' of github.com:jaandrle/deka-dom-el

This commit is contained in:
Jan Andrle 2024-01-31 14:39:03 +01:00
commit 8dcf4b83ff
Signed by: jaandrle
GPG Key ID: B3A25AED155AFFAB
13 changed files with 104 additions and 75 deletions

File diff suppressed because one or more lines are too long

34
dist/dde.js vendored

File diff suppressed because one or more lines are too long

View File

@ -54,7 +54,7 @@ interface observable{
* */
el<S extends any>(observable: Observable<S, any>, el: (v: S)=> Element | Element[] | DocumentFragment): DocumentFragment;
attribute(name: string, initial?: string): Observable<string, {}>;
observedAttributes(custom_element: HTMLElement): Record<string, Observable<any, any>>;
}
export const observable: observable;
export const O: observable;
@ -64,16 +64,11 @@ declare global {
type ddeActions<V>= Actions<V>
}
type CustomElementTagNameMap= { '#text': Text, '#comment': Comment }
declare global {
interface ddePublicElementTagNameMap{
}
}
type SupportedElement=
HTMLElementTagNameMap[keyof HTMLElementTagNameMap]
| SVGElementTagNameMap[keyof SVGElementTagNameMap]
| MathMLElementTagNameMap[keyof MathMLElementTagNameMap]
| CustomElementTagNameMap[keyof CustomElementTagNameMap]
| ddePublicElementTagNameMap[keyof ddePublicElementTagNameMap];
declare global {
type ddeComponentAttributes= Record<any, any> | undefined;
type ddeElementAddon<El extends SupportedElement | DocumentFragment>= (element: El)=> El | void;
@ -112,7 +107,7 @@ export function classListDeclarative<El extends SupportedElement>(element: El, c
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(element: El, attr: ATT, value: ElementAttributes<El>[ATT]): ElementAttributes<El>[ATT]
type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap & ddePublicElementTagNameMap
type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap;
type textContent= string | ( (set?: string)=> string ); // TODO: for some reason `Observable<string, any>` leads to `attrs?: any`
export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap & string,
@ -239,9 +234,22 @@ export const scope: {
pop(): ReturnType<Array<Scope>["pop"]>,
};
/* TypeScript MEH // TODO for SVG */
type ddeAppend<el>= (...nodes: (Node | string)[])=> el;
export function customElementRender<
EL extends HTMLElement,
P extends any = Record<string, any>
>(
custom_element: EL,
render: (props: P)=> SupportedElement,
props?: P | ((...args: any[])=> P)
): EL
export function customElementWithDDE<EL extends HTMLElement>(custom_element: EL): EL
export function lifecycleToEvents<EL extends HTMLElement>(custom_element: EL): EL
export function observedAttributes(custom_element: HTMLElement): Record<string, string>
/* TypeScript MEH */
declare global{
type ddeAppend<el>= (...nodes: (Node | string)[])=> el;
interface ddeDocumentFragment extends DocumentFragment{ append: ddeAppend<ddeDocumentFragment>; }
interface ddeHTMLElement extends HTMLElement{ append: ddeAppend<ddeHTMLElement>; }
interface ddeSVGElement extends SVGElement{ append: ddeAppend<ddeSVGElement>; }

File diff suppressed because one or more lines are too long

26
dist/esm.d.ts vendored
View File

@ -54,7 +54,7 @@ interface observable{
* */
el<S extends any>(observable: Observable<S, any>, el: (v: S)=> Element | Element[] | DocumentFragment): DocumentFragment;
attribute(name: string, initial?: string): Observable<string, {}>;
observedAttributes(custom_element: HTMLElement): Record<string, Observable<any, any>>;
}
export const observable: observable;
export const O: observable;
@ -64,16 +64,11 @@ declare global {
type ddeActions<V>= Actions<V>
}
type CustomElementTagNameMap= { '#text': Text, '#comment': Comment }
declare global {
interface ddePublicElementTagNameMap{
}
}
type SupportedElement=
HTMLElementTagNameMap[keyof HTMLElementTagNameMap]
| SVGElementTagNameMap[keyof SVGElementTagNameMap]
| MathMLElementTagNameMap[keyof MathMLElementTagNameMap]
| CustomElementTagNameMap[keyof CustomElementTagNameMap]
| ddePublicElementTagNameMap[keyof ddePublicElementTagNameMap];
declare global {
type ddeComponentAttributes= Record<any, any> | undefined;
type ddeElementAddon<El extends SupportedElement | DocumentFragment>= (element: El)=> El | void;
@ -112,7 +107,7 @@ export function classListDeclarative<El extends SupportedElement>(element: El, c
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(element: El, attr: ATT, value: ElementAttributes<El>[ATT]): ElementAttributes<El>[ATT]
type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap & ddePublicElementTagNameMap
type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap;
type textContent= string | ( (set?: string)=> string ); // TODO: for some reason `Observable<string, any>` leads to `attrs?: any`
export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap & string,
@ -239,9 +234,22 @@ export const scope: {
pop(): ReturnType<Array<Scope>["pop"]>,
};
/* TypeScript MEH // TODO for SVG */
type ddeAppend<el>= (...nodes: (Node | string)[])=> el;
export function customElementRender<
EL extends HTMLElement,
P extends any = Record<string, any>
>(
custom_element: EL,
render: (props: P)=> SupportedElement,
props?: P | ((...args: any[])=> P)
): EL
export function customElementWithDDE<EL extends HTMLElement>(custom_element: EL): EL
export function lifecycleToEvents<EL extends HTMLElement>(custom_element: EL): EL
export function observedAttributes(custom_element: HTMLElement): Record<string, string>
/* TypeScript MEH */
declare global{
type ddeAppend<el>= (...nodes: (Node | string)[])=> el;
interface ddeDocumentFragment extends DocumentFragment{ append: ddeAppend<ddeDocumentFragment>; }
interface ddeHTMLElement extends HTMLElement{ append: ddeAppend<ddeHTMLElement>; }
interface ddeSVGElement extends SVGElement{ append: ddeAppend<ddeSVGElement>; }

2
dist/esm.js vendored

File diff suppressed because one or more lines are too long

View File

@ -67,7 +67,7 @@ document.body.append(
function log({ type, detail }){
console.log({ _this: this, type, detail });
}
</code></div><script>Flems(document.getElementById("code-example-1-35hjjp3e4js"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { el, on } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js\\\";\\nconst paragraph= el(\\\"p\\\", \\\"See live-cycle events in console.\\\",\\n\\tel=> log({ type: \\\"dde:created\\\", detail: el }),\\n\\ton.connected(log),\\n\\ton.disconnected(log),\\n\\ton.attributeChanged(log));\\n\\ndocument.body.append(\\n\\tparagraph,\\n\\tel(\\\"button\\\", \\\"Update attribute\\\", on(\\\"click\\\", ()=> paragraph.setAttribute(\\\"test\\\", Math.random().toString()))),\\n\\t\\\" \\\",\\n\\tel(\\\"button\\\", \\\"Remove\\\", on(\\\"click\\\", ()=> paragraph.remove()))\\n);\\n\\n/** @param {Partial<CustomEvent>} event */\\nfunction log({ type, detail }){\\n\\tconsole.log({ _this: this, type, detail });\\n}\\n\"}],\"toolbar\":false}"));</script><p>For Custom elements, we will later introduce a way to replace <code>*Callback</code> syntax with <code>dde:*</code> events. The <code>on.*</code> functions then listen to the appropriate Custom Elements events (see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks">Custom element lifecycle callbacks | MDN</a>).</p><p>But, in case of regular elemnets the <a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver"><code>MutationObserver</code> | MDN</a> is internaly used to track these events. Therefore, there are some drawbacks:</p><ul><li>To proper listener registration, you need to use <code>on.*</code> not `on("dde:*", …)`!</li><li>Use sparingly! Internally, library must loop of all registered events and fires event properly. <strong>It is good practice to use the fact that if an element is removed, its children are also removed!</strong> In this spirit, we will introduce later the <strong>host</strong> syntax to register clean up procedures when the component is removed from the app.</li></ul><h3 id="h-final-notes"><!--<dde:mark type="component" name="h3" host="parentElement" ssr/>--><a href="#h-final-notes" tabindex="-1">#</a> Final notes</h3><p>The library also provides a&nbsp;method to dispatch the events.</p><!--<dde:mark type="component" name="example" host="this" ssr/>--><div id="code-example-2-b6a1hbrxh7s" class="example"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">import { el, on, dispatchEvent } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js";
</code></div><script>Flems(document.getElementById("code-example-1-35hjjp3e4js"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { el, on } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js\\\";\\nconst paragraph= el(\\\"p\\\", \\\"See live-cycle events in console.\\\",\\n\\tel=> log({ type: \\\"dde:created\\\", detail: el }),\\n\\ton.connected(log),\\n\\ton.disconnected(log),\\n\\ton.attributeChanged(log));\\n\\ndocument.body.append(\\n\\tparagraph,\\n\\tel(\\\"button\\\", \\\"Update attribute\\\", on(\\\"click\\\", ()=> paragraph.setAttribute(\\\"test\\\", Math.random().toString()))),\\n\\t\\\" \\\",\\n\\tel(\\\"button\\\", \\\"Remove\\\", on(\\\"click\\\", ()=> paragraph.remove()))\\n);\\n\\n/** @param {Partial<CustomEvent>} event */\\nfunction log({ type, detail }){\\n\\tconsole.log({ _this: this, type, detail });\\n}\\n\"}],\"toolbar\":false}"));</script><p>For Custom elements, we will later introduce a way to replace <code>*Callback</code> syntax with <code>dde:*</code> events. The <code>on.*</code> functions then listen to the appropriate Custom Elements events (see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks">Custom element lifecycle callbacks | MDN</a>).</p><p>But, in case of regular elemnets the <a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver"><code>MutationObserver</code> | MDN</a> is internaly used to track these events. Therefore, there are some drawbacks:</p><ul><li>To proper listener registration, you need to use <code>on.*</code> not `on("dde:*", …)`!</li><li>Use sparingly! Internally, library must loop of all registered events and fires event properly. <strong>It is good practice to use the fact that if an element is removed, its children are also removed!</strong> In this spirit, we will introduce later the <strong>host</strong> syntax to register clean up procedures when the component is removed from the app.</li></ul><p>To provide intuitive behaviour, similar also to how the life-cycle events works in other frameworks/libraries, deka-dom-el library ensures that <code>on.connected</code> and <code>on.disconnected</code> are called only once and only when the element is (dis)connected to live DOM. The solution is inspired by <a href="https://vuejs.org/guide/extras/web-components.html#lifecycle" title="Vue and Web Components | Lifecycle">Vue</a>. For using native behaviour re-(dis)connecting element, use:</p><ul><li>custom <code>MutationObserver</code> or logic in (dis)<div class="code"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js"></code></div> or…</li><li>re-add <code>on.connected</code> or <code>on.disconnected</code> listeners.</li></ul><h3 id="h-final-notes"><!--<dde:mark type="component" name="h3" host="parentElement" ssr/>--><a href="#h-final-notes" tabindex="-1">#</a> Final notes</h3><p>The library also provides a&nbsp;method to dispatch the events.</p><!--<dde:mark type="component" name="example" host="this" ssr/>--><div id="code-example-2-b6a1hbrxh7s" class="example"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">import { el, on, dispatchEvent } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js";
document.body.append(
el("p", "Listenning to `test` event.", on("test", console.log)).append(
el("br"),

View File

@ -108,6 +108,17 @@ export function page({ pkg, info }){
" clean up procedures when the component is removed from the app."
),
),
el("p").append(
"To provide intuitive behaviour, similar also to how the life-cycle events works in other",
" frameworks/libraries, deka-dom-el library ensures that ", el("code", "on.connected"),
" and ", el("code", "on.disconnected"), " are called only once and only when the element",
" is (dis)connected to live DOM. The solution is inspired by ", el("a", { textContent: "Vue", href: "https://vuejs.org/guide/extras/web-components.html#lifecycle", title: "Vue and Web Components | Lifecycle" }), ".",
" For using native behaviour re-(dis)connecting element, use:"
),
el("ul").append(
el("li").append("custom ", el("code", "MutationObserver"), " or logic in (dis)", el(code, "connectedCallback"), " or…"),
el("li").append("re-add ", el("code", "on.connected"), " or ", el("code", "on.disconnected"), " listeners.")
),
el(h3, "Final notes"),
el("p", "The library also provides a method to dispatch the events."),

View File

@ -84,7 +84,7 @@ function todoComponent({ textContent, value }){
return el("li").append(
O.el(is_editable, is=> is
? el("input", { value: textContent(), type: "text" }, onedited)
: el("span", { textContent, onclick: is_editable.bind(null, true) }),
: el("span", { textContent, onclick: is_editable.bind(null, true) })
),
el("button", { type: "button", value, textContent: "-" }, onclick)
);

12
index.d.ts vendored
View File

@ -1,16 +1,11 @@
import { Observable } from "./observables";
type CustomElementTagNameMap= { '#text': Text, '#comment': Comment }
declare global {
interface ddePublicElementTagNameMap{
}
}
type SupportedElement=
HTMLElementTagNameMap[keyof HTMLElementTagNameMap]
| SVGElementTagNameMap[keyof SVGElementTagNameMap]
| MathMLElementTagNameMap[keyof MathMLElementTagNameMap]
| CustomElementTagNameMap[keyof CustomElementTagNameMap]
| ddePublicElementTagNameMap[keyof ddePublicElementTagNameMap];
declare global {
type ddeComponentAttributes= Record<any, any> | undefined;
type ddeElementAddon<El extends SupportedElement | DocumentFragment>= (element: El)=> El | void;
@ -48,7 +43,7 @@ export function classListDeclarative<El extends SupportedElement>(element: El, c
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(element: El, attr: ATT, value: ElementAttributes<El>[ATT]): ElementAttributes<El>[ATT]
type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap & ddePublicElementTagNameMap
type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap;
type textContent= string | ( (set?: string)=> string ); // TODO: for some reason `Observable<string, any>` leads to `attrs?: any`
export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap & string,
@ -188,9 +183,10 @@ export function customElementWithDDE<EL extends HTMLElement>(custom_element: EL)
export function lifecycleToEvents<EL extends HTMLElement>(custom_element: EL): EL
export function observedAttributes(custom_element: HTMLElement): Record<string, string>
/* TypeScript MEH // TODO for SVG */
type ddeAppend<el>= (...nodes: (Node | string)[])=> el;
/* TypeScript MEH */
declare global{
type ddeAppend<el>= (...nodes: (Node | string)[])=> el;
interface ddeDocumentFragment extends DocumentFragment{ append: ddeAppend<ddeDocumentFragment>; }
interface ddeHTMLElement extends HTMLElement{ append: ddeAppend<ddeHTMLElement>; }
interface ddeSVGElement extends SVGElement{ append: ddeAppend<ddeSVGElement>; }

View File

@ -71,6 +71,12 @@
"brotli": false
},
{
"path": "./index-with-observables.js",
"limit": "15 kB",
"gzip": false,
"brotli": false
},
{
"path": "./index-with-observables.js",
"limit": "5 kB"