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

Compare commits

..

No commits in common. "8318915d7440cbcb2bd9a8e07624927a2a6684c6" and "13c75fede1f0a67bb2aecc02f737ef9112b86e42" have entirely different histories.

24 changed files with 267 additions and 325 deletions

File diff suppressed because one or more lines are too long

40
dist/dde.js vendored

File diff suppressed because one or more lines are too long

View File

@ -3,7 +3,6 @@ type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typ
//type SymbolObservable= Symbol; //type SymbolObservable= Symbol;
type SymbolOnclear= symbol; type SymbolOnclear= symbol;
type Actions<V>= Record<string | SymbolOnclear, Action<V>>; type Actions<V>= Record<string | SymbolOnclear, Action<V>>;
type OnListenerOptions= Pick<AddEventListenerOptions, "signal"> & { first_time?: boolean };
interface observable{ interface observable{
_: Symbol _: Symbol
/** /**
@ -41,7 +40,7 @@ interface observable{
...params: A[N] extends (...args: infer P)=> any ? P : never ...params: A[N] extends (...args: infer P)=> any ? P : never
): void; ): void;
clear(...observables: Observable<any, any>[]): void; clear(...observables: Observable<any, any>[]): void;
on<T>(observable: Observable<T, any>, onchange: (a: T)=> void, options?: OnListenerOptions): void; on<T>(observable: Observable<T, any>, onchange: (a: T)=> void, options?: AddEventListenerOptions): void;
symbols: { symbols: {
//observable: SymbolObservable; //observable: SymbolObservable;
onclear: SymbolOnclear; onclear: SymbolOnclear;
@ -55,7 +54,7 @@ interface observable{
* */ * */
el<S extends any>(observable: Observable<S, any>, el: (v: S)=> Element | Element[] | DocumentFragment): DocumentFragment; el<S extends any>(observable: Observable<S, any>, el: (v: S)=> Element | Element[] | DocumentFragment): DocumentFragment;
observedAttributes(custom_element: HTMLElement): Record<string, Observable<any, any>>; attribute(name: string, initial?: string): Observable<string, {}>;
} }
export const observable: observable; export const observable: observable;
export const O: observable; export const O: observable;
@ -65,11 +64,16 @@ declare global {
type ddeActions<V>= Actions<V> type ddeActions<V>= Actions<V>
} }
type CustomElementTagNameMap= { '#text': Text, '#comment': Comment } type CustomElementTagNameMap= { '#text': Text, '#comment': Comment }
declare global {
interface ddePublicElementTagNameMap{
}
}
type SupportedElement= type SupportedElement=
HTMLElementTagNameMap[keyof HTMLElementTagNameMap] HTMLElementTagNameMap[keyof HTMLElementTagNameMap]
| SVGElementTagNameMap[keyof SVGElementTagNameMap] | SVGElementTagNameMap[keyof SVGElementTagNameMap]
| MathMLElementTagNameMap[keyof MathMLElementTagNameMap] | MathMLElementTagNameMap[keyof MathMLElementTagNameMap]
| CustomElementTagNameMap[keyof CustomElementTagNameMap] | CustomElementTagNameMap[keyof CustomElementTagNameMap]
| ddePublicElementTagNameMap[keyof ddePublicElementTagNameMap];
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>= (element: El)=> El | void;
@ -108,7 +112,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 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 & ddePublicElementTagNameMap
type textContent= string | ( (set?: string)=> string ); // TODO: for some reason `Observable<string, any>` leads to `attrs?: any` type textContent= string | ( (set?: string)=> string ); // TODO: for some reason `Observable<string, any>` leads to `attrs?: any`
export function el< export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap & string, TAG extends keyof ExtendedHTMLElementTagNameMap & string,
@ -235,23 +239,9 @@ export const scope: {
pop(): ReturnType<Array<Scope>["pop"]>, pop(): ReturnType<Array<Scope>["pop"]>,
}; };
export function customElementRender< /* TypeScript MEH // TODO for SVG */
EL extends HTMLElement, type ddeAppend<el>= (...nodes: (Node | string)[])=> el;
P extends any = Record<string, any>
>(
custom_element: EL,
target: ShadowRoot | 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{ declare global{
type ddeAppend<el>= (...nodes: (Node | string)[])=> el;
interface ddeDocumentFragment extends DocumentFragment{ append: ddeAppend<ddeDocumentFragment>; } interface ddeDocumentFragment extends DocumentFragment{ append: ddeAppend<ddeDocumentFragment>; }
interface ddeHTMLElement extends HTMLElement{ append: ddeAppend<ddeHTMLElement>; } interface ddeHTMLElement extends HTMLElement{ append: ddeAppend<ddeHTMLElement>; }
interface ddeSVGElement extends SVGElement{ append: ddeAppend<ddeSVGElement>; } interface ddeSVGElement extends SVGElement{ append: ddeAppend<ddeSVGElement>; }

File diff suppressed because one or more lines are too long

30
dist/esm.d.ts vendored
View File

@ -3,7 +3,6 @@ type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typ
//type SymbolObservable= Symbol; //type SymbolObservable= Symbol;
type SymbolOnclear= symbol; type SymbolOnclear= symbol;
type Actions<V>= Record<string | SymbolOnclear, Action<V>>; type Actions<V>= Record<string | SymbolOnclear, Action<V>>;
type OnListenerOptions= Pick<AddEventListenerOptions, "signal"> & { first_time?: boolean };
interface observable{ interface observable{
_: Symbol _: Symbol
/** /**
@ -41,7 +40,7 @@ interface observable{
...params: A[N] extends (...args: infer P)=> any ? P : never ...params: A[N] extends (...args: infer P)=> any ? P : never
): void; ): void;
clear(...observables: Observable<any, any>[]): void; clear(...observables: Observable<any, any>[]): void;
on<T>(observable: Observable<T, any>, onchange: (a: T)=> void, options?: OnListenerOptions): void; on<T>(observable: Observable<T, any>, onchange: (a: T)=> void, options?: AddEventListenerOptions): void;
symbols: { symbols: {
//observable: SymbolObservable; //observable: SymbolObservable;
onclear: SymbolOnclear; onclear: SymbolOnclear;
@ -55,7 +54,7 @@ interface observable{
* */ * */
el<S extends any>(observable: Observable<S, any>, el: (v: S)=> Element | Element[] | DocumentFragment): DocumentFragment; el<S extends any>(observable: Observable<S, any>, el: (v: S)=> Element | Element[] | DocumentFragment): DocumentFragment;
observedAttributes(custom_element: HTMLElement): Record<string, Observable<any, any>>; attribute(name: string, initial?: string): Observable<string, {}>;
} }
export const observable: observable; export const observable: observable;
export const O: observable; export const O: observable;
@ -65,11 +64,16 @@ declare global {
type ddeActions<V>= Actions<V> type ddeActions<V>= Actions<V>
} }
type CustomElementTagNameMap= { '#text': Text, '#comment': Comment } type CustomElementTagNameMap= { '#text': Text, '#comment': Comment }
declare global {
interface ddePublicElementTagNameMap{
}
}
type SupportedElement= type SupportedElement=
HTMLElementTagNameMap[keyof HTMLElementTagNameMap] HTMLElementTagNameMap[keyof HTMLElementTagNameMap]
| SVGElementTagNameMap[keyof SVGElementTagNameMap] | SVGElementTagNameMap[keyof SVGElementTagNameMap]
| MathMLElementTagNameMap[keyof MathMLElementTagNameMap] | MathMLElementTagNameMap[keyof MathMLElementTagNameMap]
| CustomElementTagNameMap[keyof CustomElementTagNameMap] | CustomElementTagNameMap[keyof CustomElementTagNameMap]
| ddePublicElementTagNameMap[keyof ddePublicElementTagNameMap];
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>= (element: El)=> El | void;
@ -108,7 +112,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 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 & ddePublicElementTagNameMap
type textContent= string | ( (set?: string)=> string ); // TODO: for some reason `Observable<string, any>` leads to `attrs?: any` type textContent= string | ( (set?: string)=> string ); // TODO: for some reason `Observable<string, any>` leads to `attrs?: any`
export function el< export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap & string, TAG extends keyof ExtendedHTMLElementTagNameMap & string,
@ -235,23 +239,9 @@ export const scope: {
pop(): ReturnType<Array<Scope>["pop"]>, pop(): ReturnType<Array<Scope>["pop"]>,
}; };
export function customElementRender< /* TypeScript MEH // TODO for SVG */
EL extends HTMLElement, type ddeAppend<el>= (...nodes: (Node | string)[])=> el;
P extends any = Record<string, any>
>(
custom_element: EL,
target: ShadowRoot | 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{ declare global{
type ddeAppend<el>= (...nodes: (Node | string)[])=> el;
interface ddeDocumentFragment extends DocumentFragment{ append: ddeAppend<ddeDocumentFragment>; } interface ddeDocumentFragment extends DocumentFragment{ append: ddeAppend<ddeDocumentFragment>; }
interface ddeHTMLElement extends HTMLElement{ append: ddeAppend<ddeHTMLElement>; } interface ddeHTMLElement extends HTMLElement{ append: ddeAppend<ddeHTMLElement>; }
interface ddeSVGElement extends SVGElement{ append: ddeAppend<ddeSVGElement>; } 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

@ -1,4 +1,4 @@
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="Basic concepts of elements modifications and creations."><title>`deka-dom-el` — Elements</title><!--<dde:mark type="component" name="metaAuthor" host="this" ssr/>--><meta name="author" content="Jan Andrle"><link type="text/plain" rel="author" href="https://jaandrle.github.io/humans.txt"><meta name="generator" content="deka-dom-el"><!--<dde:mark type="component" name="metaTwitter" host="this" ssr/>--><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="twitter:title" content="deka-dom-el"><meta name="twitter:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="twitter:creator" content="@jaandrle"><!--<dde:mark type="component" name="metaFacebook" host="this" ssr/>--><meta name="og:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="og:title" content="deka-dom-el"><meta name="og:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="og:creator" content="@jaandrle"><script src="https://cdn.jsdelivr.net/npm/shiki@0.9" defer=""></script><script type="module" src="code.js.js"></script><script src="https://flems.io/flems.html" type="text/javascript" charset="utf-8"></script><link rel="stylesheet" href="global.css"></head><body><!--<dde:mark type="component" name="page" host="this" ssr/>--><!--<dde:mark type="component" name="simplePage" host="this" ssr/>--><!--<dde:mark type="component" name="header" host="this" ssr/>--><header><h1>`deka-dom-el` — Elements</h1><p>Basic concepts of elements modifications and creations.</p></header><nav><a href="https://github.com/jaandrle/deka-dom-el"><svg class="icon" viewBox="0 0 32 32"><!--<dde:mark type="component" name="iconGitHub" host="parentElement" ssr/>--><path d="M 16,0.395c -8.836,0 -16,7.163 -16,16c 0,7.069 4.585,13.067 10.942,15.182c 0.8,0.148 1.094,-0.347 1.094,-0.77c 0,-0.381 -0.015,-1.642 -0.022,-2.979c -4.452,0.968 -5.391,-1.888 -5.391,-1.888c -0.728,-1.849 -1.776,-2.341 -1.776,-2.341c -1.452,-0.993 0.11,-0.973 0.11,-0.973c 1.606,0.113 2.452,1.649 2.452,1.649c 1.427,2.446 3.743,1.739 4.656,1.33c 0.143,-1.034 0.558,-1.74 1.016,-2.14c -3.554,-0.404 -7.29,-1.777 -7.29,-7.907c 0,-1.747 0.625,-3.174 1.649,-4.295c -0.166,-0.403 -0.714,-2.03 0.155,-4.234c 0,0 1.344,-0.43 4.401,1.64c 1.276,-0.355 2.645,-0.532 4.005,-0.539c 1.359,0.006 2.729,0.184 4.008,0.539c 3.054,-2.07 4.395,-1.64 4.395,-1.64c 0.871,2.204 0.323,3.831 0.157,4.234c 1.026,1.12 1.647,2.548 1.647,4.295c 0,6.145 -3.743,7.498 -7.306,7.895c 0.574,0.497 1.085,1.47 1.085,2.963c 0,2.141 -0.019,3.864 -0.019,4.391c 0,0.426 0.288,0.925 1.099,0.768c 6.354,-2.118 10.933,-8.113 10.933,-15.18c 0,-8.837 -7.164,-16 -16,-16Z"></path></svg>GitHub</a><a href="./" title="Introducing a&nbsp;library.">1. Introduction</a><a href="p02-elements" title="Basic concepts of elements modifications and creations." class="current">2. Elements</a><a href="p03-events" title="Using not only events in UI declaratively.">3. Events and Addons</a><a href="p04-observables" title="Handling reactivity in UI via observables.">4. Observables and reactivity</a><a href="p05-scopes" title="Organizing UI into components">5. Scopes and components</a><a href="p06-customElement" title="Using custom elements in combinantion with DDE">6. Custom elements</a></nav><main><h2>Native JavaScript DOM elements creations</h2><p>Lets go through all patterns we would like to use and what needs to be improved for better experience.</p><div class="code" data-js="todo"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm.js <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="Basic concepts of elements modifications and creations."><title>`deka-dom-el` — Elements</title><!--<dde:mark type="component" name="metaAuthor" host="this" ssr/>--><meta name="author" content="Jan Andrle"><link type="text/plain" rel="author" href="https://jaandrle.github.io/humans.txt"><meta name="generator" content="deka-dom-el"><!--<dde:mark type="component" name="metaTwitter" host="this" ssr/>--><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="twitter:title" content="deka-dom-el"><meta name="twitter:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="twitter:creator" content="@jaandrle"><!--<dde:mark type="component" name="metaFacebook" host="this" ssr/>--><meta name="og:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="og:title" content="deka-dom-el"><meta name="og:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="og:creator" content="@jaandrle"><script src="https://cdn.jsdelivr.net/npm/shiki" defer=""></script><script type="module" src="code.js.js"></script><script src="https://flems.io/flems.html" type="text/javascript" charset="utf-8"></script><link rel="stylesheet" href="global.css"></head><body><!--<dde:mark type="component" name="page" host="this" ssr/>--><!--<dde:mark type="component" name="simplePage" host="this" ssr/>--><!--<dde:mark type="component" name="header" host="this" ssr/>--><header><h1>`deka-dom-el` — Elements</h1><p>Basic concepts of elements modifications and creations.</p></header><nav><a href="https://github.com/jaandrle/deka-dom-el"><svg class="icon" viewBox="0 0 32 32"><!--<dde:mark type="component" name="iconGitHub" host="parentElement" ssr/>--><path d="M 16,0.395c -8.836,0 -16,7.163 -16,16c 0,7.069 4.585,13.067 10.942,15.182c 0.8,0.148 1.094,-0.347 1.094,-0.77c 0,-0.381 -0.015,-1.642 -0.022,-2.979c -4.452,0.968 -5.391,-1.888 -5.391,-1.888c -0.728,-1.849 -1.776,-2.341 -1.776,-2.341c -1.452,-0.993 0.11,-0.973 0.11,-0.973c 1.606,0.113 2.452,1.649 2.452,1.649c 1.427,2.446 3.743,1.739 4.656,1.33c 0.143,-1.034 0.558,-1.74 1.016,-2.14c -3.554,-0.404 -7.29,-1.777 -7.29,-7.907c 0,-1.747 0.625,-3.174 1.649,-4.295c -0.166,-0.403 -0.714,-2.03 0.155,-4.234c 0,0 1.344,-0.43 4.401,1.64c 1.276,-0.355 2.645,-0.532 4.005,-0.539c 1.359,0.006 2.729,0.184 4.008,0.539c 3.054,-2.07 4.395,-1.64 4.395,-1.64c 0.871,2.204 0.323,3.831 0.157,4.234c 1.026,1.12 1.647,2.548 1.647,4.295c 0,6.145 -3.743,7.498 -7.306,7.895c 0.574,0.497 1.085,1.47 1.085,2.963c 0,2.141 -0.019,3.864 -0.019,4.391c 0,0.426 0.288,0.925 1.099,0.768c 6.354,-2.118 10.933,-8.113 10.933,-15.18c 0,-8.837 -7.164,-16 -16,-16Z"></path></svg>GitHub</a><a href="./" title="Introducing a&nbsp;library.">1. Introduction</a><a href="p02-elements" title="Basic concepts of elements modifications and creations." class="current">2. Elements</a><a href="p03-events" title="Using not only events in UI declaratively.">3. Events and Addons</a><a href="p04-observables" title="Handling reactivity in UI via observables.">4. Observables and reactivity</a><a href="p05-scopes" title="Organizing UI into components">5. Scopes and components</a><a href="p06-customElement" title="Using custom elements in combinantion with DDE">6. Custom elements</a></nav><main><h2>Native JavaScript DOM elements creations</h2><p>Lets go through all patterns we would like to use and what needs to be improved for better experience.</p><div class="code" data-js="todo"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm.js
import { import {
assign, assign,
el, createElement, el, createElement,

View File

@ -1,4 +1,4 @@
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="Using not only events in UI declaratively."><title>`deka-dom-el` — Events and Addons</title><!--<dde:mark type="component" name="metaAuthor" host="this" ssr/>--><meta name="author" content="Jan Andrle"><link type="text/plain" rel="author" href="https://jaandrle.github.io/humans.txt"><meta name="generator" content="deka-dom-el"><!--<dde:mark type="component" name="metaTwitter" host="this" ssr/>--><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="twitter:title" content="deka-dom-el"><meta name="twitter:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="twitter:creator" content="@jaandrle"><!--<dde:mark type="component" name="metaFacebook" host="this" ssr/>--><meta name="og:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="og:title" content="deka-dom-el"><meta name="og:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="og:creator" content="@jaandrle"><script src="https://cdn.jsdelivr.net/npm/shiki@0.9" defer=""></script><script type="module" src="code.js.js"></script><script src="https://flems.io/flems.html" type="text/javascript" charset="utf-8"></script><link rel="stylesheet" href="global.css"></head><body><!--<dde:mark type="component" name="page" host="this" ssr/>--><!--<dde:mark type="component" name="simplePage" host="this" ssr/>--><!--<dde:mark type="component" name="header" host="this" ssr/>--><header><h1>`deka-dom-el` — Events and Addons</h1><p>Using not only events in UI declaratively.</p></header><nav><a href="https://github.com/jaandrle/deka-dom-el"><svg class="icon" viewBox="0 0 32 32"><!--<dde:mark type="component" name="iconGitHub" host="parentElement" ssr/>--><path d="M 16,0.395c -8.836,0 -16,7.163 -16,16c 0,7.069 4.585,13.067 10.942,15.182c 0.8,0.148 1.094,-0.347 1.094,-0.77c 0,-0.381 -0.015,-1.642 -0.022,-2.979c -4.452,0.968 -5.391,-1.888 -5.391,-1.888c -0.728,-1.849 -1.776,-2.341 -1.776,-2.341c -1.452,-0.993 0.11,-0.973 0.11,-0.973c 1.606,0.113 2.452,1.649 2.452,1.649c 1.427,2.446 3.743,1.739 4.656,1.33c 0.143,-1.034 0.558,-1.74 1.016,-2.14c -3.554,-0.404 -7.29,-1.777 -7.29,-7.907c 0,-1.747 0.625,-3.174 1.649,-4.295c -0.166,-0.403 -0.714,-2.03 0.155,-4.234c 0,0 1.344,-0.43 4.401,1.64c 1.276,-0.355 2.645,-0.532 4.005,-0.539c 1.359,0.006 2.729,0.184 4.008,0.539c 3.054,-2.07 4.395,-1.64 4.395,-1.64c 0.871,2.204 0.323,3.831 0.157,4.234c 1.026,1.12 1.647,2.548 1.647,4.295c 0,6.145 -3.743,7.498 -7.306,7.895c 0.574,0.497 1.085,1.47 1.085,2.963c 0,2.141 -0.019,3.864 -0.019,4.391c 0,0.426 0.288,0.925 1.099,0.768c 6.354,-2.118 10.933,-8.113 10.933,-15.18c 0,-8.837 -7.164,-16 -16,-16Z"></path></svg>GitHub</a><a href="./" title="Introducing a&nbsp;library.">1. Introduction</a><a href="p02-elements" title="Basic concepts of elements modifications and creations.">2. Elements</a><a href="p03-events" title="Using not only events in UI declaratively." class="current">3. Events and Addons</a><a href="p04-observables" title="Handling reactivity in UI via observables.">4. Observables and reactivity</a><a href="p05-scopes" title="Organizing UI into components">5. Scopes and components</a><a href="p06-customElement" title="Using custom elements in combinantion with DDE">6. Custom elements</a></nav><main><h2>Listenning to the native DOM events and other Addons</h2><p>We quickly introduce helper to listening to the native DOM events. And library syntax/pattern so-called <em>Addon</em> to incorporate not only this in UI templates declaratively.</p><div class="code" data-js="todo"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm.js <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="Using not only events in UI declaratively."><title>`deka-dom-el` — Events and Addons</title><!--<dde:mark type="component" name="metaAuthor" host="this" ssr/>--><meta name="author" content="Jan Andrle"><link type="text/plain" rel="author" href="https://jaandrle.github.io/humans.txt"><meta name="generator" content="deka-dom-el"><!--<dde:mark type="component" name="metaTwitter" host="this" ssr/>--><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="twitter:title" content="deka-dom-el"><meta name="twitter:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="twitter:creator" content="@jaandrle"><!--<dde:mark type="component" name="metaFacebook" host="this" ssr/>--><meta name="og:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="og:title" content="deka-dom-el"><meta name="og:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="og:creator" content="@jaandrle"><script src="https://cdn.jsdelivr.net/npm/shiki" defer=""></script><script type="module" src="code.js.js"></script><script src="https://flems.io/flems.html" type="text/javascript" charset="utf-8"></script><link rel="stylesheet" href="global.css"></head><body><!--<dde:mark type="component" name="page" host="this" ssr/>--><!--<dde:mark type="component" name="simplePage" host="this" ssr/>--><!--<dde:mark type="component" name="header" host="this" ssr/>--><header><h1>`deka-dom-el` — Events and Addons</h1><p>Using not only events in UI declaratively.</p></header><nav><a href="https://github.com/jaandrle/deka-dom-el"><svg class="icon" viewBox="0 0 32 32"><!--<dde:mark type="component" name="iconGitHub" host="parentElement" ssr/>--><path d="M 16,0.395c -8.836,0 -16,7.163 -16,16c 0,7.069 4.585,13.067 10.942,15.182c 0.8,0.148 1.094,-0.347 1.094,-0.77c 0,-0.381 -0.015,-1.642 -0.022,-2.979c -4.452,0.968 -5.391,-1.888 -5.391,-1.888c -0.728,-1.849 -1.776,-2.341 -1.776,-2.341c -1.452,-0.993 0.11,-0.973 0.11,-0.973c 1.606,0.113 2.452,1.649 2.452,1.649c 1.427,2.446 3.743,1.739 4.656,1.33c 0.143,-1.034 0.558,-1.74 1.016,-2.14c -3.554,-0.404 -7.29,-1.777 -7.29,-7.907c 0,-1.747 0.625,-3.174 1.649,-4.295c -0.166,-0.403 -0.714,-2.03 0.155,-4.234c 0,0 1.344,-0.43 4.401,1.64c 1.276,-0.355 2.645,-0.532 4.005,-0.539c 1.359,0.006 2.729,0.184 4.008,0.539c 3.054,-2.07 4.395,-1.64 4.395,-1.64c 0.871,2.204 0.323,3.831 0.157,4.234c 1.026,1.12 1.647,2.548 1.647,4.295c 0,6.145 -3.743,7.498 -7.306,7.895c 0.574,0.497 1.085,1.47 1.085,2.963c 0,2.141 -0.019,3.864 -0.019,4.391c 0,0.426 0.288,0.925 1.099,0.768c 6.354,-2.118 10.933,-8.113 10.933,-15.18c 0,-8.837 -7.164,-16 -16,-16Z"></path></svg>GitHub</a><a href="./" title="Introducing a&nbsp;library.">1. Introduction</a><a href="p02-elements" title="Basic concepts of elements modifications and creations.">2. Elements</a><a href="p03-events" title="Using not only events in UI declaratively." class="current">3. Events and Addons</a><a href="p04-observables" title="Handling reactivity in UI via observables.">4. Observables and reactivity</a><a href="p05-scopes" title="Organizing UI into components">5. Scopes and components</a><a href="p06-customElement" title="Using custom elements in combinantion with DDE">6. Custom elements</a></nav><main><h2>Listenning to the native DOM events and other Addons</h2><p>We quickly introduce helper to listening to the native DOM events. And library syntax/pattern so-called <em>Addon</em> to incorporate not only this in UI templates declaratively.</p><div class="code" data-js="todo"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm.js
import { on, dispatchEvent } from "deka-dom-el"; import { on, dispatchEvent } from "deka-dom-el";
/** @type {ddeElementAddon} */ /** @type {ddeElementAddon} */
@ -67,7 +67,7 @@ document.body.append(
function log({ type, detail }){ function log({ type, detail }){
console.log({ _this: this, 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><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"; </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";
document.body.append( document.body.append(
el("p", "Listenning to `test` event.", on("test", console.log)).append( el("p", "Listenning to `test` event.", on("test", console.log)).append(
el("br"), el("br"),

View File

@ -1,4 +1,4 @@
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="Handling reactivity in UI via observables."><title>`deka-dom-el` — Observables and reactivity</title><!--<dde:mark type="component" name="metaAuthor" host="this" ssr/>--><meta name="author" content="Jan Andrle"><link type="text/plain" rel="author" href="https://jaandrle.github.io/humans.txt"><meta name="generator" content="deka-dom-el"><!--<dde:mark type="component" name="metaTwitter" host="this" ssr/>--><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="twitter:title" content="deka-dom-el"><meta name="twitter:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="twitter:creator" content="@jaandrle"><!--<dde:mark type="component" name="metaFacebook" host="this" ssr/>--><meta name="og:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="og:title" content="deka-dom-el"><meta name="og:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="og:creator" content="@jaandrle"><script src="https://cdn.jsdelivr.net/npm/shiki@0.9" defer=""></script><script type="module" src="code.js.js"></script><script src="https://flems.io/flems.html" type="text/javascript" charset="utf-8"></script><link rel="stylesheet" href="global.css"></head><body><!--<dde:mark type="component" name="page" host="this" ssr/>--><!--<dde:mark type="component" name="simplePage" host="this" ssr/>--><!--<dde:mark type="component" name="header" host="this" ssr/>--><header><h1>`deka-dom-el` — Observables and reactivity</h1><p>Handling reactivity in UI via observables.</p></header><nav><a href="https://github.com/jaandrle/deka-dom-el"><svg class="icon" viewBox="0 0 32 32"><!--<dde:mark type="component" name="iconGitHub" host="parentElement" ssr/>--><path d="M 16,0.395c -8.836,0 -16,7.163 -16,16c 0,7.069 4.585,13.067 10.942,15.182c 0.8,0.148 1.094,-0.347 1.094,-0.77c 0,-0.381 -0.015,-1.642 -0.022,-2.979c -4.452,0.968 -5.391,-1.888 -5.391,-1.888c -0.728,-1.849 -1.776,-2.341 -1.776,-2.341c -1.452,-0.993 0.11,-0.973 0.11,-0.973c 1.606,0.113 2.452,1.649 2.452,1.649c 1.427,2.446 3.743,1.739 4.656,1.33c 0.143,-1.034 0.558,-1.74 1.016,-2.14c -3.554,-0.404 -7.29,-1.777 -7.29,-7.907c 0,-1.747 0.625,-3.174 1.649,-4.295c -0.166,-0.403 -0.714,-2.03 0.155,-4.234c 0,0 1.344,-0.43 4.401,1.64c 1.276,-0.355 2.645,-0.532 4.005,-0.539c 1.359,0.006 2.729,0.184 4.008,0.539c 3.054,-2.07 4.395,-1.64 4.395,-1.64c 0.871,2.204 0.323,3.831 0.157,4.234c 1.026,1.12 1.647,2.548 1.647,4.295c 0,6.145 -3.743,7.498 -7.306,7.895c 0.574,0.497 1.085,1.47 1.085,2.963c 0,2.141 -0.019,3.864 -0.019,4.391c 0,0.426 0.288,0.925 1.099,0.768c 6.354,-2.118 10.933,-8.113 10.933,-15.18c 0,-8.837 -7.164,-16 -16,-16Z"></path></svg>GitHub</a><a href="./" title="Introducing a&nbsp;library.">1. Introduction</a><a href="p02-elements" title="Basic concepts of elements modifications and creations.">2. Elements</a><a href="p03-events" title="Using not only events in UI declaratively.">3. Events and Addons</a><a href="p04-observables" title="Handling reactivity in UI via observables." class="current">4. Observables and reactivity</a><a href="p05-scopes" title="Organizing UI into components">5. Scopes and components</a><a href="p06-customElement" title="Using custom elements in combinantion with DDE">6. Custom elements</a></nav><main><h2>Using observables to manage reactivity</h2><p>How a program responds to variable data or user interactions is one of the fundamental problems of programming. If we desire to solve the issue in a declarative manner, observables may be a viable approach.</p><div class="code" data-js="todo"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="Handling reactivity in UI via observables."><title>`deka-dom-el` — Observables and reactivity</title><!--<dde:mark type="component" name="metaAuthor" host="this" ssr/>--><meta name="author" content="Jan Andrle"><link type="text/plain" rel="author" href="https://jaandrle.github.io/humans.txt"><meta name="generator" content="deka-dom-el"><!--<dde:mark type="component" name="metaTwitter" host="this" ssr/>--><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="twitter:title" content="deka-dom-el"><meta name="twitter:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="twitter:creator" content="@jaandrle"><!--<dde:mark type="component" name="metaFacebook" host="this" ssr/>--><meta name="og:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="og:title" content="deka-dom-el"><meta name="og:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="og:creator" content="@jaandrle"><script src="https://cdn.jsdelivr.net/npm/shiki" defer=""></script><script type="module" src="code.js.js"></script><script src="https://flems.io/flems.html" type="text/javascript" charset="utf-8"></script><link rel="stylesheet" href="global.css"></head><body><!--<dde:mark type="component" name="page" host="this" ssr/>--><!--<dde:mark type="component" name="simplePage" host="this" ssr/>--><!--<dde:mark type="component" name="header" host="this" ssr/>--><header><h1>`deka-dom-el` — Observables and reactivity</h1><p>Handling reactivity in UI via observables.</p></header><nav><a href="https://github.com/jaandrle/deka-dom-el"><svg class="icon" viewBox="0 0 32 32"><!--<dde:mark type="component" name="iconGitHub" host="parentElement" ssr/>--><path d="M 16,0.395c -8.836,0 -16,7.163 -16,16c 0,7.069 4.585,13.067 10.942,15.182c 0.8,0.148 1.094,-0.347 1.094,-0.77c 0,-0.381 -0.015,-1.642 -0.022,-2.979c -4.452,0.968 -5.391,-1.888 -5.391,-1.888c -0.728,-1.849 -1.776,-2.341 -1.776,-2.341c -1.452,-0.993 0.11,-0.973 0.11,-0.973c 1.606,0.113 2.452,1.649 2.452,1.649c 1.427,2.446 3.743,1.739 4.656,1.33c 0.143,-1.034 0.558,-1.74 1.016,-2.14c -3.554,-0.404 -7.29,-1.777 -7.29,-7.907c 0,-1.747 0.625,-3.174 1.649,-4.295c -0.166,-0.403 -0.714,-2.03 0.155,-4.234c 0,0 1.344,-0.43 4.401,1.64c 1.276,-0.355 2.645,-0.532 4.005,-0.539c 1.359,0.006 2.729,0.184 4.008,0.539c 3.054,-2.07 4.395,-1.64 4.395,-1.64c 0.871,2.204 0.323,3.831 0.157,4.234c 1.026,1.12 1.647,2.548 1.647,4.295c 0,6.145 -3.743,7.498 -7.306,7.895c 0.574,0.497 1.085,1.47 1.085,2.963c 0,2.141 -0.019,3.864 -0.019,4.391c 0,0.426 0.288,0.925 1.099,0.768c 6.354,-2.118 10.933,-8.113 10.933,-15.18c 0,-8.837 -7.164,-16 -16,-16Z"></path></svg>GitHub</a><a href="./" title="Introducing a&nbsp;library.">1. Introduction</a><a href="p02-elements" title="Basic concepts of elements modifications and creations.">2. Elements</a><a href="p03-events" title="Using not only events in UI declaratively.">3. Events and Addons</a><a href="p04-observables" title="Handling reactivity in UI via observables." class="current">4. Observables and reactivity</a><a href="p05-scopes" title="Organizing UI into components">5. Scopes and components</a><a href="p06-customElement" title="Using custom elements in combinantion with DDE">6. Custom elements</a></nav><main><h2>Using observables to manage reactivity</h2><p>How a program responds to variable data or user interactions is one of the fundamental problems of programming. If we desire to solve the issue in a declarative manner, observables may be a viable approach.</p><div class="code" data-js="todo"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js
import { O, observable } from "deka-dom-el/observables"; import { O, observable } from "deka-dom-el/observables";
O===observable O===observable
/** @type {ddeObservable} */ /** @type {ddeObservable} */

View File

@ -1,4 +1,4 @@
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="Organizing UI into components"><title>`deka-dom-el` — Scopes and components</title><!--<dde:mark type="component" name="metaAuthor" host="this" ssr/>--><meta name="author" content="Jan Andrle"><link type="text/plain" rel="author" href="https://jaandrle.github.io/humans.txt"><meta name="generator" content="deka-dom-el"><!--<dde:mark type="component" name="metaTwitter" host="this" ssr/>--><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="twitter:title" content="deka-dom-el"><meta name="twitter:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="twitter:creator" content="@jaandrle"><!--<dde:mark type="component" name="metaFacebook" host="this" ssr/>--><meta name="og:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="og:title" content="deka-dom-el"><meta name="og:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="og:creator" content="@jaandrle"><script src="https://cdn.jsdelivr.net/npm/shiki@0.9" defer=""></script><script type="module" src="code.js.js"></script><script src="https://flems.io/flems.html" type="text/javascript" charset="utf-8"></script><link rel="stylesheet" href="global.css"></head><body><!--<dde:mark type="component" name="page" host="this" ssr/>--><!--<dde:mark type="component" name="simplePage" host="this" ssr/>--><!--<dde:mark type="component" name="header" host="this" ssr/>--><header><h1>`deka-dom-el` — Scopes and components</h1><p>Organizing UI into components</p></header><nav><a href="https://github.com/jaandrle/deka-dom-el"><svg class="icon" viewBox="0 0 32 32"><!--<dde:mark type="component" name="iconGitHub" host="parentElement" ssr/>--><path d="M 16,0.395c -8.836,0 -16,7.163 -16,16c 0,7.069 4.585,13.067 10.942,15.182c 0.8,0.148 1.094,-0.347 1.094,-0.77c 0,-0.381 -0.015,-1.642 -0.022,-2.979c -4.452,0.968 -5.391,-1.888 -5.391,-1.888c -0.728,-1.849 -1.776,-2.341 -1.776,-2.341c -1.452,-0.993 0.11,-0.973 0.11,-0.973c 1.606,0.113 2.452,1.649 2.452,1.649c 1.427,2.446 3.743,1.739 4.656,1.33c 0.143,-1.034 0.558,-1.74 1.016,-2.14c -3.554,-0.404 -7.29,-1.777 -7.29,-7.907c 0,-1.747 0.625,-3.174 1.649,-4.295c -0.166,-0.403 -0.714,-2.03 0.155,-4.234c 0,0 1.344,-0.43 4.401,1.64c 1.276,-0.355 2.645,-0.532 4.005,-0.539c 1.359,0.006 2.729,0.184 4.008,0.539c 3.054,-2.07 4.395,-1.64 4.395,-1.64c 0.871,2.204 0.323,3.831 0.157,4.234c 1.026,1.12 1.647,2.548 1.647,4.295c 0,6.145 -3.743,7.498 -7.306,7.895c 0.574,0.497 1.085,1.47 1.085,2.963c 0,2.141 -0.019,3.864 -0.019,4.391c 0,0.426 0.288,0.925 1.099,0.768c 6.354,-2.118 10.933,-8.113 10.933,-15.18c 0,-8.837 -7.164,-16 -16,-16Z"></path></svg>GitHub</a><a href="./" title="Introducing a&nbsp;library.">1. Introduction</a><a href="p02-elements" title="Basic concepts of elements modifications and creations.">2. Elements</a><a href="p03-events" title="Using not only events in UI declaratively.">3. Events and Addons</a><a href="p04-observables" title="Handling reactivity in UI via observables.">4. Observables and reactivity</a><a href="p05-scopes" title="Organizing UI into components" class="current">5. Scopes and components</a><a href="p06-customElement" title="Using custom elements in combinantion with DDE">6. Custom elements</a></nav><main><h2>Using functions as UI components</h2><p>For state-less components we can use functions as UI components (see “Elements” page). But in real life, we may need to handle the component live-cycle and provide JavaScript the way to properly use the <a href="https://developer.mozilla.org/en-US/docs/Glossary/Garbage_collection" title="Garbage collection | MDN">Garbage collection</a>.</p><div class="code" data-js="todo"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="Organizing UI into components"><title>`deka-dom-el` — Scopes and components</title><!--<dde:mark type="component" name="metaAuthor" host="this" ssr/>--><meta name="author" content="Jan Andrle"><link type="text/plain" rel="author" href="https://jaandrle.github.io/humans.txt"><meta name="generator" content="deka-dom-el"><!--<dde:mark type="component" name="metaTwitter" host="this" ssr/>--><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="twitter:title" content="deka-dom-el"><meta name="twitter:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="twitter:creator" content="@jaandrle"><!--<dde:mark type="component" name="metaFacebook" host="this" ssr/>--><meta name="og:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="og:title" content="deka-dom-el"><meta name="og:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="og:creator" content="@jaandrle"><script src="https://cdn.jsdelivr.net/npm/shiki" defer=""></script><script type="module" src="code.js.js"></script><script src="https://flems.io/flems.html" type="text/javascript" charset="utf-8"></script><link rel="stylesheet" href="global.css"></head><body><!--<dde:mark type="component" name="page" host="this" ssr/>--><!--<dde:mark type="component" name="simplePage" host="this" ssr/>--><!--<dde:mark type="component" name="header" host="this" ssr/>--><header><h1>`deka-dom-el` — Scopes and components</h1><p>Organizing UI into components</p></header><nav><a href="https://github.com/jaandrle/deka-dom-el"><svg class="icon" viewBox="0 0 32 32"><!--<dde:mark type="component" name="iconGitHub" host="parentElement" ssr/>--><path d="M 16,0.395c -8.836,0 -16,7.163 -16,16c 0,7.069 4.585,13.067 10.942,15.182c 0.8,0.148 1.094,-0.347 1.094,-0.77c 0,-0.381 -0.015,-1.642 -0.022,-2.979c -4.452,0.968 -5.391,-1.888 -5.391,-1.888c -0.728,-1.849 -1.776,-2.341 -1.776,-2.341c -1.452,-0.993 0.11,-0.973 0.11,-0.973c 1.606,0.113 2.452,1.649 2.452,1.649c 1.427,2.446 3.743,1.739 4.656,1.33c 0.143,-1.034 0.558,-1.74 1.016,-2.14c -3.554,-0.404 -7.29,-1.777 -7.29,-7.907c 0,-1.747 0.625,-3.174 1.649,-4.295c -0.166,-0.403 -0.714,-2.03 0.155,-4.234c 0,0 1.344,-0.43 4.401,1.64c 1.276,-0.355 2.645,-0.532 4.005,-0.539c 1.359,0.006 2.729,0.184 4.008,0.539c 3.054,-2.07 4.395,-1.64 4.395,-1.64c 0.871,2.204 0.323,3.831 0.157,4.234c 1.026,1.12 1.647,2.548 1.647,4.295c 0,6.145 -3.743,7.498 -7.306,7.895c 0.574,0.497 1.085,1.47 1.085,2.963c 0,2.141 -0.019,3.864 -0.019,4.391c 0,0.426 0.288,0.925 1.099,0.768c 6.354,-2.118 10.933,-8.113 10.933,-15.18c 0,-8.837 -7.164,-16 -16,-16Z"></path></svg>GitHub</a><a href="./" title="Introducing a&nbsp;library.">1. Introduction</a><a href="p02-elements" title="Basic concepts of elements modifications and creations.">2. Elements</a><a href="p03-events" title="Using not only events in UI declaratively.">3. Events and Addons</a><a href="p04-observables" title="Handling reactivity in UI via observables.">4. Observables and reactivity</a><a href="p05-scopes" title="Organizing UI into components" class="current">5. Scopes and components</a><a href="p06-customElement" title="Using custom elements in combinantion with DDE">6. Custom elements</a></nav><main><h2>Using functions as UI components</h2><p>For state-less components we can use functions as UI components (see “Elements” page). But in real life, we may need to handle the component live-cycle and provide JavaScript the way to properly use the <a href="https://developer.mozilla.org/en-US/docs/Glossary/Garbage_collection" title="Garbage collection | MDN">Garbage collection</a>.</p><div class="code" data-js="todo"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js
import { scope, el } from "deka-dom-el"; import { scope, el } from "deka-dom-el";
/** @type {ddeElementAddon} */ /** @type {ddeElementAddon} */
</code></div><p>The library therefore use <em>scopes</em> to provide these functionalities.</p><h3 id="h-scopes-and-hosts"><!--<dde:mark type="component" name="h3" host="parentElement" ssr/>--><a href="#h-scopes-and-hosts" tabindex="-1">#</a> Scopes and hosts</h3><p>The <strong>host</strong> is the name for the element representing the component. This is typically element returned by function. To get reference, you can use <code>scope.host()</code> to applly addons just use <code>scope.host(...&lt;addons&gt;)</code>.</p><!--<dde:mark type="component" name="example" host="this" ssr/>--><div id="code-example-1-8meex5b3tyo" class="example"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">import { el, on, scope } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js"; </code></div><p>The library therefore use <em>scopes</em> to provide these functionalities.</p><h3 id="h-scopes-and-hosts"><!--<dde:mark type="component" name="h3" host="parentElement" ssr/>--><a href="#h-scopes-and-hosts" tabindex="-1">#</a> Scopes and hosts</h3><p>The <strong>host</strong> is the name for the element representing the component. This is typically element returned by function. To get reference, you can use <code>scope.host()</code> to applly addons just use <code>scope.host(...&lt;addons&gt;)</code>.</p><!--<dde:mark type="component" name="example" host="this" ssr/>--><div id="code-example-1-8meex5b3tyo" class="example"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">import { el, on, scope } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js";

View File

@ -1,4 +1,4 @@
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="Using custom elements in combinantion with DDE"><title>`deka-dom-el` — Custom elements</title><!--<dde:mark type="component" name="metaAuthor" host="this" ssr/>--><meta name="author" content="Jan Andrle"><link type="text/plain" rel="author" href="https://jaandrle.github.io/humans.txt"><meta name="generator" content="deka-dom-el"><!--<dde:mark type="component" name="metaTwitter" host="this" ssr/>--><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="twitter:title" content="deka-dom-el"><meta name="twitter:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="twitter:creator" content="@jaandrle"><!--<dde:mark type="component" name="metaFacebook" host="this" ssr/>--><meta name="og:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="og:title" content="deka-dom-el"><meta name="og:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="og:creator" content="@jaandrle"><script src="https://cdn.jsdelivr.net/npm/shiki@0.9" defer=""></script><script type="module" src="code.js.js"></script><link rel="stylesheet" href="global.css"></head><body><!--<dde:mark type="component" name="page" host="this" ssr/>--><!--<dde:mark type="component" name="simplePage" host="this" ssr/>--><!--<dde:mark type="component" name="header" host="this" ssr/>--><header><h1>`deka-dom-el` — Custom elements</h1><p>Using custom elements in combinantion with DDE</p></header><nav><a href="https://github.com/jaandrle/deka-dom-el"><svg class="icon" viewBox="0 0 32 32"><!--<dde:mark type="component" name="iconGitHub" host="parentElement" ssr/>--><path d="M 16,0.395c -8.836,0 -16,7.163 -16,16c 0,7.069 4.585,13.067 10.942,15.182c 0.8,0.148 1.094,-0.347 1.094,-0.77c 0,-0.381 -0.015,-1.642 -0.022,-2.979c -4.452,0.968 -5.391,-1.888 -5.391,-1.888c -0.728,-1.849 -1.776,-2.341 -1.776,-2.341c -1.452,-0.993 0.11,-0.973 0.11,-0.973c 1.606,0.113 2.452,1.649 2.452,1.649c 1.427,2.446 3.743,1.739 4.656,1.33c 0.143,-1.034 0.558,-1.74 1.016,-2.14c -3.554,-0.404 -7.29,-1.777 -7.29,-7.907c 0,-1.747 0.625,-3.174 1.649,-4.295c -0.166,-0.403 -0.714,-2.03 0.155,-4.234c 0,0 1.344,-0.43 4.401,1.64c 1.276,-0.355 2.645,-0.532 4.005,-0.539c 1.359,0.006 2.729,0.184 4.008,0.539c 3.054,-2.07 4.395,-1.64 4.395,-1.64c 0.871,2.204 0.323,3.831 0.157,4.234c 1.026,1.12 1.647,2.548 1.647,4.295c 0,6.145 -3.743,7.498 -7.306,7.895c 0.574,0.497 1.085,1.47 1.085,2.963c 0,2.141 -0.019,3.864 -0.019,4.391c 0,0.426 0.288,0.925 1.099,0.768c 6.354,-2.118 10.933,-8.113 10.933,-15.18c 0,-8.837 -7.164,-16 -16,-16Z"></path></svg>GitHub</a><a href="./" title="Introducing a&nbsp;library.">1. Introduction</a><a href="p02-elements" title="Basic concepts of elements modifications and creations.">2. Elements</a><a href="p03-events" title="Using not only events in UI declaratively.">3. Events and Addons</a><a href="p04-observables" title="Handling reactivity in UI via observables.">4. Observables and reactivity</a><a href="p05-scopes" title="Organizing UI into components">5. Scopes and components</a><a href="p06-customElement" title="Using custom elements in combinantion with DDE" class="current">6. Custom elements</a></nav><main><h2>Using custom elements in combinantion with DDE</h2><p></p><div class="code" data-js="todo"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="Using custom elements in combinantion with DDE"><title>`deka-dom-el` — Custom elements</title><!--<dde:mark type="component" name="metaAuthor" host="this" ssr/>--><meta name="author" content="Jan Andrle"><link type="text/plain" rel="author" href="https://jaandrle.github.io/humans.txt"><meta name="generator" content="deka-dom-el"><!--<dde:mark type="component" name="metaTwitter" host="this" ssr/>--><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="twitter:title" content="deka-dom-el"><meta name="twitter:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="twitter:creator" content="@jaandrle"><!--<dde:mark type="component" name="metaFacebook" host="this" ssr/>--><meta name="og:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="og:title" content="deka-dom-el"><meta name="og:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="og:creator" content="@jaandrle"><script src="https://cdn.jsdelivr.net/npm/shiki" defer=""></script><script type="module" src="code.js.js"></script><link rel="stylesheet" href="global.css"></head><body><!--<dde:mark type="component" name="page" host="this" ssr/>--><!--<dde:mark type="component" name="simplePage" host="this" ssr/>--><!--<dde:mark type="component" name="header" host="this" ssr/>--><header><h1>`deka-dom-el` — Custom elements</h1><p>Using custom elements in combinantion with DDE</p></header><nav><a href="https://github.com/jaandrle/deka-dom-el"><svg class="icon" viewBox="0 0 32 32"><!--<dde:mark type="component" name="iconGitHub" host="parentElement" ssr/>--><path d="M 16,0.395c -8.836,0 -16,7.163 -16,16c 0,7.069 4.585,13.067 10.942,15.182c 0.8,0.148 1.094,-0.347 1.094,-0.77c 0,-0.381 -0.015,-1.642 -0.022,-2.979c -4.452,0.968 -5.391,-1.888 -5.391,-1.888c -0.728,-1.849 -1.776,-2.341 -1.776,-2.341c -1.452,-0.993 0.11,-0.973 0.11,-0.973c 1.606,0.113 2.452,1.649 2.452,1.649c 1.427,2.446 3.743,1.739 4.656,1.33c 0.143,-1.034 0.558,-1.74 1.016,-2.14c -3.554,-0.404 -7.29,-1.777 -7.29,-7.907c 0,-1.747 0.625,-3.174 1.649,-4.295c -0.166,-0.403 -0.714,-2.03 0.155,-4.234c 0,0 1.344,-0.43 4.401,1.64c 1.276,-0.355 2.645,-0.532 4.005,-0.539c 1.359,0.006 2.729,0.184 4.008,0.539c 3.054,-2.07 4.395,-1.64 4.395,-1.64c 0.871,2.204 0.323,3.831 0.157,4.234c 1.026,1.12 1.647,2.548 1.647,4.295c 0,6.145 -3.743,7.498 -7.306,7.895c 0.574,0.497 1.085,1.47 1.085,2.963c 0,2.141 -0.019,3.864 -0.019,4.391c 0,0.426 0.288,0.925 1.099,0.768c 6.354,-2.118 10.933,-8.113 10.933,-15.18c 0,-8.837 -7.164,-16 -16,-16Z"></path></svg>GitHub</a><a href="./" title="Introducing a&nbsp;library.">1. Introduction</a><a href="p02-elements" title="Basic concepts of elements modifications and creations.">2. Elements</a><a href="p03-events" title="Using not only events in UI declaratively.">3. Events and Addons</a><a href="p04-observables" title="Handling reactivity in UI via observables.">4. Observables and reactivity</a><a href="p05-scopes" title="Organizing UI into components">5. Scopes and components</a><a href="p06-customElement" title="Using custom elements in combinantion with DDE" class="current">6. Custom elements</a></nav><main><h2>Using custom elements in combinantion with DDE</h2><p></p><div class="code" data-js="todo"><!--<dde:mark type="component" name="code" host="parentElement" ssr/>--><code class="language-js">// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js
import { import {
customElementRender, customElementRender,
customElementWithDDE, customElementWithDDE,

View File

@ -54,7 +54,7 @@ function registerClientPart(page_id){
if(is_registered[page_id]) return; if(is_registered[page_id]) return;
document.head.append( 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", defer: true }),
); );
registerClientFile( registerClientFile(
new URL("./code.js.js", import.meta.url), new URL("./code.js.js", import.meta.url),

View File

@ -108,17 +108,6 @@ export function page({ pkg, info }){
" clean up procedures when the component is removed from the app." " 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(h3, "Final notes"),
el("p", "The library also provides a method to dispatch the events."), 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( return el("li").append(
O.el(is_editable, is=> is O.el(is_editable, is=> is
? el("input", { value: textContent(), type: "text" }, onedited) ? el("input", { value: textContent(), type: "text" }, onedited)
: el("span", { textContent, onclick: is_editable.bind(null, true) }) : el("span", { textContent, onclick: is_editable.bind(null, true) }),
), ),
el("button", { type: "button", value, textContent: "-" }, onclick) el("button", { type: "button", value, textContent: "-" }, onclick)
); );

View File

@ -12,7 +12,9 @@ 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) this.attachShadow({ mode: "open" }).append(
customElementRender(this, this.render, this.attributes)
);
} }
attributes(element){ attributes(element){
@ -61,7 +63,7 @@ export class CustomSlottingHTMLElement extends HTMLElement{
)); ));
} }
connectedCallback(){ connectedCallback(){
customElementRender(this, this, this.render); this.append(customElementRender(this, this.render));
} }
} }
customElementWithDDE(CustomSlottingHTMLElement); customElementWithDDE(CustomSlottingHTMLElement);

13
index.d.ts vendored
View File

@ -1,11 +1,16 @@
import { Observable } from "./observables"; import { Observable } from "./observables";
type CustomElementTagNameMap= { '#text': Text, '#comment': Comment } type CustomElementTagNameMap= { '#text': Text, '#comment': Comment }
declare global {
interface ddePublicElementTagNameMap{
}
}
type SupportedElement= type SupportedElement=
HTMLElementTagNameMap[keyof HTMLElementTagNameMap] HTMLElementTagNameMap[keyof HTMLElementTagNameMap]
| SVGElementTagNameMap[keyof SVGElementTagNameMap] | SVGElementTagNameMap[keyof SVGElementTagNameMap]
| MathMLElementTagNameMap[keyof MathMLElementTagNameMap] | MathMLElementTagNameMap[keyof MathMLElementTagNameMap]
| CustomElementTagNameMap[keyof CustomElementTagNameMap] | CustomElementTagNameMap[keyof CustomElementTagNameMap]
| ddePublicElementTagNameMap[keyof ddePublicElementTagNameMap];
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>= (element: El)=> El | void;
@ -43,7 +48,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 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 & ddePublicElementTagNameMap
type textContent= string | ( (set?: string)=> string ); // TODO: for some reason `Observable<string, any>` leads to `attrs?: any` type textContent= string | ( (set?: string)=> string ); // TODO: for some reason `Observable<string, any>` leads to `attrs?: any`
export function el< export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap & string, TAG extends keyof ExtendedHTMLElementTagNameMap & string,
@ -175,7 +180,6 @@ export function customElementRender<
P extends any = Record<string, any> P extends any = Record<string, any>
>( >(
custom_element: EL, custom_element: EL,
target: ShadowRoot | EL,
render: (props: P)=> SupportedElement, render: (props: P)=> SupportedElement,
props?: P | ((...args: any[])=> P) props?: P | ((...args: any[])=> P)
): EL ): EL
@ -183,10 +187,9 @@ export function customElementWithDDE<EL extends HTMLElement>(custom_element: EL)
export function lifecycleToEvents<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> export function observedAttributes(custom_element: HTMLElement): Record<string, string>
/* TypeScript MEH */ /* TypeScript MEH // TODO for SVG */
type ddeAppend<el>= (...nodes: (Node | string)[])=> el;
declare global{ declare global{
type ddeAppend<el>= (...nodes: (Node | string)[])=> el;
interface ddeDocumentFragment extends DocumentFragment{ append: ddeAppend<ddeDocumentFragment>; } interface ddeDocumentFragment extends DocumentFragment{ append: ddeAppend<ddeDocumentFragment>; }
interface ddeHTMLElement extends HTMLElement{ append: ddeAppend<ddeHTMLElement>; } interface ddeHTMLElement extends HTMLElement{ append: ddeAppend<ddeHTMLElement>; }
interface ddeSVGElement extends SVGElement{ append: ddeAppend<ddeSVGElement>; } interface ddeSVGElement extends SVGElement{ append: ddeAppend<ddeSVGElement>; }

3
observables.d.ts vendored
View File

@ -3,7 +3,6 @@ type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typ
//type SymbolObservable= Symbol; //type SymbolObservable= Symbol;
type SymbolOnclear= symbol; type SymbolOnclear= symbol;
type Actions<V>= Record<string | SymbolOnclear, Action<V>>; type Actions<V>= Record<string | SymbolOnclear, Action<V>>;
type OnListenerOptions= Pick<AddEventListenerOptions, "signal"> & { first_time?: boolean };
interface observable{ interface observable{
_: Symbol _: Symbol
/** /**
@ -41,7 +40,7 @@ interface observable{
...params: A[N] extends (...args: infer P)=> any ? P : never ...params: A[N] extends (...args: infer P)=> any ? P : never
): void; ): void;
clear(...observables: Observable<any, any>[]): void; clear(...observables: Observable<any, any>[]): void;
on<T>(observable: Observable<T, any>, onchange: (a: T)=> void, options?: OnListenerOptions): void; on<T>(observable: Observable<T, any>, onchange: (a: T)=> void, options?: AddEventListenerOptions): void;
symbols: { symbols: {
//observable: SymbolObservable; //observable: SymbolObservable;
onclear: SymbolOnclear; onclear: SymbolOnclear;

View File

@ -1,6 +1,6 @@
{ {
"name": "deka-dom-el", "name": "deka-dom-el",
"version": "0.7.8", "version": "0.7.7",
"description": "A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks.", "description": "A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks.",
"author": "Jan Andrle <andrle.jan@centrum.cz>", "author": "Jan Andrle <andrle.jan@centrum.cz>",
"license": "MIT", "license": "MIT",
@ -59,24 +59,18 @@
"size-limit": [ "size-limit": [
{ {
"path": "./index.js", "path": "./index.js",
"limit": "10.5 kB", "limit": "10 kB",
"gzip": false, "gzip": false,
"brotli": false "brotli": false
}, },
{ {
"path": "./observables.js", "path": "./observables.js",
"limit": "12 kB", "limit": "11.5 kB",
"gzip": false, "gzip": false,
"brotli": false "brotli": false
}, },
{
"path": "./index-with-observables.js",
"limit": "15 kB",
"gzip": false,
"brotli": false
},
{ {
"path": "./index-with-observables.js", "path": "./index-with-observables.js",
"limit": "5 kB" "limit": "5 kB"

View File

@ -1,40 +1,30 @@
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"; export function customElementRender(custom_element, render, props= observedAttributes){
export function customElementRender(custom_element, target, render, props= observedAttributes){
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,
custom_element
}); });
if(typeof props==="function") props= props.call(custom_element, custom_element); if(typeof props==="function") props= props.call(custom_element, custom_element);
const is_lte= custom_element[keyLTE];
if(!is_lte) lifecycleToEvents(custom_element);
const out= render.call(custom_element, props); const out= render.call(custom_element, props);
if(!is_lte) custom_element.dispatchEvent(new Event(evc));
if(target.nodeType===11 && typeof target.mode==="string") // is ShadowRoot
custom_element.addEventListener(evd, c_ch_o.observe(target), { once: true });
scope.pop(); scope.pop();
return target.append(out); return out;
} }
export function lifecycleToEvents(class_declaration){ export function lifecycleToEvents(class_declaration){
wrapMethod(class_declaration.prototype, "connectedCallback", function(target, thisArg, detail){ for (const name of [ "connected", "disconnected" ])
wrapMethod(class_declaration.prototype, name+"Callback", function(target, thisArg, detail){
target.apply(thisArg, detail); target.apply(thisArg, detail);
thisArg.dispatchEvent(new Event(evc)); thisArg.dispatchEvent(new Event("dde:"+name));
}); });
wrapMethod(class_declaration.prototype, "disconnectedCallback", function(target, thisArg, detail){ const name= "attributeChanged";
target.apply(thisArg, detail); wrapMethod(class_declaration.prototype, name+"Callback", function(target, thisArg, detail){
(queueMicrotask || setTimeout)(
()=> !thisArg.isConnected && thisArg.dispatchEvent(new Event(evd))
);
});
wrapMethod(class_declaration.prototype, "attributeChangedCallback", function(target, thisArg, detail){
const [ attribute, , value ]= detail; const [ attribute, , value ]= detail;
thisArg.dispatchEvent(new CustomEvent(eva, { thisArg.dispatchEvent(new CustomEvent("dde:"+name, {
detail: [ attribute, value ] detail: [ attribute, value ]
})); }));
target.apply(thisArg, detail); target.apply(thisArg, detail);
}); });
class_declaration.prototype[keyLTE]= true; class_declaration.prototype.__dde_lifecycleToEvents= true;
return class_declaration; return class_declaration;
} }
export { lifecycleToEvents as customElementWithDDE }; export { lifecycleToEvents as customElementWithDDE };

View File

@ -26,7 +26,3 @@ function setDeleteAttr(obj, prop, val){
if(Reflect.get(obj, prop)==="undefined") if(Reflect.get(obj, prop)==="undefined")
return Reflect.set(obj, prop, ""); return Reflect.set(obj, prop, "");
} }
export const keyLTE= "__dde_lifecycleToEvents"; //boolean
export const evc= "dde:connected";
export const evd= "dde:disconnected";
export const eva= "dde:attributeChanged";

View File

@ -5,6 +5,7 @@ import { enviroment as env } from './dom-common.js';
const scopes= [ { const scopes= [ {
get scope(){ return env.D.body; }, get scope(){ return env.D.body; },
host: c=> c ? c(env.D.body) : env.D.body, host: c=> c ? c(env.D.body) : env.D.body,
custom_element: false,
prevent: true, prevent: true,
} ]; } ];
export const scope= { export const scope= {

View File

@ -1,138 +0,0 @@
import { enviroment as env, evc, evd } from './dom-common.js';
export const c_ch_o= env.M ? connectionsChangesObserverConstructor() : new Proxy({}, {
get(){ return ()=> {}; }
});
function connectionsChangesObserverConstructor(){
const store= new Map();
let is_observing= false;
const observerListener= stop=> function(mutations){
for(const mutation of mutations){
if(mutation.type!=="childList") continue;
if(observerAdded(mutation.addedNodes, true)){
stop();
continue;
}
if(observerRemoved(mutation.removedNodes, true))
stop();
}
};
const observer= new env.M(observerListener(stop));
return {
observe(element){
const o= new env.M(observerListener(()=> {}));
o.observe(element, { childList: true, subtree: true });
return ()=> o.disconnect();
},
onConnected(element, listener){
start();
const listeners= getElementStore(element);
if(listeners.connected.has(listener)) return;
listeners.connected.add(listener);
listeners.length_c+= 1;
},
offConnected(element, listener){
if(!store.has(element)) return;
const ls= store.get(element);
if(!ls.connected.has(listener)) return;
ls.connected.delete(listener);
ls.length_c-= 1;
cleanWhenOff(element, ls);
},
onDisconnected(element, listener){
start();
const listeners= getElementStore(element);
if(listeners.disconnected.has(listener)) return;
listeners.disconnected.add(listener);
listeners.length_d+= 1;
},
offDisconnected(element, listener){
if(!store.has(element)) return;
const ls= store.get(element);
if(!ls.disconnected.has(listener)) return;
ls.disconnected.delete(listener);
ls.length_d-= 1;
cleanWhenOff(element, ls);
}
};
function cleanWhenOff(element, ls){
if(ls.length_c || ls.length_d)
return;
store.delete(element);
stop();
}
function getElementStore(element){
if(store.has(element)) return store.get(element);
const out= {
connected: new WeakSet(),
length_c: 0,
disconnected: new WeakSet(),
length_d: 0
};
store.set(element, out);
return out;
}
function start(){
if(is_observing) return;
is_observing= true;
observer.observe(env.D.body, { childList: true, subtree: true });
}
function stop(){
if(!is_observing || store.size) return;
is_observing= false;
observer.disconnect();
}
//TODO remount support?
function requestIdle(){ return new Promise(function(resolve){
(requestIdleCallback || requestAnimationFrame)(resolve);
}); }
async function collectChildren(element){
if(store.size > 30)//TODO limit?
await requestIdle();
const out= [];
if(!(element instanceof Node)) return out;
for(const el of store.keys()){
if(el===element || !(el instanceof Node)) continue;
if(element.contains(el))
out.push(el);
}
return out;
}
function observerAdded(addedNodes, is_root){
let out= false;
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;
if(!ls.length_d) store.delete(element);
out= true;
}
return out;
}
function observerRemoved(removedNodes, is_root){
let out= false;
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;
(queueMicrotask || setTimeout)(dispatchRemove(element));
out= true;
}
return out;
}
function dispatchRemove(element){
return ()=> {
if(element.isConnected) return;
element.dispatchEvent(new Event(evd));
store.delete(element);
};
}
}

View File

@ -1,5 +1,5 @@
export { registerReactivity } from './observables-common.js'; export { registerReactivity } from './observables-common.js';
import { enviroment as env, keyLTE, evc, evd, eva } from './dom-common.js'; import { enviroment as env } from './dom-common.js';
export function dispatchEvent(name, options, host){ export function dispatchEvent(name, options, host){
if(!options) options= {}; if(!options) options= {};
@ -19,17 +19,25 @@ export function on(event, listener, options){
}; };
} }
import { c_ch_o } from "./events-observer.js"; const c_ch_o= env.M ? connectionsChangesObserverConstructor() : new Proxy({}, {
get(){ return ()=> {}; }
});
const els_attribute_store= new WeakSet();
import { scope } from "./dom.js";
import { onAbort } from './helpers.js'; import { onAbort } from './helpers.js';
const lifeOptions= obj=> Object.assign({}, typeof obj==="object" ? obj : null, { once: true });
//TODO: cleanUp when event before abort? //TODO: cleanUp when event before abort?
//TODO: docs (e.g.) https://nolanlawson.com/2024/01/13/web-component-gotcha-constructor-vs-connectedcallback/
on.connected= function(listener, options){ on.connected= function(listener, options){
options= lifeOptions(options); const { custom_element }= scope.current;
const name= "connected";
if(typeof options !== "object")
options= {};
options.once= true;
return function registerElement(element){ return function registerElement(element){
element.addEventListener(evc, listener, options); if(custom_element) element= custom_element;
if(element[keyLTE]) return element; const event= "dde:"+name;
if(element.isConnected) return ( element.dispatchEvent(new Event(evc)), element ); element.addEventListener(event, listener, options);
if(element.__dde_lifecycleToEvents) return element;
if(element.isConnected) return ( element.dispatchEvent(new Event(event)), element );
const c= onAbort(options.signal, ()=> c_ch_o.offConnected(element, listener)); const c= onAbort(options.signal, ()=> c_ch_o.offConnected(element, listener));
if(c) c_ch_o.onConnected(element, listener); if(c) c_ch_o.onConnected(element, listener);
@ -37,10 +45,16 @@ on.connected= function(listener, options){
}; };
}; };
on.disconnected= function(listener, options){ on.disconnected= function(listener, options){
options= lifeOptions(options); const { custom_element }= scope.current;
const name= "disconnected";
if(typeof options !== "object")
options= {};
options.once= true;
return function registerElement(element){ return function registerElement(element){
element.addEventListener(evd, listener, options); if(custom_element) element= custom_element;
if(element[keyLTE]) return element; const event= "dde:"+name;
element.addEventListener(event, listener, options);
if(element.__dde_lifecycleToEvents) return element;
const c= onAbort(options.signal, ()=> c_ch_o.offDisconnected(element, listener)); const c= onAbort(options.signal, ()=> c_ch_o.offDisconnected(element, listener));
if(c) c_ch_o.onDisconnected(element, listener); if(c) c_ch_o.onDisconnected(element, listener);
@ -56,13 +70,14 @@ on.disconnectedAsAbort= function(host){
host(on.disconnected(()=> a.abort())); host(on.disconnected(()=> a.abort()));
return a; return a;
}; };
const els_attribute_store= new WeakSet();
on.attributeChanged= function(listener, options){ on.attributeChanged= function(listener, options){
const name= "attributeChanged";
if(typeof options !== "object") if(typeof options !== "object")
options= {}; options= {};
return function registerElement(element){ return function registerElement(element){
element.addEventListener(eva, listener, options); const event= "dde:"+name;
if(element[keyLTE] || els_attribute_store.has(element)) element.addEventListener(event, listener, options);
if(element.__dde_lifecycleToEvents || els_attribute_store.has(element))
return element; return element;
if(!env.M) return element; if(!env.M) return element;
@ -70,7 +85,7 @@ on.attributeChanged= function(listener, options){
const observer= new env.M(function(mutations){ const observer= new env.M(function(mutations){
for(const { attributeName, target } of mutations) for(const { attributeName, target } of mutations)
target.dispatchEvent( target.dispatchEvent(
new CustomEvent(eva, { detail: [ attributeName, target.getAttribute(attributeName) ] })); new CustomEvent(event, { detail: [ attributeName, target.getAttribute(attributeName) ] }));
}); });
const c= onAbort(options.signal, ()=> observer.disconnect()); const c= onAbort(options.signal, ()=> observer.disconnect());
if(c) observer.observe(element, { attributes: true }); if(c) observer.observe(element, { attributes: true });
@ -78,3 +93,127 @@ on.attributeChanged= function(listener, options){
return element; return element;
}; };
}; };
function connectionsChangesObserverConstructor(){
const store= new Map();
let is_observing= false;
const observer= new env.M(function(mutations){
for(const mutation of mutations){
if(mutation.type!=="childList") continue;
if(observerAdded(mutation.addedNodes, true)){
stop();
continue;
}
if(observerRemoved(mutation.removedNodes, true))
stop();
}
});
return {
onConnected(element, listener){
start();
const listeners= getElementStore(element);
if(listeners.connected.has(listener)) return;
listeners.connected.add(listener);
listeners.length_c+= 1;
},
offConnected(element, listener){
if(!store.has(element)) return;
const ls= store.get(element);
if(!ls.connected.has(listener)) return;
ls.connected.delete(listener);
ls.length_c-= 1;
cleanWhenOff(element, ls);
},
onDisconnected(element, listener){
start();
const listeners= getElementStore(element);
if(listeners.disconnected.has(listener)) return;
listeners.disconnected.add(listener);
listeners.length_d+= 1;
},
offDisconnected(element, listener){
if(!store.has(element)) return;
const ls= store.get(element);
if(!ls.disconnected.has(listener)) return;
ls.disconnected.delete(listener);
ls.length_d-= 1;
cleanWhenOff(element, ls);
}
};
function cleanWhenOff(element, ls){
if(ls.length_c || ls.length_d)
return;
store.delete(element);
stop();
}
function getElementStore(element){
if(store.has(element)) return store.get(element);
const out= {
connected: new WeakSet(),
length_c: 0,
disconnected: new WeakSet(),
length_d: 0
};
store.set(element, out);
return out;
}
function start(){
if(is_observing) return;
is_observing= true;
observer.observe(env.D.body, { childList: true, subtree: true });
}
function stop(){
if(!is_observing || store.size) return;
is_observing= false;
observer.disconnect();
}
//TODO remount support?
function requestIdle(){ return new Promise(function(resolve){
(requestIdleCallback || requestAnimationFrame)(resolve);
}); }
async function collectChildren(element, filter){
if(store.size > 30)//TODO limit?
await requestIdle();
const out= [];
if(!(element instanceof Node)) return out;
for(const el of store.keys()){
if(el===element || !(el instanceof Node) || filter(el)) continue;
if(element.contains(el))
out.push(el);
}
return out;
}
function observerAdded(addedNodes, is_root){
let out= false;
for(const element of addedNodes){
if(is_root) collectChildren(element, el=> !el.isConnectedd).then(observerAdded);
if(!store.has(element)) continue;
const ls= store.get(element);
if(!ls.length_c) continue;
element.dispatchEvent(new Event("dde:connected"));
ls.connected= new WeakSet();
ls.length_c= 0;
if(!ls.length_d) store.delete(element);
out= true;
}
return out;
}
function observerRemoved(removedNodes, is_root){
let out= false;
for(const element of removedNodes){
if(is_root) collectChildren(element, el=> el.isConnectedd).then(observerRemoved);
if(!store.has(element)) continue;
const ls= store.get(element);
if(!ls.length_d) continue;
element.dispatchEvent(new Event("dde:disconnected"));
store.delete(element);
out= true;
}
return out;
}
}

View File

@ -96,7 +96,7 @@ observable.el= function(o, map){
out.append(mark_start, mark_end); out.append(mark_start, mark_end);
const { current }= scope; const { current }= scope;
const reRenderReactiveElement= v=> { const reRenderReactiveElement= v=> {
if(!mark_start.parentNode || !mark_end.parentNode) // isConnected or wasnt yet rendered if(!mark_start.parentNode || !mark_end.parentNode)
return removeObservableListener(o, reRenderReactiveElement); return removeObservableListener(o, reRenderReactiveElement);
scope.push(current); scope.push(current);
let els= map(v); let els= map(v);
@ -107,21 +107,12 @@ observable.el= function(o, map){
while(( el_r= mark_start.nextSibling ) !== mark_end) while(( el_r= mark_start.nextSibling ) !== mark_end)
el_r.remove(); el_r.remove();
mark_start.after(...els); mark_start.after(...els);
if(mark_start.isConnected)
requestCleanUpReactives(current.host());
}; };
addObservableListener(o, reRenderReactiveElement); addObservableListener(o, reRenderReactiveElement);
removeObservablesFromElements(o, reRenderReactiveElement, mark_start, map); removeObservablesFromElements(o, reRenderReactiveElement, mark_start, map);
reRenderReactiveElement(o()); reRenderReactiveElement(o());
return out; return out;
}; };
function requestCleanUpReactives(host){
if(!host || !host[key_reactive]) return;
(requestIdleCallback || setTimeout)(function(){
host[key_reactive]= host[key_reactive]
.filter(([ o, el ])=> el.isConnected ? true : (removeObservableListener(...o), false));
});
}
import { on } from "./events.js"; import { on } from "./events.js";
import { observedAttributes } from "./helpers.js"; import { observedAttributes } from "./helpers.js";
const observedAttributeActions= { const observedAttributeActions= {
@ -161,11 +152,7 @@ export const observables_config= {
isObservable, isObservable,
processReactiveAttribute(element, key, attrs, set){ processReactiveAttribute(element, key, attrs, set){
if(!isObservable(attrs)) return attrs; if(!isObservable(attrs)) return attrs;
const l= attr=> { const l= attr=> set(key, attr);
if(!element.isConnected)
return removeObservableListener(attrs, l);
set(key, attr);
};
addObservableListener(attrs, l); addObservableListener(attrs, l);
removeObservablesFromElements(attrs, l, element, key); removeObservablesFromElements(attrs, l, element, key);
return attrs(); return attrs();