1
0
mirror of https://github.com/jaandrle/deka-dom-el synced 2024-11-24 09:29:37 +01:00

Compare commits

..

No commits in common. "98d9d6a5b18024f63901331df98d70ed33a0b25e" and "64a03c8ba53ccb7eb645bdc6d89c9760e7ee260f" have entirely different histories.

22 changed files with 62 additions and 368 deletions

File diff suppressed because one or more lines are too long

2
dist/dde.js vendored

File diff suppressed because one or more lines are too long

View File

@ -37,13 +37,14 @@ type ElementAttributes<T extends keyof ElementTagNameMap | ElementTagNameMap[key
Omit<ElementTagNameMap[T],"classList"|"className"> & AttrsModified : Omit<ElementTagNameMap[T],"classList"|"className"> & AttrsModified :
Omit<T,"classList"|"className"> & AttrsModified; Omit<T,"classList"|"className"> & AttrsModified;
export function assign<El extends Element>(element: El, ...attrs_array: Partial<ElementAttributes<El>>[]): El export function assign<El extends Element>(element: El, ...attrs_array: Partial<ElementAttributes<El>>[]): El
type TagNameFragment= "<>";
export function el<TAG extends keyof ElementTagNameMap>( export function el<TAG extends keyof ElementTagNameMap>(
tag_name: TAG, tag_name: TAG,
attrs?: Partial<ElementAttributes<ElementTagNameMap[TAG]>>, attrs?: Partial<ElementAttributes<ElementTagNameMap[TAG]>>,
...extenders: ddeElementExtender<ElementTagNameMap[TAG]>[] ...extenders: ddeElementExtender<ElementTagNameMap[TAG]>[]
): ElementTagNameMap[TAG] ): ElementTagNameMap[TAG]
export function el<T>( export function el<T>(
tag_name?: "<>", tag_name: TagNameFragment,
): DocumentFragment ): DocumentFragment
export function el< export function el<
A extends ddeComponentAttributes, A extends ddeComponentAttributes,

File diff suppressed because one or more lines are too long

3
dist/esm.d.ts vendored
View File

@ -37,13 +37,14 @@ type ElementAttributes<T extends keyof ElementTagNameMap | ElementTagNameMap[key
Omit<ElementTagNameMap[T],"classList"|"className"> & AttrsModified : Omit<ElementTagNameMap[T],"classList"|"className"> & AttrsModified :
Omit<T,"classList"|"className"> & AttrsModified; Omit<T,"classList"|"className"> & AttrsModified;
export function assign<El extends Element>(element: El, ...attrs_array: Partial<ElementAttributes<El>>[]): El export function assign<El extends Element>(element: El, ...attrs_array: Partial<ElementAttributes<El>>[]): El
type TagNameFragment= "<>";
export function el<TAG extends keyof ElementTagNameMap>( export function el<TAG extends keyof ElementTagNameMap>(
tag_name: TAG, tag_name: TAG,
attrs?: Partial<ElementAttributes<ElementTagNameMap[TAG]>>, attrs?: Partial<ElementAttributes<ElementTagNameMap[TAG]>>,
...extenders: ddeElementExtender<ElementTagNameMap[TAG]>[] ...extenders: ddeElementExtender<ElementTagNameMap[TAG]>[]
): ElementTagNameMap[TAG] ): ElementTagNameMap[TAG]
export function el<T>( export function el<T>(
tag_name?: "<>", tag_name: TagNameFragment,
): DocumentFragment ): DocumentFragment
export function el< export function el<
A extends ddeComponentAttributes, A extends ddeComponentAttributes,

2
dist/esm.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,32 +1,21 @@
@import url(https://cdn.simplecss.org/simple.min.css);
:root { :root {
color-scheme: dark light;
--body-max-width: 45rem; --body-max-width: 45rem;
--marked: #fb3779;
--code: #0d47a1;
--accent: #d81b60;
} }
@media (prefers-color-scheme:dark) { *, ::before, ::after { box-sizing: border-box; }
::backdrop, :root { body > * {
--accent: #f06292; margin-inline: max(.5rem, calc(50% - var(--body-max-width) / 2));
--code: #62c1f0; font-family: Tahoma, Verdana, Arial, sans-serif;
}
}
body {
grid-template-columns: 1fr min(var(--body-max-width), 90%) 1fr;
} }
h1{ h1{
text-align: center; text-align: center;
text-wrap: balanc; text-wrap: balanc;
grid-column: 1 / 4;
} }
.note{ .note{
font-size: .9rem; font-size: .9rem;
font-style: italic; font-style: italic;
} }
.example{ .example{
grid-column: 1/4; --body-max-width: 80rem;
width: 100%; height: 25rem;
max-width: calc(9/5 * var(--body-max-width));
height: calc(3/5 * var(--body-max-width));
margin-inline: auto;
} }

View File

@ -1,7 +1,7 @@
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="index.css"><meta name="description" content="Introducing basic concepts."><!--<dde:mark type="component" name="metaTwitter" host="this"/>--><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"/>--><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"><title>`deka-dom-el` — Introduction/Guide</title><script src="https://flems.io/flems.html" type="text/javascript" charset="utf-8"></script></head><body><!--<dde:mark type="component" name="body" host="this"/>--><h1>`deka-dom-el` — Introduction/Guide</h1><p>The library tries to provide pure JavaScript tool(s) to create reactive interfaces. The main goals are:</p><ul><li>provide a&nbsp;small wrappers/utilities around the native JavaScript DOM</li><li>keep library size around 10kB at maximum (if possible)</li><li>zero dependencies (if possible)</li><li>prefer a&nbsp;declarative/functional approach</li><li>unopinionated (allow alternative methods)</li></ul><p class="note">It is, in fact, an reimplementation of <a href="https://github.com/jaandrle/dollar_dom_component" title="GitHub repository of library. Motto: Functional DOM components without JSX and virtual DOM.">jaandrle/dollar_dom_component</a>.</p><!--<dde:mark type="component" name="example" host="this"/>--><div id="code-hrmiy" class="example"><pre><code class="language-javascript">import { el, S } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js"; <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="index.css"><meta name="description" content="Introducing basic concepts."><!--<dde:mark type="component" name="metaTwitter"/>--><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"/>--><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"><title>`deka-dom-el` — Introduction/Guide</title><script src="https://flems.io/flems.html" type="text/javascript" charset="utf-8"></script></head><body><!--<dde:mark type="component" name="body"/>--><h1>`deka-dom-el` — Introduction/Guide</h1><p>The library tries to provide pure JavaScript tool(s) to create reactive interfaces.The main goals are:</p><ul><li>provide a&nbsp;small wrapper around the native JavaScript DOM</li><li>keep library size under 10kB</li><li>zero dependencies (if possible)</li><li>prefer a&nbsp;declarative/functional approach</li><li>unopinionated (allow alternative methods)</li></ul><p class="note">It is, in fact, an reimplementation of <a href="https://github.com/jaandrle/dollar_dom_component" title="GitHub repository of library. Motto: Functional DOM components without JSX and virtual DOM.">jaandrle/dollar_dom_component</a>.</p><!--<dde:mark type="component" name="example"/>--><div id="code-vup3z" class="example"><pre><code class="language-javascript">import { el, S } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
const clicks= S(0); const clicks= S(0);
document.body.append( document.body.append(
el().append( el("&lt;&gt;").append(
el("p", S(()=&gt; el("p", S(()=&gt;
"Hello World "+"🎉".repeat(clicks()) "Hello World "+"🎉".repeat(clicks())
)), )),
@ -12,106 +12,4 @@ document.body.append(
}) })
) )
); );
</code></pre></div><script>Flems(document.getElementById("code-hrmiy"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { el, S } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js\\\";\\nconst clicks= S(0);\\ndocument.body.append(\\n\\tel().append(\\n\\t\\tel(\\\"p\\\", S(()=>\\n\\t\\t\\t\\\"Hello World \\\"+\\\"🎉\\\".repeat(clicks())\\n\\t\\t)),\\n\\t\\tel(\\\"button\\\", {\\n\\t\\t\\ttype: \\\"button\\\",\\n\\t\\t\\tonclick: ()=> clicks(clicks()+1),\\n\\t\\t\\ttextContent: \\\"Fire\\\"\\n\\t\\t})\\n\\t)\\n);\\n\"}],\"toolbar\":false}"));</script><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><h3>Creating element(s) (with custom attributes)</h3><p>You can create a native DOM element by using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement" title="MDN documentation for document.createElement()"><code>document.createElement()</code></a>. It is also possible to provide a&nbsp;some attribute(s) declaratively using <code>Object.assign()</code>. More precisely, this way you can sets some <a href="https://developer.mozilla.org/en-US/docs/Glossary/IDL" title="MDN page about Interface Description Language"><abbr title="Interface Description Language">IDL</abbr></a>.</p><!--<dde:mark type="component" name="example" host="this"/>--><div id="code-x0k3n" class="example"><pre><code class="language-javascript">document.body.append( </code></pre></div><script>Flems(document.getElementById("code-vup3z"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { el, S } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js\\\";\\nconst clicks= S(0);\\ndocument.body.append(\\n\\tel(\\\"<>\\\").append(\\n\\t\\tel(\\\"p\\\", S(()=>\\n\\t\\t\\t\\\"Hello World \\\"+\\\"🎉\\\".repeat(clicks())\\n\\t\\t)),\\n\\t\\tel(\\\"button\\\", {\\n\\t\\t\\ttype: \\\"button\\\",\\n\\t\\t\\tonclick: ()=> clicks(clicks()+1),\\n\\t\\t\\ttextContent: \\\"Fire\\\"\\n\\t\\t})\\n\\t)\\n);\\n\"}],\"toolbar\":false}"));</script><script src="https://cdn.jsdelivr.net/npm/shiki"></script><script src="index.js" type="module"></script></body></html>
document.createElement("div")
);
console.log(
"Emty div is generated inside &lt;body&gt;:",
document.body.innerHTML.includes("&lt;div&gt;&lt;/div&gt;")
);
document.body.append(
Object.assign(
document.createElement("p"),
{ textContent: "Elements text content.", style: "color: coral;" }
)
);
</code></pre></div><script>Flems(document.getElementById("code-x0k3n"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"document.body.append(\\n\\tdocument.createElement(\\\"div\\\")\\n);\\nconsole.log(\\n\\t\\\"Emty div is generated inside <body>:\\\",\\n\\tdocument.body.innerHTML.includes(\\\"<div></div>\\\")\\n);\\n\\ndocument.body.append(\\n\\tObject.assign(\\n\\t\\tdocument.createElement(\\\"p\\\"),\\n\\t\\t{ textContent: \\\"Elements text content.\\\", style: \\\"color: coral;\\\" }\\n\\t)\\n);\\n\"}],\"toolbar\":false}"));</script><p>To make this easier, you can use the <code>el</code> function. Internally in basic examples, it is wrapper around <code>assign(document.createElement(…), { … })</code>.</p><!--<dde:mark type="component" name="example" host="this"/>--><div id="code-tz88s" class="example"><pre><code class="language-javascript">import { el, assign } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
const color= "lightcoral";
document.body.append(
el("p", { textContent: "Hello (first time)", style: { color } })
);
document.body.append(
assign(
document.createElement("p"),
{ textContent: "Hello (second time)", style: { color } }
)
);
</code></pre></div><script>Flems(document.getElementById("code-tz88s"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { el, assign } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js\\\";\\nconst color= \\\"lightcoral\\\";\\ndocument.body.append(\\n\\tel(\\\"p\\\", { textContent: \\\"Hello (first time)\\\", style: { color } })\\n);\\ndocument.body.append(\\n\\tassign(\\n\\t\\tdocument.createElement(\\\"p\\\"),\\n\\t\\t{ textContent: \\\"Hello (second time)\\\", style: { color } }\\n\\t)\\n);\\n\"}],\"toolbar\":false}"));</script><p>The <code>assign</code> function provides improved behaviour of <code>Object.assign()</code>. You can declaratively sets any IDL and attribute of the given element. Function prefers IDL and fallback to the <code>element.setAttribute</code> if there is no writable property in the element prototype.</p><p>You can study all JavaScript elements interfaces to the corresponding HTML elements. All HTML elements inherits from <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement">HTMLElement</a>. To see all available IDLs for example for paragraphs, see <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLParagraphElement">HTMLParagraphElement</a>. Moreover, the <code>assign</code> provides a&nbsp;way to sets declaratively some convenient properties:</p><ul><li>It is possible to sets <code>data-*</code>/<code>aria-*</code> attributes using object notation.</li><li>In opposite, it is also possible to sets <code>data-*</code>/<code>aria-*</code> attribute using camelCase notation.</li><li>You can use string or object as a&nbsp;value for <code>style</code> property.</li><li><code>className</code> (IDL preffered)/<code>class</code> are ways to add CSS class to the element. You can use string (similarly to <code>class="…"</code> syntax in HTML) or array of strings. This is handy to concat conditional classes.</li><li>Use <code>classList</code> to toggle specific classes. This will be handy later when the reactivity via signals is beeing introduced.</li><li>The <code>assign</code> also accepts the <code>undefined</code> as a&nbsp;value for any property to remove it from the element declaratively. Also for some IDL the corresponding attribute is removed as it can be confusing. <em>For example, natievly the elements <code>id</code> is removed by setting the IDL to an empty string.</em></li></ul><p>For processing, the <code>assign</code> internally uses <code>assignAttribute</code> and <code>classListDeclarative</code>.</p><!--<dde:mark type="component" name="example" host="this"/>--><div id="code-8doer" class="example"><pre><code class="language-javascript">import { assignAttribute, classListDeclarative } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
const paragraph= document.createElement("p");
assignAttribute(paragraph, "textContent", "Hello, world!");
assignAttribute(paragraph, "style", { color: "navy" });
assignAttribute(paragraph, "dataTest1", "v1");
assignAttribute(paragraph, "dataset", { test2: "v2" });
assignAttribute(paragraph, "ariaLabel", "v1");
assignAttribute(paragraph, "ariaset", { role: "none" });
classListDeclarative(paragraph, {
classAdd: true,
classRemove: false,
classAdd1: 1,
classRemove1: 0,
classToggle: -1
});
console.log(paragraph.outerHTML);
document.body.append(
paragraph
);
</code></pre></div><script>Flems(document.getElementById("code-8doer"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { assignAttribute, classListDeclarative } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js\\\";\\nconst paragraph= document.createElement(\\\"p\\\");\\n\\nassignAttribute(paragraph, \\\"textContent\\\", \\\"Hello, world!\\\");\\nassignAttribute(paragraph, \\\"style\\\", { color: \\\"navy\\\" });\\n\\nassignAttribute(paragraph, \\\"dataTest1\\\", \\\"v1\\\");\\nassignAttribute(paragraph, \\\"dataset\\\", { test2: \\\"v2\\\" });\\n\\nassignAttribute(paragraph, \\\"ariaLabel\\\", \\\"v1\\\");\\nassignAttribute(paragraph, \\\"ariaset\\\", { role: \\\"none\\\" });\\n\\n\\nclassListDeclarative(paragraph, {\\n\\tclassAdd: true,\\n\\tclassRemove: false,\\n\\tclassAdd1: 1,\\n\\tclassRemove1: 0,\\n\\tclassToggle: -1\\n});\\n\\nconsole.log(paragraph.outerHTML);\\ndocument.body.append(\\n\\tparagraph\\n);\\n\"}],\"toolbar\":false}"));</script><h3>Native JavaScript templating</h3><p>By default, the native JS has no good way to define HTML template using DOM API:</p><!--<dde:mark type="component" name="example" host="this"/>--><div id="code-9pfmb" class="example"><pre><code class="language-javascript">document.body.append(
document.createElement("div"),
document.createElement("span"),
document.createElement("main")
);
console.log(document.body.innerHTML.includes("&lt;div&gt;&lt;/div&gt;&lt;span&gt;&lt;/span&gt;&lt;main&gt;&lt;/main&gt;"));
const template= document.createElement("main").append(
document.createElement("div"),
document.createElement("span"),
);
console.log(typeof template==="undefined");
</code></pre></div><script>Flems(document.getElementById("code-9pfmb"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"document.body.append(\\n\\tdocument.createElement(\\\"div\\\"),\\n\\tdocument.createElement(\\\"span\\\"),\\n\\tdocument.createElement(\\\"main\\\")\\n);\\nconsole.log(document.body.innerHTML.includes(\\\"<div></div><span></span><main></main>\\\"));\\nconst template= document.createElement(\\\"main\\\").append(\\n\\tdocument.createElement(\\\"div\\\"),\\n\\tdocument.createElement(\\\"span\\\"),\\n);\\nconsole.log(typeof template===\\\"undefined\\\");\\n\"}],\"toolbar\":false}"));</script><p>This library therefore ooverwrites the <code>append</code> method to always return parent element.</p><!--<dde:mark type="component" name="example" host="this"/>--><div id="code-ya3r1" class="example"><pre><code class="language-javascript">import { el } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
document.head.append(
el("style").append(
"tr, td{ border: 1px solid red; padding: 1em; }",
"table{ border-collapse: collapse; }"
)
);
document.body.append(
el("p", "Example of a&nbsp;complex template. Using for example nesting lists:"),
el("ul").append(
el("li", "List item 1"),
el("li").append(
el("ul").append(
el("li", "Nested list item 1"),
)
)
),
el("table").append(
el("tr").append(
el("td", "Row 1 Col 1"),
el("td", "Row 1 Col 2")
)
)
);
</code></pre></div><script>Flems(document.getElementById("code-ya3r1"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { el } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js\\\";\\ndocument.head.append(\\n\\tel(\\\"style\\\").append(\\n\\t\\t\\\"tr, td{ border: 1px solid red; padding: 1em; }\\\",\\n\\t\\t\\\"table{ border-collapse: collapse; }\\\"\\n\\t)\\n);\\ndocument.body.append(\\n\\tel(\\\"p\\\", \\\"Example of a complex template. Using for example nesting lists:\\\"),\\n\\tel(\\\"ul\\\").append(\\n\\t\\tel(\\\"li\\\", \\\"List item 1\\\"),\\n\\t\\tel(\\\"li\\\").append(\\n\\t\\t\\tel(\\\"ul\\\").append(\\n\\t\\t\\t\\tel(\\\"li\\\", \\\"Nested list item 1\\\"),\\n\\t\\t\\t)\\n\\t\\t)\\n\\t),\\n\\tel(\\\"table\\\").append(\\n\\t\\tel(\\\"tr\\\").append(\\n\\t\\t\\tel(\\\"td\\\", \\\"Row 1 Col 1\\\"),\\n\\t\\t\\tel(\\\"td\\\", \\\"Row 1 Col 2\\\")\\n\\t\\t)\\n\\t)\\n);\\n\"}],\"toolbar\":false}"));</script><h2>Creating advanced (reactive) templates and components</h2><h3>Basic components</h3><p>You can use functions for encapsulation (repeating) logic. The <code>el</code> accepts function as first argument. In that case, the function should return dom elements and the second argument for <code>el</code> is argument for given element.</p><!--<dde:mark type="component" name="example" host="this"/>--><div id="code-5abxx" class="example"><pre><code class="language-javascript">import { el } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
document.head.append(
el("style").append(
".class1{ font-weight: bold; }",
".class2{ color: purple; }"
)
);
document.body.append(
el(component, { className: "class2", textContent: "Hello World!" }),
component({ className: "class2", textContent: "Hello World!" })
);
function component({ className, textContent }){
return el("div", { className: [ "class1", className ] }).append(
el("p", textContent)
);
}
</code></pre></div><script>Flems(document.getElementById("code-5abxx"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { el } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js\\\";\\ndocument.head.append(\\n\\tel(\\\"style\\\").append(\\n\\t\\t\\\".class1{ font-weight: bold; }\\\",\\n\\t\\t\\\".class2{ color: purple; }\\\"\\n\\t)\\n);\\ndocument.body.append(\\n\\tel(component, { className: \\\"class2\\\", textContent: \\\"Hello World!\\\" }),\\n\\tcomponent({ className: \\\"class2\\\", textContent: \\\"Hello World!\\\" })\\n);\\n\\nfunction component({ className, textContent }){\\n\\treturn el(\\\"div\\\", { className: [ \\\"class1\\\", className ] }).append(\\n\\t\\tel(\\\"p\\\", textContent)\\n\\t);\\n}\\n\"}],\"toolbar\":false}"));</script><p>It is nice to use similar naming convention as native DOM API.</p><script src="https://cdn.jsdelivr.net/npm/shiki"></script><script src="index.js" type="module"></script></body></html>

View File

@ -1,12 +1,9 @@
let is_registered= false; let is_register= false;
import { styles } from "../index.css.js"; import { styles } from "../index.css.js";
export const css= styles().scope(example).css` export const css= styles().scope(example).css`
:host{ :host{
grid-column: 1/4; --body-max-width: 80rem;
width: 100%; height: 25rem;
max-width: calc(9/5 * var(--body-max-width));
height: calc(3/5 * var(--body-max-width));
margin-inline: auto;
} }
` `
import { el } from "deka-dom-el"; import { el } from "deka-dom-el";
@ -18,10 +15,13 @@ export function example({ src, language= "javascript" }){
.toString() .toString()
.replaceAll(' from "../../../index-with-signals.js";', ' from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";'); .replaceAll(' from "../../../index-with-signals.js";', ' from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";');
const id= "code-"+Math.random().toString(36).slice(2, 7); const id= "code-"+Math.random().toString(36).slice(2, 7);
return el().append( return el("<>").append(
el("div", { id, className: example.name }).append( el("div", { id, className: example.name }).append(
el("pre").append( el("pre").append(
el("code", { className: "language-"+language, textContent: code }) el("code", {
className: "language-"+language,
textContent: code
})
) )
), ),
elCode({ id, content: code }) elCode({ id, content: code })
@ -35,9 +35,9 @@ function elCode({ id, content }){
return el("script", `Flems(document.getElementById("${id}"), JSON.parse(${JSON.stringify(options)}));`); return el("script", `Flems(document.getElementById("${id}"), JSON.parse(${JSON.stringify(options)}));`);
} }
function register(){ function register(){
if(is_registered) return; if(is_register) return;
document.head.append( document.head.append(
el("script", { src: "https://flems.io/flems.html", type: "text/javascript", charset: "utf-8" }) el("script", { src: "https://flems.io/flems.html", type: "text/javascript", charset: "utf-8" })
); );
is_registered= true; is_register= true;
} }

View File

@ -1,24 +0,0 @@
import { el } from "../../../index-with-signals.js";
document.head.append(
el("style").append(
"tr, td{ border: 1px solid red; padding: 1em; }",
"table{ border-collapse: collapse; }"
)
);
document.body.append(
el("p", "Example of a complex template. Using for example nesting lists:"),
el("ul").append(
el("li", "List item 1"),
el("li").append(
el("ul").append(
el("li", "Nested list item 1"),
)
)
),
el("table").append(
el("tr").append(
el("td", "Row 1 Col 1"),
el("td", "Row 1 Col 2")
)
)
);

View File

@ -1,25 +0,0 @@
import { assignAttribute, classListDeclarative } from "../../../index-with-signals.js";
const paragraph= document.createElement("p");
assignAttribute(paragraph, "textContent", "Hello, world!");
assignAttribute(paragraph, "style", { color: "navy" });
assignAttribute(paragraph, "dataTest1", "v1");
assignAttribute(paragraph, "dataset", { test2: "v2" });
assignAttribute(paragraph, "ariaLabel", "v1");
assignAttribute(paragraph, "ariaset", { role: "none" });
classListDeclarative(paragraph, {
classAdd: true,
classRemove: false,
classAdd1: 1,
classRemove1: 0,
classToggle: -1
});
console.log(paragraph.outerHTML);
document.body.append(
paragraph
);

View File

@ -1,17 +0,0 @@
import { el } from "../../../index-with-signals.js";
document.head.append(
el("style").append(
".class1{ font-weight: bold; }",
".class2{ color: purple; }"
)
);
document.body.append(
el(component, { className: "class2", textContent: "Hello World!" }),
component({ className: "class2", textContent: "Hello World!" })
);
function component({ className, textContent }){
return el("div", { className: [ "class1", className ] }).append(
el("p", textContent)
);
}

View File

@ -1,11 +0,0 @@
import { el, assign } from "../../../index-with-signals.js";
const color= "lightcoral";
document.body.append(
el("p", { textContent: "Hello (first time)", style: { color } })
);
document.body.append(
assign(
document.createElement("p"),
{ textContent: "Hello (second time)", style: { color } }
)
);

View File

@ -1,7 +1,7 @@
import { el, S } from "../../../index-with-signals.js"; import { el, S } from "../../../index-with-signals.js";
const clicks= S(0); const clicks= S(0);
document.body.append( document.body.append(
el().append( el("<>").append(
el("p", S(()=> el("p", S(()=>
"Hello World "+"🎉".repeat(clicks()) "Hello World "+"🎉".repeat(clicks())
)), )),

View File

@ -1,11 +0,0 @@
document.body.append(
document.createElement("div"),
document.createElement("span"),
document.createElement("main")
);
console.log(document.body.innerHTML.includes("<div></div><span></span><main></main>"));
const template= document.createElement("main").append(
document.createElement("div"),
document.createElement("span"),
);
console.log(typeof template==="undefined");

View File

@ -1,14 +0,0 @@
document.body.append(
document.createElement("div")
);
console.log(
"Emty div is generated inside <body>:",
document.body.innerHTML.includes("<div></div>")
);
document.body.append(
Object.assign(
document.createElement("p"),
{ textContent: "Elements text content.", style: "color: coral;" }
)
);

View File

@ -28,25 +28,17 @@ export function css(...a){
} }
export function styles(){ return Object.assign(Object.create(s), { content: "" }); } export function styles(){ return Object.assign(Object.create(s), { content: "" }); }
export const common= css` export const common= css`
@import url(https://cdn.simplecss.org/simple.min.css);
:root { :root {
color-scheme: dark light;
--body-max-width: 45rem; --body-max-width: 45rem;
--marked: #fb3779;
--code: #0d47a1;
--accent: #d81b60;
} }
@media (prefers-color-scheme:dark) { *, ::before, ::after { box-sizing: border-box; }
::backdrop, :root { body > * {
--accent: #f06292; margin-inline: max(.5rem, calc(50% - var(--body-max-width) / 2));
--code: #62c1f0; font-family: Tahoma, Verdana, Arial, sans-serif;
}
}
body {
grid-template-columns: 1fr min(var(--body-max-width), 90%) 1fr;
} }
h1{ h1{
text-align: center; text-align: center;
text-wrap: balanc; text-wrap: balanc;
grid-column: 1 / 4;
} }
`; `;

View File

@ -21,15 +21,15 @@ export const css= styles()
` `
.include(example_css); .include(example_css);
export function body(pkg){ export function body(pkg){
return el().append( return el("<>").append(
el("h1", pageName(pkg)), el("h1", pageName(pkg)),
el("p").append( el("p").append(
"The library tries to provide pure JavaScript tool(s) to create reactive interfaces.", "The library tries to provide pure JavaScript tool(s) to create reactive interfaces.",
"The main goals are:" "The main goals are:"
), ),
el("ul").append( el("ul").append(
el("li", "provide a small wrappers/utilities around the native JavaScript DOM"), el("li", "provide a small wrapper around the native JavaScript DOM"),
el("li", "keep library size around 10kB at maximum (if possible)"), el("li", "keep library size under 10kB"),
el("li", "zero dependencies (if possible)"), el("li", "zero dependencies (if possible)"),
el("li", "prefer a declarative/functional approach"), el("li", "prefer a declarative/functional approach"),
el("li", "unopinionated (allow alternative methods)"), el("li", "unopinionated (allow alternative methods)"),
@ -43,94 +43,7 @@ export function body(pkg){
}), }),
".", ".",
), ),
el(example, { src: "./components/examples/helloWorld.js" }), el(example, { src: "./components/examples/helloWorld.js" })
el("h2", "Native JavaScript DOM elements creations"),
el("p", "Lets go through all patterns we would like to use and what needs to be improved for better experience."),
el("h3", "Creating element(s) (with custom attributes)"),
el("p").append(
"You can create a native DOM element by using the ",
el("a", { href: "https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement", title: "MDN documentation for document.createElement()" }).append(
el("code", "document.createElement()")
), ". ",
"It is also possible to provide a some attribute(s) declaratively using ", el("code", "Object.assign()"), ". ",
"More precisely, this way you can sets some ",
el("a", {
href: "https://developer.mozilla.org/en-US/docs/Glossary/IDL",
title: "MDN page about Interface Description Language"
}).append(
el("abbr", { textContent: "IDL", title: "Interface Description Language" })
), "."
),
el(example, { src: "./components/examples/nativeCreateElement.js" }),
el("p").append(
"To make this easier, you can use the ", el("code", "el"), " function. ",
"Internally in basic examples, it is wrapper around ", el("code", "assign(document.createElement(…), { … })"), "."
),
el(example, { src: "./components/examples/dekaCreateElement.js" }),
el("p").append(
"The ", el("code", "assign"), " function provides improved behaviour of ", el("code", "Object.assign()"), ". ",
"You can declaratively sets any IDL and attribute of the given element. ",
"Function prefers IDL and fallback to the ", el("code", "element.setAttribute"), " if there is no writable property in the element prototype."
),
el("p").append(
"You can study all JavaScript elements interfaces to the corresponding HTML elements. ",
"All HTML elements inherits from ", el("a", { textContent: "HTMLElement", href: "https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement" }), ". ",
"To see all available IDLs for example for paragraphs, see ", el("a", { textContent: "HTMLParagraphElement", href: "https://developer.mozilla.org/en-US/docs/Web/API/HTMLParagraphElement" }), ". ",
"Moreover, the ", el("code", "assign"), " provides a way to sets declaratively some convenient properties:"
),
el("ul").append(
el("li").append(
"It is possible to sets ", el("code", "data-*"), "/", el("code", "aria-*"), " attributes using object notation."
),
el("li").append(
"In opposite, it is also possible to sets ", el("code", "data-*"), "/", el("code", "aria-*"), " attribute using camelCase notation."
),
el("li").append(
"You can use string or object as a value for ", el("code", "style"), " property."
),
el("li").append(
el("code", "className"), " (IDL preffered)/", el("code", "class"), " are ways to add CSS class to the element. ",
"You can use string (similarly to ", el("code", "class=\"…\"") ," syntax in HTML) or array of strings. ",
"This is handy to concat conditional classes."
),
el("li").append(
"Use ", el("code", "classList"), " to toggle specific classes. This will be handy later when the reactivity via signals is beeing introduced.",
),
el("li").append(
"The ", el("code", "assign"), " also accepts the ", el("code", "undefined"), " as a value for any property to remove it from the element declaratively. ",
"Also for some IDL the corresponding attribute is removed as it can be confusing. ",
el("em").append(
"For example, natievly the elements ", el("code", "id"), " is removed by setting the IDL to an empty string."
)
)
),
el("p").append(
"For processing, the ", el("code", "assign"), " internally uses ", el("code", "assignAttribute"), " and ", el("code", "classListDeclarative"), "."
),
el(example, { src: "./components/examples/dekaAssign.js" }),
el("h3", "Native JavaScript templating"),
el("p", "By default, the native JS has no good way to define HTML template using DOM API:"),
el(example, { src: "./components/examples/nativeAppend.js" }),
el("p").append(
"This library therefore ooverwrites the ", el("code", "append"), " method to always return parent element."
),
el(example, { src: "./components/examples/dekaAppend.js" }),
el("h2", "Creating advanced (reactive) templates and components"),
el("h3", "Basic components"),
el("p").append(
"You can use functions for encapsulation (repeating) logic. ",
"The ", el("code", "el"), " accepts function as first argument. ",
"In that case, the function should return dom elements and the second argument for ", el("code", "el"), " is argument for given element."
),
el(example, { src: "./components/examples/dekaBasicComponent.js" }),
el("p", "It is nice to use similar naming convention as native DOM API.")
); );
} }
function pageName(pkg){ function pageName(pkg){

View File

@ -12,7 +12,7 @@ import { el } from "deka-dom-el";
* @param {object} def * @param {object} def
* */ * */
export function head({ id, title, description, pkg, path_target }){ export function head({ id, title, description, pkg, path_target }){
return el().append( return el("<>").append(
el("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), el("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
el("link", { rel: "stylesheet", href: stylesheetHref(path_target, id) }), el("link", { rel: "stylesheet", href: stylesheetHref(path_target, id) }),
@ -23,7 +23,7 @@ export function head({ id, title, description, pkg, path_target }){
); );
} }
function metaTwitter({ name, description, homepage }){ function metaTwitter({ name, description, homepage }){
return el().append( return el("<>").append(
el("meta", { name: "twitter:card", content: "summary_large_image" }), el("meta", { name: "twitter:card", content: "summary_large_image" }),
//el("meta", { name: "twitter:domain", content: "" }), //el("meta", { name: "twitter:domain", content: "" }),
el("meta", { name: "twitter:url", content: homepage }), el("meta", { name: "twitter:url", content: homepage }),
@ -34,7 +34,7 @@ function metaTwitter({ name, description, homepage }){
); );
} }
function metaFacebook({ name, description, homepage }){ function metaFacebook({ name, description, homepage }){
return el().append( return el("<>").append(
el("meta", { name: "og:url", content: homepage }), el("meta", { name: "og:url", content: homepage }),
el("meta", { name: "og:title", content: name }), el("meta", { name: "og:title", content: name }),
el("meta", { name: "og:description", content: description }), el("meta", { name: "og:description", content: description }),

3
index.d.ts vendored
View File

@ -38,13 +38,14 @@ type ElementAttributes<T extends keyof ElementTagNameMap | ElementTagNameMap[key
Omit<T,"classList"|"className"> & AttrsModified; Omit<T,"classList"|"className"> & AttrsModified;
export function assign<El extends Element>(element: El, ...attrs_array: Partial<ElementAttributes<El>>[]): El export function assign<El extends Element>(element: El, ...attrs_array: Partial<ElementAttributes<El>>[]): El
type TagNameFragment= "<>";
export function el<TAG extends keyof ElementTagNameMap>( export function el<TAG extends keyof ElementTagNameMap>(
tag_name: TAG, tag_name: TAG,
attrs?: Partial<ElementAttributes<ElementTagNameMap[TAG]>>, attrs?: Partial<ElementAttributes<ElementTagNameMap[TAG]>>,
...extenders: ddeElementExtender<ElementTagNameMap[TAG]>[] ...extenders: ddeElementExtender<ElementTagNameMap[TAG]>[]
): ElementTagNameMap[TAG] ): ElementTagNameMap[TAG]
export function el<T>( export function el<T>(
tag_name?: "<>", tag_name: TagNameFragment,
): DocumentFragment ): DocumentFragment
export function el< export function el<

View File

@ -48,7 +48,7 @@
"latedef": "true", "latedef": "true",
"maxparams": 5, "maxparams": 5,
"maxdepth": 3, "maxdepth": 3,
"maxcomplexity": 14, "maxcomplexity": 12,
"globals": { "globals": {
"requestIdleCallback": false, "requestIdleCallback": false,
"AbortController": false, "AbortController": false,

View File

@ -52,14 +52,14 @@ export function createElement(tag, attributes, ...connect){
scoped= 1; scoped= 1;
scope.push({ scope: tag, host: c=> c ? (scoped===1 ? connect.unshift(c) : c(el_host), undefined) : el_host }); scope.push({ scope: tag, host: c=> c ? (scoped===1 ? connect.unshift(c) : c(el_host), undefined) : el_host });
el= tag(attributes || undefined); el= tag(attributes || undefined);
const is_fragment= el instanceof DocumentFragment; const el_mark= document.createComment(`<dde:mark type="component" name="${tag.name}"/>`);
const el_mark= document.createComment(`<dde:mark type="component" name="${tag.name}" host="${is_fragment ? "this" : "parentElement"}"/>`);
el.prepend(el_mark); el.prepend(el_mark);
if(is_fragment) el_host= el_mark; if(el instanceof DocumentFragment)
el_host= el_mark;
break; break;
} }
case tag==="#text": el= assign.call(this, document.createTextNode(""), attributes); break; case tag==="#text": el= assign.call(this, document.createTextNode(""), attributes); break;
case tag==="<>" || !tag: el= assign.call(this, document.createDocumentFragment(), attributes); break; case tag==="<>": el= assign.call(this, document.createDocumentFragment(), attributes); break;
case namespace!=="html": el= assign.call(this, document.createElementNS(namespace, tag), attributes); break; case namespace!=="html": el= assign.call(this, document.createElementNS(namespace, tag), attributes); break;
case !el: el= assign.call(this, document.createElement(tag), attributes); case !el: el= assign.call(this, document.createElement(tag), attributes);
} }
@ -84,6 +84,7 @@ export function assign(element, ...attributes){
return element; return element;
} }
export function assignAttribute(element, key, value){ export function assignAttribute(element, key, value){
/* jshint maxcomplexity:14 */
const { setRemoveAttr, s }= assignContext(element, this); const { setRemoveAttr, s }= assignContext(element, this);
const _this= this; const _this= this;