all repos — h3 @ 20b159df7a56508b7f38c800c586ed73bbe65687

A tiny, extremely minimalist JavaScript microframework.

Updated docs etc.
h3rald h3rald@h3rald.com
Sat, 01 Aug 2020 12:25:29 +0200
commit

20b159df7a56508b7f38c800c586ed73bbe65687

parent

e6944be8d0056c3096d58a82273d403a29bcc694

M README.mdREADME.md

@@ -11,9 +11,9 @@ **H3** is a microframework to build client-side single-page applications (SPAs) in modern JavaScript.

H3 is also: -- **tiny**, less than 4KB minified gzipped. +- **tiny**, less than 4KB minified and gzipped. - **modern**, in the sense that it runs only in modern browsers (latest versions of Chrome, Firefox, Edge & similar). -- **easy** to learn, its API is comprised of only six methods and two properties. +- **easy** to learn, its API is comprised of only seven methods and two properties. ### I'm sold! Where can I get it?

@@ -31,7 +31,7 @@ Here's an example of an extremely minimal SPA created with H3:

```js import h3 from "./h3.js"; -h3.init(() => h3("h1", "Hello, World!")); +h3.init(() => h("h1", "Hello, World!")); ``` This will render a `h1` tag within the document body, containing the text `"Hello, World!"`.
M docs/H3_DeveloperGuide.htmdocs/H3_DeveloperGuide.htm

@@ -7264,7 +7264,7 @@ <li><a href="#Store">Store</a></li>

<li><a href="#Modules">Modules</a></li> <li><a href="#Router">Router</a> <ul> - <li><a href="#Route-Components">Route Components</a></li> + <li><a href="#Screen">Screen</a></li> </ul> </li> <li><a href="#How-everything-works...">How everything works...</a></li>

@@ -7274,13 +7274,13 @@ <li><a href="#Tutorial">Tutorial</a>

<ul> <li><a href="#Create-a-simple-HTML-file">Create a simple HTML file</a></li> <li><a href="#Create-a-single-page-application">Create a single-page application</a></li> - <li><a href="#Initialization-and-post-redraw-operations">Initialization and post-redraw operations</a></li> + <li><a href="#Initialization">Initialization</a></li> <li><a href="#Next-steps">Next steps</a></li> </ul> </li> <li><a href="#API">API</a> <ul> - <li><a href="#h3(selector:-string,-attributes:-object,-children:-array)">h3(selector: string, attributes: object, children: array)</a> + <li><a href="#h(selector:-string,-attributes:-object,-children:-array)">h(selector: string, attributes: object, children: array)</a> <ul> <li><a href="#Create-an-element,-with-an-ID,-classes,-attributes-and-children">Create an element, with an ID, classes, attributes and children</a></li> <li><a href="#Create-an-empty-element">Create an empty element</a></li>

@@ -7291,6 +7291,7 @@ <li><a href="#Render-child-components">Render child components</a></li>

<li><a href="#Special-attributes">Special attributes</a></li> </ul> </li> + <li><a href="#h3.screen({setup,-display,-teardown})">h3.screen({setup, display, teardown})</a></li> <li><a href="#h3.dispatch(event:-string,-data:-any)">h3.dispatch(event: string, data: any)</a></li> <li><a href="#h3.init(config:-object)">h3.init(config: object)</a></li> <li><a href="#h3.navigateTo(path:-string,-params:-object)">h3.navigateTo(path: string, params: object)</a></li>

@@ -7326,9 +7327,9 @@

<p>H3 is also:</p> <ul> -<li><strong>tiny</strong>, less than 4KB minified gzipped.</li> +<li><strong>tiny</strong>, less than 4KB minified and gzipped.</li> <li><strong>modern</strong>, in the sense that it runs only in modern browsers (latest versions of Chrome, Firefox, Edge &amp; similar).</li> -<li><strong>easy</strong> to learn, its API is comprised of only six methods and two properties.</li> +<li><strong>easy</strong> to learn, its API is comprised of only seven methods and two properties.</li> </ul>

@@ -7349,7 +7350,7 @@

<p>Here&rsquo;s an example of an extremely minimal SPA created with H3:</p> <pre><code class="js">import h3 from "./h3.js"; -h3.init(() =&gt; h3("h1", "Hello, World!")); +h3.init(() =&gt; h("h1", "Hello, World!")); </code></pre> <p>This will render a <code>h1</code> tag within the document body, containing the text <code>"Hello, World!"</code>.</p>

@@ -7425,10 +7426,10 @@ <pre><code class="js">// A simple component printing the current date and time

// Pressig the Refresh button causes the application to redraw // And updates the displayed date/dime. const Page = () =&gt; { - return h3("main", [ - h3("h1", "Welcome!"), - h3("p", `The current date and time is ${new Date()}`), - h3("button", { + return h("main", [ + h("h1", "Welcome!"), + h("p", `The current date and time is ${new Date()}`), + h("button", { onclick: () =&gt; h3.redraw() }, "Refresh") ]);

@@ -7453,11 +7454,11 @@ <p>The main difference between H3&rsquo;s HyperScript implementation and others is that it uses <strong>h3</strong> as the main constructor to create nodes. HyperScript uses <strong>h</strong>, Mithril uses <strong>m</strong>, &hellip;kind of an obvious choice if you ask me. If you don&rsquo;t like it, you can rename it to <em>piripicchio</em> if you want, and it will <em>still</em> be used in the same way.</p>

<p>How, you ask? Like this:</p> -<pre><code class="js">h3("div.test", [ - h3("ul", [ - h3("li", "This is..."), - h3("li", "...a simple..."), - h3("li", "unordered list.") +<pre><code class="js">h("div.test", [ + h("ul", [ + h("li", "This is..."), + h("li", "...a simple..."), + h("li", "unordered list.") ]) ]); </code></pre>

@@ -7484,7 +7485,7 @@ <p>Yes that&rsquo;s it. An example? here:</p>

<pre><code class="js">let count = 0; const CounterButton = () =&gt; { - return h3("button", { + return h("button", { onclick: () =&gt; count +=1 &amp;&amp; h3.redraw() }, `You clicked me ${count} times.`); }

@@ -7518,18 +7519,18 @@ <p>H3 comes with a very minimal but fully functional URL fragment router. You create your application routes when initializing your application, and you can navigate to them using ordinary <code>href</code> links or programmatically using the <code>h3.navigateTo</code> method.</p>

<p>The current route is always accessible via the <code>h3.route</code> property.</p> -<a name="Route-Components"></a> -<h4>Route Components<a href="#document-top" title="Go to top"></a></h4> +<a name="Screen"></a> +<h4>Screen<a href="#document-top" title="Go to top"></a></h4> -<p>A route component is a top-level component that handles a route. Unlike ordinary components, route components:</p> +<p>A screen is a top-level component that handles a route. Unlike ordinary components, screens:</p> <ul> -<li>may have a dedicated <em>setup</em> (after the route component is added to the DOM) and <em>teardown</em> phase (after the route component is removed from the DOM and before the new route component is loaded).</li> -<li>may have built-in local state, initialized during setup and (typically) destroyed during teardown. Such state is passed as the first (and only) parameter of the route component when executed.</li> +<li>may have a dedicated <em>setup</em> (after the screen is added to the DOM) and <em>teardown</em> phase (after the screen is removed from the DOM and before the new screen is loaded).</li> +<li>may have built-in local state, initialized during setup and (typically) destroyed during teardown. Such state is passed as the first (and only) parameter of the screen when executed.</li> </ul> -<p>Route components are stll created using ordinary function returning a VNode, but you can optionally define a <strong>setup</strong> and a <strong>teardown</strong> async methods on them (functions are objects in JavaScript after all&hellip;) to be executed during each corresponding phase.</p> +<p>Screens are typically created using the <strong>h3.screen</strong> shorthand method, but they can stll created using an ordinary function returning a VNode, but you can optionally define a <strong>setup</strong> and a <strong>teardown</strong> async methods on them (functions are objects in JavaScript after all&hellip;) to be executed during each corresponding phase.</p> <p>Note that: * Both the <strong>setup</strong> method take an object as a parameter, representing the component state. Such object will be empty the first time the <strong>setup</strong> method is called for a given component, but it may contain properties not removed during subsequent teardowns.

@@ -7551,9 +7552,9 @@ <li>Any <em>Module</em> specified when calling <code>h3.init()</code> is executed.</li>

<li>The <strong>$init</strong> event is dispatched.</li> <li>The <em>preStart</em> function (if specified when calling <code>h3.init()</code>) is executed.</li> <li>The <em>Router</em> is initialized and started.</li> -<li>The <strong>setup()</strong> method of the matching Route Component is called (if any).</li> +<li>The <strong>setup()</strong> method of the matching Screen is called (if any).</li> <li>The <strong>$navigation</strong> event is dispatched.</li> -<li>The <em>Route Component</em> matching the current route and all its child components are rendered for the first time.</li> +<li>The <em>Screen</em> matching the current route and all its child components are rendered for the first time.</li> <li>The <strong>$redraw</strong> event is dispatched.</li> </ol>

@@ -7570,12 +7571,12 @@ <p>Similarly, whenever the <code>h3.navigateTo()</code> method is called (typically within a component), or the URL fragment changes:</p>

<ol> <li>The <em>Router</em> processes the new path and determine which component to render based on the routing configuration.</li> -<li>The <strong>teardow()</strong> method of the current Route Component is called (if any).</li> -<li>The <strong>setup()</strong> method of the new matching Route Component is called (if any).</li> +<li>The <strong>teardow()</strong> method of the current Screen is called (if any).</li> +<li>The <strong>setup()</strong> method of the new matching Screen is called (if any).</li> <li>All DOM nodes within the scope of the routing are removed, all components are removed.</li> <li>The <strong>$navigation</strong> event is dispatched.</li> <li>All DOM nodes are removed.</li> -<li>The <em>Route Component</em> matching the new route and all its child components are rendered.</li> +<li>The <em>Screen</em> matching the new route and all its child components are rendered.</li> <li>The <strong>$redraw</strong> event is dispatched.</li> </ol>

@@ -7607,15 +7608,18 @@ <p>Start by creating a simple HTML file. Place a script loading the entry point of your application within the <code>body</code> and set its type to <code>module</code>.</p>

<p>This will let you load an ES6 file containing imports to other files&hellip; it works in all major browsers, but it doesn&rsquo;t work in IE (but we don&rsquo;t care about that, do we?).</p> -<pre><code class="html">&lt;!doctype html&gt; +<pre><code class="html">&lt;!DOCTYPE html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;meta charset="utf-8" /&gt; &lt;title&gt;H3&lt;/title&gt; - &lt;meta name="description" content="A bare-bones client-side web microframework" /&gt; + &lt;meta + name="description" + content="A bare-bones client-side web microframework" + /&gt; &lt;meta name="author" content="Fabio Cevasco" /&gt; &lt;meta name="viewport" content="width=device-width, initial-scale=1" /&gt; - &lt;link rel="shortcut icon" href="favicon.png" type="image/png"&gt; + &lt;link rel="shortcut icon" href="favicon.png" type="image/png" /&gt; &lt;link rel="stylesheet" href="css/mini-default.css" /&gt; &lt;link rel="stylesheet" href="css/prism.css" /&gt; &lt;link rel="stylesheet" href="css/style.css" /&gt;

@@ -7635,7 +7639,7 @@ <p>Normally you&rsquo;d have several components, at least one file containing modules to manage the application state, etc. (see the <a href="https://github.com/h3rald/h3/tree/master/docs/example">todo list example</a>), but in this case a single component is sufficient.</p>

<p>Start by importing all the JavaScript modules you need:</p> -<pre><code class="js">import h3 from "./h3.js"; +<pre><code class="js">import { h3, h } from "./h3.js"; import marked from "./vendor/marked.js"; import Prism from "./vendor/prism.js"; </code></pre>

@@ -7654,14 +7658,11 @@ </code></pre>

<p>We are going to store the HTML contents of each page in an Object, and we&rsquo;re going to need a simple function to <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">fetch</a> the Markdown file and render it as HTML:</p> -<pre><code class="js">const pages = {}; - -const fetchPage = async (pages, id, md) =&gt; { +<pre><code class="js">const fetchPage = async ({ pages, id, md }) =&gt; { if (!pages[id]) { const response = await fetch(md); const text = await response.text(); pages[id] = marked(text); - h3.redraw(); } }; </code></pre>

@@ -7670,49 +7671,64 @@ <p>Basically this function is going to be called when you navigate to each page, and it:</p>

<ol> <li>fetches the content of the requested file (<code>md</code>))</li> -<li>renders the Markdown code into HTML using marked, and stores it in the <code>pages</code> object</li> -<li>Triggers a redraw of the application</li> +<li>renders the Markdown code into HTML using the <em>marked</em> library, and stores it in the <code>pages</code> object</li> </ol> -<p>We are gonna use our <code>fetchPage</code> function inside the main component of our app, <code>Page</code>:</p> +<p>We are gonna use our <code>fetchPage</code> function inside the <code>setup</code> of the main (and only) screen of our app, <code>Page</code>:</p> -<pre><code class="js">const Page = () =&gt; { - const id = h3.route.path.slice(1); - const ids = Object.keys(labels); - const md = ids.includes(id) ? `md/${id}.md` : `md/overview.md`; - fetchPage(pages, id, md); - return h3("div.page", [ - Header, - h3("div.row", [ - h3("input#drawer-control.drawer", { type: "checkbox" }), - Navigation(id, ids), - Content(pages[id]), - Footer, - ]), - ]); -}; +<pre><code class="js">const Page = h3.screen({ + setup: async (state) =&gt; { + state.pages = {}; + state.id = h3.route.path.slice(1); + state.ids = Object.keys(labels); + state.md = state.ids.includes(state.id) + ? `md/${state.id}.md` + : `md/overview.md`; + await fetchPage(state); + }, + display: (state) =&gt; { + return h("div.page", [ + Header, + h("div.row", [ + h("input#drawer-control.drawer", { type: "checkbox" }), + Navigation(state.id, state.ids), + Content(state.pages[state.id]), + Footer, + ]), + ]); + }, + teardown: (state) =&gt; state, +}); </code></pre> -<p>The main responsibility of this component is to fetch the Markdown content and render the whole page, but note how the rendering different portions of the page are delegated to different components: <code>Header</code>, <code>Navigation</code>, <code>Content</code>, and <code>Footer</code>.</p> +<p>Note that this screen has a <code>setup</code>, a <code>display</code> and a <code>teardown</code> method, both taking <code>state</code> as parameter. In H3, screens are nothing but stateful components that are used to render the whole page of the application, and are therefore typically redered when navigating to a new route.</p> + +<p>The <code>state</code> parameter is nothing but an empty object that can be used to store data that will be accessible to the <code>setup</code>, <code>display</code> and <code>teardown</code> methods, and (typically) will be destroyed when another screen is rendered.</p> + +<p>The <code>setup</code> function allows you to perform some operations that should take place <em>before</em> the screen is rendered. In this case, we want to fetch the page contents (if necessary) beforehand to avoid displaying a spinner while the content is being loaded. Note that the <code>setup</code> method can be asynchronous, and in this case the <code>display</code> method will not be called until all asynchronous operations have been completed (assuming you are <code>await</code>ing them).</p> + +<p>The <code>teardown</code> function in this case only makes sure that the existing screen state (in particular any loaded markdown page) will be passed on to the next screen during navigation (which, in this case, is still the <code>Page</code> screen), so that existing pages will not be fetched again.</p> + +<p>The main responsibility of this screen is to fetch the Markdown content and render the whole page, but note how the rendering different portions of the page are delegated to different components: <code>Header</code>, <code>Navigation</code>, <code>Content</code>, and <code>Footer</code>.</p> <p>The <code>Header</code> and <code>Footer</code> components are very simple: their only job is to render static content:</p> <pre><code class="js">const Header = () =&gt; { - return h3("header.row.sticky", [ - h3("a.logo.col-sm-1", { href: "#/" }, [ - h3("img", { alt: "H3", src: "images/h3.svg" }), + return h("header.row.sticky", [ + h("a.logo.col-sm-1", { href: "#/" }, [ + h("img", { alt: "H3", src: "images/h3.svg" }), ]), - h3("div.version.col-sm.col-md", [ - h3("div.version-number", "v0.10.0"), - h3("div.version-label", "“Jittery Jem'Hadar“"), + h("div.version.col-sm.col-md", [ + h("div.version-number", "v0.10.0"), + h("div.version-label", "“Jittery Jem'Hadar“"), ]), - h3("label.drawer-toggle.button.col-sm-last", { for: "drawer-control" }), + h("label.drawer-toggle.button.col-sm-last", { for: "drawer-control" }), ]); }; const Footer = () =&gt; { - return h3("footer", [h3("div", "© 2020 Fabio Cevasco")]); + return h("footer", [h("div", "© 2020 Fabio Cevasco")]); }; </code></pre>

@@ -7728,23 +7744,24 @@ <p>&hellip;and it uses this information to create the site navigation menu dynamically:</p>

<pre><code class="js">const Navigation = (id, ids) =&gt; { const menu = ids.map((p) =&gt; - h3(`a${p === id ? ".active" : ""}`, { href: `#/${p}` }, labels[p]) + h(`a${p === id ? ".active" : ""}`, { href: `#/${p}` }, labels[p]) ); - return h3("nav#navigation.col-md-3", [ - h3("label.drawer-close", { for: "drawer-control" }), + return h("nav#navigation.col-md-3", [ + h("label.drawer-close", { for: "drawer-control" }), ...menu, ]); }; </code></pre> -<p>Finally, the <code>Content</code> component optionally takes a string containing the HTML of the page content to render. If no content is provided, it will display a loading spinner, otherwise it will render the content by using the special <code>$html</code> attribute that can be used to essentially set the <code>innerHTML</code> of an element:</p> +<p>Finally, the <code>Content</code> component takes a string containing the HTML of the page content to render using the special <code>$html</code> attribute that can be used to essentially set the <code>innerHTML</code> property of an element:</p> <pre><code class="js">const Content = (html) =&gt; { - const content = html - ? h3("div.content", { $html: html }) - : h3("div.spinner-container", h3("span.spinner")); - return h3("main.col-sm-12.col-md-9", [ - h3("div.card.fluid", h3("div.section", content)), + const content = h("div.content", { $html: html }); + return h("main.col-sm-12.col-md-9", [ + h( + "div.card.fluid", + h("div.section", { $onrender: () =&gt; Prism.highlightAll() }, content) + ), ]); }; </code></pre>

@@ -7757,27 +7774,24 @@ <p>In a similar way, other well-known pages can easily be mapped to IDs, but it is also important to handle <em>unknown</em> pages (technically I could even pass an URL to a different site containing a malicious markdown page and have it rendered!), and if a page passed in the URL fragment is not present in the <code>labels</code> Object, the Overview page will be rendered instead.</p>

<p>This feature is also handy to automatically load the Overview when no fragment is specified.</p> -<a name="Initialization-and-post-redraw-operations"></a> -<h3>Initialization and post-redraw operations<a href="#document-top" title="Go to top"></a></h3> +<p>What is that weird <code>$onrender</code> property you ask? Well, that&rsquo;s a H3-specific callback that will be executed whenever the corresponding DOM node is rendered&hellip; that&rsquo;s essentially the perfect place to for executing operations that must be perform when the DOM is fully available, like highlighting our code snippets using <em>Prism</em> in this case.</p> + +<a name="Initialization"></a> +<h3>Initialization<a href="#document-top" title="Go to top"></a></h3> <p>Done? Not quite. We need to initialize the SPA by passing the <code>Page</code> component to the <code>h3.init()</code> method to trigger the first rendering:</p> <pre><code class="js">h3.init(Page); </code></pre> -<p>And that&rsquo;s it. Noooo wait, what about syntax highlighting? That needs to be applied <em>after</em> the HTML markup is rendered. How can we manage that?</p> - -<p>Easy enough, add a handler to be executed whenever the SPA is redrawn:</p> - -<pre><code class="js">h3.on("$redraw", () =&gt; Prism.highlightAll()); -</code></pre> +<p>And that&rsquo;s it. Now, keep in mind that this is the <em>short</em> version of initialization using a single component and a single route, but still, that&rsquo;s good enough for our use case.</p> <a name="Next-steps"></a> <h3>Next steps<a href="#document-top" title="Go to top"></a></h3> <p>Made it this far? Good. Wanna know more? Have a look at the code of the <a href="https://github.com/h3rald/h3/tree/master/docs/example">todo list example</a> and try it out <a href="https://h3.js.org/example/index.html">here</a>.</p> -<p>Once you feel more comfortable and you are ready to dive into a more complex application, featuring different routes, route components, forms, confirmation messages, plenty of third-party components etc., have a look at <a href="https://github.com/h3rald/litepad">LitePad</a>. You can see it in action here: <a href="https://litepad.h3rald.com/">litepad.h3rald.com</a>.</p> +<p>Once you feel more comfortable and you are ready to dive into a more complex application, featuring different routes, screens, forms, validation, confirmation messages, plenty of third-party components etc., have a look at <a href="https://github.com/h3rald/litepad">LitePad</a>. You can see it in action here: <a href="https://litepad.h3rald.com/">litepad.h3rald.com</a>.</p> <p>Note: the LitePad online demo will store all its data in localStorage.</p>

@@ -7786,10 +7800,10 @@ <h2>API<a href="#document-top" title="Go to top"></a></h2>

<p>The core of the H3 API is comprised of the following six methods and two properties.</p> -<a name="h3(selector:-string,-attributes:-object,-children:-array)"></a> -<h3>h3(selector: string, attributes: object, children: array)<a href="#document-top" title="Go to top"></a></h3> +<a name="h(selector:-string,-attributes:-object,-children:-array)"></a> +<h3>h(selector: string, attributes: object, children: array)<a href="#document-top" title="Go to top"></a></h3> -<p>The <code>h3</code> object is also used as a constructor for Virtual DOM Nodes (VNodes). It can actually take from one to three arguments used to configure the resulting node.</p> +<p>The <code>h</code> function is a constructor for Virtual DOM Nodes (VNodes). It can actually take from one to any number of arguments used to configure the resulting node.</p> <p>The best way to understand how it works is by providing a few different examples. Please note that in each example the corresponding <em>HTML</em> markup is provided, although such markup will actually be generated when the Virtual Node is rendered/redrawn.</p>

@@ -7798,9 +7812,13 @@ <h4>Create an element, with an ID, classes, attributes and children<a href="#document-top" title="Go to top"></a></h4>

<p>This is a complete example showing how to create a link with an <code>href</code> attribute, an ID, two classes, and three child nodes.</p> -<pre><code class="js">h3("a#test-link.btn.primary", { - href: "#/test" -}, ["This is a ", h3("em", "test"), "link."]); +<pre><code class="js">h( + "a#test-link.btn.primary", + { + href: "#/test", + }, + ["This is a ", h("em", "test"), "link."] +); </code></pre> <p>↓</p>

@@ -7813,7 +7831,7 @@

<a name="Create-an-empty-element"></a> <h4>Create an empty element<a href="#document-top" title="Go to top"></a></h4> -<pre><code class="js">h3("div"); +<pre><code class="js">h("div"); </code></pre> <p>↓</p>

@@ -7824,7 +7842,7 @@

<a name="Create-an-element-with-a-textual-child-node"></a> <h4>Create an element with a textual child node<a href="#document-top" title="Go to top"></a></h4> -<pre><code class="js">h3("span", "This is a test"); +<pre><code class="js">h("span", "This is a test"); </code></pre> <p>↓</p>

@@ -7835,13 +7853,23 @@

<a name="Create-an-element-with-child-nodes"></a> <h4>Create an element with child nodes<a href="#document-top" title="Go to top"></a></h4> -<pre><code class="js">h3("ol", [ - h3("li", "Do this first."), - h3("li", "Then this."), - h3("li", "And finally this.") +<pre><code class="js">h("ol", [ + h("li", "Do this first."), + h("li", "Then this."), + h("li", "And finally this."), ]); </code></pre> +<p><em>or</em></p> + +<pre><code class="js">h( + "ol", + h("li", "Do this first."), + h("li", "Then this."), + h("li", "And finally this.") +); +</code></pre> + <p>↓</p> <pre><code class="html">&lt;ol&gt;

@@ -7855,11 +7883,15 @@ <a name="Render-a-component"></a>

<h4>Render a component<a href="#document-top" title="Go to top"></a></h4> <pre><code class="js">const TestComponent = () =&gt; { - return h3("button.test", { - onclick: () =&gt; alert("Hello!") - }, "Show Alert"); + return h( + "button.test", + { + onclick: () =&gt; alert("Hello!"), + }, + "Show Alert" + ); }; -h3(TestComponent); +h(TestComponent); </code></pre> <p>↓</p>

@@ -7872,8 +7904,8 @@

<a name="Render-child-components"></a> <h4>Render child components<a href="#document-top" title="Go to top"></a></h4> -<pre><code class="js">const TestLi = (text) =&gt; h3("li.test", text); -h3("ul", ["A", "B", "C"].map(TestLi)); +<pre><code class="js">const TestLi = (text) =&gt; h("li.test", text); +h("ul", ["A", "B", "C"].map(TestLi)); </code></pre> <p>↓</p>

@@ -7901,15 +7933,54 @@ <p>The <code>$html</code> and the <code>$onrender</code> special attributes should be used sparingly, and typically only when interfacing with third-party libraries that need access to the real DOM.</p>

<p>For example, consider the following code snippet that can be used to initialize the <a href="https://github.com/Inscryb/inscryb-markdown-editor">InscrybMDE</a> Markdown editor on a textarea element:</p> -<pre><code class="js">h3("textarea", { +<pre><code class="js">h("textarea", { $onrender: (element) =&gt; { const editor = new window.InscrybMDE({ - element + element, }); - } + }, }); </code></pre> +<a name="h3.screen({setup,-display,-teardown})"></a> +<h3>h3.screen({setup, display, teardown})<a href="#document-top" title="Go to top"></a></h3> + +<p>Creates a Screen by providing a (mandatory) <strong>display</strong> function used to render content, an an optional <strong>setup</strong> and <strong>teardown</strong> functions, executed before and after the display function respectively.</p> + +<p>Each of these functions takes a single <strong>screen</strong> parameter, which is initialized as an empty object before the setup, and is (optionally) returned by the teardown function should state be preserved across different screens.</p> + +<p>Consider the following example:</p> + +<pre><code class="js">h3.screen({ + setup: await (state) =&gt; { + state.data = state.data || {}; + state.id = h3.route.parts.id || 1; + const url = `http://dummy.restapiexample.com/api/v1/employee/${id}`; + state.data[id] = state.data[id] || await (await fetch(url)).json(); + }, + display(state) =&gt; { + const employee = state.data[state.id]; + if (!employee) { + return h("div.error", "Invalid Employee ID."); + } + return h("div.employee", + h("h2", "Employee Profile"), + h("dl", [ + h("dt", "Name:"), + h("dd", data.employee_name), + h("dt", "Salary:"), + h("dd", `${data.employee_salary} €`), + h("dt", "Age:"), + h("dd", data.employee_age), + ]) + ) + }, + teardown: (state) =&gt; ({ data: state.data }) +}) +</code></pre> + +<p>This example shows how to implement a simple component that renders an employee profile in the <code>display</code> function, fetches data (if necessary) in the <code>setup</code> function, and preserves data in the <code>teardown</code> function.</p> + <a name="h3.dispatch(event:-string,-data:-any)"></a> <h3>h3.dispatch(event: string, data: any)<a href="#document-top" title="Go to top"></a></h3>

@@ -7960,7 +8031,7 @@ <p>Navigates to the specified path. Optionally, it is possibile to specify query string parameters as an object.</p>

<p>The following call causes the application to switch to the following URL: <code>#/posts/?orderBy=date&amp;direction=desc</code>.</p> -<pre><code class="js">h3.navigateTo("/posts/", {orderBy: 'date', direction: 'desc'}); +<pre><code class="js">h3.navigateTo("/posts/", { orderBy: "date", direction: "desc" }); </code></pre> <a name="h3.on(event:-string,-handler:-function)"></a>

@@ -8058,7 +8129,7 @@ </ul>

</div> <div id="footer"> - <p><span class="copy"></span> Fabio Cevasco &ndash; July 29, 2020</p> + <p><span class="copy"></span> Fabio Cevasco &ndash; August 1, 2020</p> <p><span>Powered by</span> <a href="https://h3rald.com/hastyscribe"><span class="hastyscribe"></span></a></p> </div> </div>
M docs/example/assets/js/h3.jsdocs/example/assets/js/h3.js

@@ -633,15 +633,17 @@ if (!this.route) {

throw new Error(`[Router] No route matches '${fragment}'`); } // Old route component teardown + let state = {}; if (oldRoute) { const oldRouteComponent = this.routes[oldRoute.def]; - oldRouteComponent.state = - oldRouteComponent.teardown && - (await oldRouteComponent.teardown(oldRouteComponent.state)); + state = + (oldRouteComponent.teardown && + (await oldRouteComponent.teardown(oldRouteComponent.state))) || + state; } // New route component setup const newRouteComponent = this.routes[this.route.def]; - newRouteComponent.state = {}; + newRouteComponent.state = state; newRouteComponent.setup && (await newRouteComponent.setup(newRouteComponent.state)); // Redrawing...
M docs/js/app.jsdocs/js/app.js

@@ -3,76 +3,81 @@ import marked from "./vendor/marked.js";

import Prism from "./vendor/prism.js"; const labels = { - overview: "Overview", - "quick-start": "Quick Start", - "key-concepts": "Key Concepts", - tutorial: "Tutorial", - api: "API", - about: "About", + overview: "Overview", + "quick-start": "Quick Start", + "key-concepts": "Key Concepts", + tutorial: "Tutorial", + api: "API", + about: "About", }; -const pages = {}; - -const fetchPage = async (pages, id, md) => { - if (!pages[id]) { - const response = await fetch(md); - const text = await response.text(); - pages[id] = marked(text); - h3.redraw(); - } -}; - -const Page = () => { - const id = h3.route.path.slice(1); - const ids = Object.keys(labels); - const md = ids.includes(id) ? `md/${id}.md` : `md/overview.md`; - fetchPage(pages, id, md); +const Page = h3.screen({ + setup: async (state) => { + state.pages = state.pages || {}; + state.id = h3.route.path.slice(1); + state.ids = Object.keys(labels); + state.md = state.ids.includes(state.id) + ? `md/${state.id}.md` + : `md/overview.md`; + await fetchPage(state); + }, + display: (state) => { return h("div.page", [ - Header, - h("div.row", [ - h("input#drawer-control.drawer", { type: "checkbox" }), - Navigation(id, ids), - Content(pages[id]), - Footer, - ]), + Header, + h("div.row", [ + h("input#drawer-control.drawer", { type: "checkbox" }), + Navigation(state.id, state.ids), + Content(state.pages[state.id]), + Footer, + ]), ]); + }, + teardown: (state) => state, +}); + +const fetchPage = async ({ pages, id, md }) => { + if (!pages[id]) { + const response = await fetch(md); + const text = await response.text(); + pages[id] = marked(text); + } }; const Header = () => { - return h("header.row.sticky", [ - h("a.logo.col-sm-1", { href: "#/" }, [ - h("img", { alt: "H3", src: "images/h3.svg" }), - ]), - h("div.version.col-sm.col-md", [ - h("div.version-number", "v0.10.0"), - h("div.version-label", "“Jittery Jem'Hadar“"), - ]), - h("label.drawer-toggle.button.col-sm-last", { for: "drawer-control" }), - ]); + return h("header.row.sticky", [ + h("a.logo.col-sm-1", { href: "#/" }, [ + h("img", { alt: "H3", src: "images/h3.svg" }), + ]), + h("div.version.col-sm.col-md", [ + h("div.version-number", "v0.10.0"), + h("div.version-label", "“Jittery Jem'Hadar“"), + ]), + h("label.drawer-toggle.button.col-sm-last", { for: "drawer-control" }), + ]); }; const Footer = () => { - return h("footer", [h("div", "© 2020 Fabio Cevasco")]); + return h("footer", [h("div", "© 2020 Fabio Cevasco")]); }; const Navigation = (id, ids) => { - const menu = ids.map((p) => - h(`a${p === id ? ".active" : ""}`, { href: `#/${p}` }, labels[p]) - ); - return h("nav#navigation.col-md-3", [ - h("label.drawer-close", { for: "drawer-control" }), - ...menu, - ]); + const menu = ids.map((p) => + h(`a${p === id ? ".active" : ""}`, { href: `#/${p}` }, labels[p]) + ); + return h("nav#navigation.col-md-3", [ + h("label.drawer-close", { for: "drawer-control" }), + ...menu, + ]); }; const Content = (html) => { - const content = html - ? h("div.content", { $html: html }) - : h("div.spinner-container", h("span.spinner")); - return h("main.col-sm-12.col-md-9", [ - h("div.card.fluid", h("div.section", content)), - ]); + const content = h("div.content", { $html: html }); + return h("main.col-sm-12.col-md-9", [ + h( + "div.card.fluid", + h("div.section", { $onrender: () => Prism.highlightAll() }, content) + ), + ]); }; h3.init(Page); -h3.on("$redraw", () => Prism.highlightAll());
M docs/js/h3.jsdocs/js/h3.js

@@ -633,15 +633,17 @@ if (!this.route) {

throw new Error(`[Router] No route matches '${fragment}'`); } // Old route component teardown + let state = {}; if (oldRoute) { const oldRouteComponent = this.routes[oldRoute.def]; - oldRouteComponent.state = - oldRouteComponent.teardown && - (await oldRouteComponent.teardown(oldRouteComponent.state)); + state = + (oldRouteComponent.teardown && + (await oldRouteComponent.teardown(oldRouteComponent.state))) || + state; } // New route component setup const newRouteComponent = this.routes[this.route.def]; - newRouteComponent.state = {}; + newRouteComponent.state = state; newRouteComponent.setup && (await newRouteComponent.setup(newRouteComponent.state)); // Redrawing...
M docs/md/api.mddocs/md/api.md

@@ -2,21 +2,24 @@ ## API

The core of the H3 API is comprised of the following six methods and two properties. +### h(selector: string, attributes: object, children: array) -### h3(selector: string, attributes: object, children: array) - -The `h3` object is also used as a constructor for Virtual DOM Nodes (VNodes). It can actually take from one to three arguments used to configure the resulting node. +The `h` function is a constructor for Virtual DOM Nodes (VNodes). It can actually take from one to any number of arguments used to configure the resulting node. -The best way to understand how it works is by providing a few different examples. Please note that in each example the corresponding *HTML* markup is provided, although such markup will actually be generated when the Virtual Node is rendered/redrawn. +The best way to understand how it works is by providing a few different examples. Please note that in each example the corresponding _HTML_ markup is provided, although such markup will actually be generated when the Virtual Node is rendered/redrawn. #### Create an element, with an ID, classes, attributes and children This is a complete example showing how to create a link with an `href` attribute, an ID, two classes, and three child nodes. ```js -h3("a#test-link.btn.primary", { - href: "#/test" -}, ["This is a ", h3("em", "test"), "link."]); +h( + "a#test-link.btn.primary", + { + href: "#/test", + }, + ["This is a ", h("em", "test"), "link."] +); ```

@@ -30,7 +33,7 @@

#### Create an empty element ```js -h3("div"); +h("div"); ```

@@ -42,7 +45,7 @@

#### Create an element with a textual child node ```js -h3("span", "This is a test"); +h("span", "This is a test"); ```

@@ -54,13 +57,24 @@

#### Create an element with child nodes ```js -h3("ol", [ - h3("li", "Do this first."), - h3("li", "Then this."), - h3("li", "And finally this.") +h("ol", [ + h("li", "Do this first."), + h("li", "Then this."), + h("li", "And finally this."), ]); ``` +_or_ + +```js +h( + "ol", + h("li", "Do this first."), + h("li", "Then this."), + h("li", "And finally this.") +); +``` + ```html

@@ -75,11 +89,15 @@ #### Render a component

```js const TestComponent = () => { - return h3("button.test", { - onclick: () => alert("Hello!") - }, "Show Alert"); + return h( + "button.test", + { + onclick: () => alert("Hello!"), + }, + "Show Alert" + ); }; -h3(TestComponent); +h(TestComponent); ```

@@ -93,8 +111,8 @@

#### Render child components ```js -const TestLi = (text) => h3("li.test", text); -h3("ul", ["A", "B", "C"].map(TestLi)); +const TestLi = (text) => h("li.test", text); +h("ul", ["A", "B", "C"].map(TestLi)); ```

@@ -109,26 +127,65 @@ ```

#### Special attributes -* Any attribute starting with *on* (e.g. onclick, onkeydown, etc.) will be treated as an event listener. -* The `classList` attribute can be set to a list of classes to apply to the element (as an alternative to using the element selector shorthand). -* The `data` attribute can be set to a simple object containing [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes). -* The special `$html` attribute can be used to set the `innerHTML` property of the resulting HTML element. Use only if you know what you are doing! -* The special `$onrender` attribute can be set to a function that will executed every time the VNode is rendered and added to the DOM. +- Any attribute starting with _on_ (e.g. onclick, onkeydown, etc.) will be treated as an event listener. +- The `classList` attribute can be set to a list of classes to apply to the element (as an alternative to using the element selector shorthand). +- The `data` attribute can be set to a simple object containing [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes). +- The special `$html` attribute can be used to set the `innerHTML` property of the resulting HTML element. Use only if you know what you are doing! +- The special `$onrender` attribute can be set to a function that will executed every time the VNode is rendered and added to the DOM. -The `$html` and the `$onrender` special attributes should be used sparingly, and typically only when interfacing with third-party libraries that need access to the real DOM. +The `$html` and the `$onrender` special attributes should be used sparingly, and typically only when interfacing with third-party libraries that need access to the real DOM. For example, consider the following code snippet that can be used to initialize the [InscrybMDE](https://github.com/Inscryb/inscryb-markdown-editor) Markdown editor on a textarea element: ```js -h3("textarea", { +h("textarea", { $onrender: (element) => { const editor = new window.InscrybMDE({ - element + element, }); - } + }, }); ``` +### h3.screen({setup, display, teardown}) + +Creates a Screen by providing a (mandatory) **display** function used to render content, an an optional **setup** and **teardown** functions, executed before and after the display function respectively. + +Each of these functions takes a single **screen** parameter, which is initialized as an empty object before the setup, and is (optionally) returned by the teardown function should state be preserved across different screens. + +Consider the following example: + +```js +h3.screen({ + setup: await (state) => { + state.data = state.data || {}; + state.id = h3.route.parts.id || 1; + const url = `http://dummy.restapiexample.com/api/v1/employee/${id}`; + state.data[id] = state.data[id] || await (await fetch(url)).json(); + }, + display(state) => { + const employee = state.data[state.id]; + if (!employee) { + return h("div.error", "Invalid Employee ID."); + } + return h("div.employee", + h("h2", "Employee Profile"), + h("dl", [ + h("dt", "Name:"), + h("dd", data.employee_name), + h("dt", "Salary:"), + h("dd", `${data.employee_salary} €`), + h("dt", "Age:"), + h("dd", data.employee_age), + ]) + ) + }, + teardown: (state) => ({ data: state.data }) +}) +``` + +This example shows how to implement a simple component that renders an employee profile in the `display` function, fetches data (if necessary) in the `setup` function, and preserves data in the `teardown` function. + ### h3.dispatch(event: string, data: any) Dispatches a event and optionally some data. Messages are typically handled centrally by modules.

@@ -139,20 +196,20 @@ ```

A event name can be any string, but keep in mind that the following names (and typically any name starting with `$`) are reserved for framework use: -* `$init` &mdash; Dispatched when the application is initialized. Useful to initialize application state. -* `$redraw` &mdash; Dispatched after an application redraw is triggered. -* `$navigation` &mdash; Dispatched after a navigation occurs. -* `$log` &mdash; Dispatched after *any* event (except `$log` iself) is dispatched. Very useful for debugging. +- `$init` &mdash; Dispatched when the application is initialized. Useful to initialize application state. +- `$redraw` &mdash; Dispatched after an application redraw is triggered. +- `$navigation` &mdash; Dispatched after a navigation occurs. +- `$log` &mdash; Dispatched after _any_ event (except `$log` iself) is dispatched. Very useful for debugging. ### h3.init(config: object) The initialization method of every H3 application. You _must_ call this method once to initialize your application by providing a component to render or configuration object with the following properties: -* **element** (Element) &mdash; The DOM Element to which the Application will be attached (default: `document.body`). -* **modules** (Array) &mdash; An array of functions used to handle the application state that will be executed once before starting the application. -* **routes** (Object) &mdash; An object containing routing definitions, using paths as keys and components as values. Routing paths can contain named parts like `:name` or `:id` which will populate the `parts` property of the current route (`h3.route`). -* **preStart** (Function) &mdash; An optional function to be executed before the application is first rendered. -* **postStart** (Function) &mdash; An optional function to be executed after the application is first rendered. +- **element** (Element) &mdash; The DOM Element to which the Application will be attached (default: `document.body`). +- **modules** (Array) &mdash; An array of functions used to handle the application state that will be executed once before starting the application. +- **routes** (Object) &mdash; An object containing routing definitions, using paths as keys and components as values. Routing paths can contain named parts like `:name` or `:id` which will populate the `parts` property of the current route (`h3.route`). +- **preStart** (Function) &mdash; An optional function to be executed before the application is first rendered. +- **postStart** (Function) &mdash; An optional function to be executed after the application is first rendered. This is an example of a simple routing configuration:

@@ -173,14 +230,14 @@

The following call causes the application to switch to the following URL: `#/posts/?orderBy=date&direction=desc`. ```js -h3.navigateTo("/posts/", {orderBy: 'date', direction: 'desc'}); +h3.navigateTo("/posts/", { orderBy: "date", direction: "desc" }); ``` ### h3.on(event: string, handler: function) Subscribes to the specified event and executes the specified handler function whenever the event is dispatched. Returns a function that can be used to delete the subscription. -Subscriptions should be typically managed in modules rather than in components: a component gets rendered several times and subscriptions *must* be properly cleaned up to avoid memory leaks. +Subscriptions should be typically managed in modules rather than in components: a component gets rendered several times and subscriptions _must_ be properly cleaned up to avoid memory leaks. Example:

@@ -193,18 +250,18 @@ ```

### h3.redraw() -Triggers an application redraw. Unlike most frameworks, in H3 redraws *must* be triggered explicitly. Just call this method whenever you want something to change and components to re-render. +Triggers an application redraw. Unlike most frameworks, in H3 redraws _must_ be triggered explicitly. Just call this method whenever you want something to change and components to re-render. ### h3.route An read-only property containing current route (Route object). A Route object has the following properties: -* **path** &mdash; The current path (fragment without #) without query string parameters, e.g. `/posts/134` -* **def** &mdash; The matching route definition, e.g. `/posts/:id` -* **query** &mdash; The query string, if present, e.g. `?comments=yes` -* **part** &mdash; An object containing the values of the parts defined in the route, e.g. `{id: "134"}` -* **params** &mdash; An object containing the query string parameters, e.g. `{comments: "yet"}` +- **path** &mdash; The current path (fragment without #) without query string parameters, e.g. `/posts/134` +- **def** &mdash; The matching route definition, e.g. `/posts/:id` +- **query** &mdash; The query string, if present, e.g. `?comments=yes` +- **part** &mdash; An object containing the values of the parts defined in the route, e.g. `{id: "134"}` +- **params** &mdash; An object containing the query string parameters, e.g. `{comments: "yet"}` ### h3.state -A read-only property containing the current application state. The state is a plain object, but its properties should only be modified using event subscription handlers. +A read-only property containing the current application state. The state is a plain object, but its properties should only be modified using event subscription handlers.
M docs/md/key-concepts.mddocs/md/key-concepts.md

@@ -13,11 +13,11 @@

How, you ask? Like this: ```js -h3("div.test", [ - h3("ul", [ - h3("li", "This is..."), - h3("li", "...a simple..."), - h3("li", "unordered list.") +h("div.test", [ + h("ul", [ + h("li", "This is..."), + h("li", "...a simple..."), + h("li", "unordered list.") ]) ]); ```

@@ -45,7 +45,7 @@

```js let count = 0; const CounterButton = () => { - return h3("button", { + return h("button", { onclick: () => count +=1 && h3.redraw() }, `You clicked me ${count} times.`); }

@@ -78,14 +78,14 @@

The current route is always accessible via the `h3.route` property. -#### Route Components +#### Screen -A route component is a top-level component that handles a route. Unlike ordinary components, route components: +A screen is a top-level component that handles a route. Unlike ordinary components, screens: -* may have a dedicated *setup* (after the route component is added to the DOM) and *teardown* phase (after the route component is removed from the DOM and before the new route component is loaded). -* may have built-in local state, initialized during setup and (typically) destroyed during teardown. Such state is passed as the first (and only) parameter of the route component when executed. +* may have a dedicated *setup* (after the screen is added to the DOM) and *teardown* phase (after the screen is removed from the DOM and before the new screen is loaded). +* may have built-in local state, initialized during setup and (typically) destroyed during teardown. Such state is passed as the first (and only) parameter of the screen when executed. -Route components are stll created using ordinary function returning a VNode, but you can optionally define a **setup** and a **teardown** async methods on them (functions are objects in JavaScript after all...) to be executed during each corresponding phase. +Screens are typically created using the **h3.screen** shorthand method, but they can stll created using an ordinary function returning a VNode, but you can optionally define a **setup** and a **teardown** async methods on them (functions are objects in JavaScript after all...) to be executed during each corresponding phase. Note that: * Both the **setup** method take an object as a parameter, representing the component state. Such object will be empty the first time the **setup** method is called for a given component, but it may contain properties not removed during subsequent teardowns.

@@ -105,9 +105,9 @@ 2. Any *Module* specified when calling `h3.init()` is executed.

3. The **$init** event is dispatched. 4. The *preStart* function (if specified when calling `h3.init()`) is executed. 5. The *Router* is initialized and started. -6. The **setup()** method of the matching Route Component is called (if any). +6. The **setup()** method of the matching Screen is called (if any). 8. The **$navigation** event is dispatched. -9. The *Route Component* matching the current route and all its child components are rendered for the first time. +9. The *Screen* matching the current route and all its child components are rendered for the first time. 10. The **$redraw** event is dispatched. Then, whenever the `h3.redraw()` method is called (typically within a component):

@@ -118,12 +118,12 @@

Similarly, whenever the `h3.navigateTo()` method is called (typically within a component), or the URL fragment changes: 1. The *Router* processes the new path and determine which component to render based on the routing configuration. -2. The **teardow()** method of the current Route Component is called (if any). -3. The **setup()** method of the new matching Route Component is called (if any). +2. The **teardow()** method of the current Screen is called (if any). +3. The **setup()** method of the new matching Screen is called (if any). 4. All DOM nodes within the scope of the routing are removed, all components are removed. 6. The **$navigation** event is dispatched. 7. All DOM nodes are removed. -8. The *Route Component* matching the new route and all its child components are rendered. +8. The *Screen* matching the new route and all its child components are rendered. 10. The **$redraw** event is dispatched. And that's it. The whole idea is to make the system extremely *simple* and *predictable* &mdash; which means everything should be very easy to debug, too.
M docs/md/overview.mddocs/md/overview.md

@@ -4,9 +4,9 @@ **H3** is a microframework to build client-side single-page applications (SPAs) in modern JavaScript.

H3 is also: -- **tiny**, less than 4KB minified gzipped. +- **tiny**, less than 4KB minified and gzipped. - **modern**, in the sense that it runs only in modern browsers (latest versions of Chrome, Firefox, Edge & similar). -- **easy** to learn, its API is comprised of only six methods and two properties. +- **easy** to learn, its API is comprised of only seven methods and two properties. ### I'm sold! Where can I get it?

@@ -24,7 +24,7 @@ Here's an example of an extremely minimal SPA created with H3:

```js import h3 from "./h3.js"; -h3.init(() => h3("h1", "Hello, World!")); +h3.init(() => h("h1", "Hello, World!")); ``` This will render a `h1` tag within the document body, containing the text `"Hello, World!"`.
M docs/md/quick-start.mddocs/md/quick-start.md

@@ -43,10 +43,10 @@ // A simple component printing the current date and time

// Pressig the Refresh button causes the application to redraw // And updates the displayed date/dime. const Page = () => { - return h3("main", [ - h3("h1", "Welcome!"), - h3("p", `The current date and time is ${new Date()}`), - h3("button", { + return h("main", [ + h("h1", "Welcome!"), + h("p", `The current date and time is ${new Date()}`), + h("button", { onclick: () => h3.redraw() }, "Refresh") ]);
M docs/md/tutorial.mddocs/md/tutorial.md

@@ -1,12 +1,12 @@

-## Tutorial +## Tutorial As a (meta) explanation of how to use H3, let's have a look at how the [H3 web site](https://h3.js.org) itself was created. The idea was to build a simple web site to display the documentation of the H3 microframework, so it must be able to: -* Provide a simple way to navigate through page. -* Render markdown content (via [marked.js](https://marked.js.org/#/README.md#README.md)) -* Apply syntax highlighting (via [Prism.js](https://prismjs.com/)) +- Provide a simple way to navigate through page. +- Render markdown content (via [marked.js](https://marked.js.org/#/README.md#README.md)) +- Apply syntax highlighting (via [Prism.js](https://prismjs.com/)) As far as look and feel is concerned, I wanted something minimal but functional, so [mini.css](https://minicss.org/) was more than enough.

@@ -14,20 +14,23 @@ The full source of the site is available [here](https://github.com/h3rald/h3/tree/master/docs).

### Create a simple HTML file -Start by creating a simple HTML file. Place a script loading the entry point of your application within the `body` and set its type to `module`. +Start by creating a simple HTML file. Place a script loading the entry point of your application within the `body` and set its type to `module`. This will let you load an ES6 file containing imports to other files... it works in all major browsers, but it doesn't work in IE (but we don't care about that, do we?). ```html -<!doctype html> +<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>H3</title> - <meta name="description" content="A bare-bones client-side web microframework" /> + <meta + name="description" + content="A bare-bones client-side web microframework" + /> <meta name="author" content="Fabio Cevasco" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> - <link rel="shortcut icon" href="favicon.png" type="image/png"> + <link rel="shortcut icon" href="favicon.png" type="image/png" /> <link rel="stylesheet" href="css/mini-default.css" /> <link rel="stylesheet" href="css/prism.css" /> <link rel="stylesheet" href="css/style.css" />

@@ -47,7 +50,7 @@

Start by importing all the JavaScript modules you need: ```js -import h3 from "./h3.js"; +import { h3, h } from "./h3.js"; import marked from "./vendor/marked.js"; import Prism from "./vendor/prism.js"; ```

@@ -66,17 +69,13 @@ };

``` We are going to store the HTML contents of each page in an Object, and we're going to need a simple function to [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) the Markdown file and render it as HTML: - ```js -const pages = {}; - -const fetchPage = async (pages, id, md) => { +const fetchPage = async ({ pages, id, md }) => { if (!pages[id]) { const response = await fetch(md); const text = await response.text(); pages[id] = marked(text); - h3.redraw(); } }; ```

@@ -84,80 +83,96 @@

Basically this function is going to be called when you navigate to each page, and it: 1. fetches the content of the requested file (`md`)) -2. renders the Markdown code into HTML using marked, and stores it in the `pages` object -3. Triggers a redraw of the application +2. renders the Markdown code into HTML using the _marked_ library, and stores it in the `pages` object -We are gonna use our `fetchPage` function inside the main component of our app, `Page`: +We are gonna use our `fetchPage` function inside the `setup` of the main (and only) screen of our app, `Page`: ```js -const Page = () => { - const id = h3.route.path.slice(1); - const ids = Object.keys(labels); - const md = ids.includes(id) ? `md/${id}.md` : `md/overview.md`; - fetchPage(pages, id, md); - return h3("div.page", [ - Header, - h3("div.row", [ - h3("input#drawer-control.drawer", { type: "checkbox" }), - Navigation(id, ids), - Content(pages[id]), - Footer, - ]), - ]); -}; +const Page = h3.screen({ + setup: async (state) => { + state.pages = {}; + state.id = h3.route.path.slice(1); + state.ids = Object.keys(labels); + state.md = state.ids.includes(state.id) + ? `md/${state.id}.md` + : `md/overview.md`; + await fetchPage(state); + }, + display: (state) => { + return h("div.page", [ + Header, + h("div.row", [ + h("input#drawer-control.drawer", { type: "checkbox" }), + Navigation(state.id, state.ids), + Content(state.pages[state.id]), + Footer, + ]), + ]); + }, + teardown: (state) => state, +}); ``` -The main responsibility of this component is to fetch the Markdown content and render the whole page, but note how the rendering different portions of the page are delegated to different components: `Header`, `Navigation`, `Content`, and `Footer`. +Note that this screen has a `setup`, a `display` and a `teardown` method, both taking `state` as parameter. In H3, screens are nothing but stateful components that are used to render the whole page of the application, and are therefore typically redered when navigating to a new route. + +The `state` parameter is nothing but an empty object that can be used to store data that will be accessible to the `setup`, `display` and `teardown` methods, and (typically) will be destroyed when another screen is rendered. + +The `setup` function allows you to perform some operations that should take place _before_ the screen is rendered. In this case, we want to fetch the page contents (if necessary) beforehand to avoid displaying a spinner while the content is being loaded. Note that the `setup` method can be asynchronous, and in this case the `display` method will not be called until all asynchronous operations have been completed (assuming you are `await`ing them). + +The `teardown` function in this case only makes sure that the existing screen state (in particular any loaded markdown page) will be passed on to the next screen during navigation (which, in this case, is still the `Page` screen), so that existing pages will not be fetched again. + +The main responsibility of this screen is to fetch the Markdown content and render the whole page, but note how the rendering different portions of the page are delegated to different components: `Header`, `Navigation`, `Content`, and `Footer`. The `Header` and `Footer` components are very simple: their only job is to render static content: ```js const Header = () => { - return h3("header.row.sticky", [ - h3("a.logo.col-sm-1", { href: "#/" }, [ - h3("img", { alt: "H3", src: "images/h3.svg" }), + return h("header.row.sticky", [ + h("a.logo.col-sm-1", { href: "#/" }, [ + h("img", { alt: "H3", src: "images/h3.svg" }), ]), - h3("div.version.col-sm.col-md", [ - h3("div.version-number", "v0.10.0"), - h3("div.version-label", "“Jittery Jem'Hadar“"), + h("div.version.col-sm.col-md", [ + h("div.version-number", "v0.10.0"), + h("div.version-label", "“Jittery Jem'Hadar“"), ]), - h3("label.drawer-toggle.button.col-sm-last", { for: "drawer-control" }), + h("label.drawer-toggle.button.col-sm-last", { for: "drawer-control" }), ]); }; const Footer = () => { - return h3("footer", [h3("div", "© 2020 Fabio Cevasco")]); + return h("footer", [h("div", "© 2020 Fabio Cevasco")]); }; ``` The `Navigation` component is more interesting, as it takes two parameters: -* The ID of the current page -* The list of page IDs +- The ID of the current page +- The list of page IDs ...and it uses this information to create the site navigation menu dynamically: ```js const Navigation = (id, ids) => { const menu = ids.map((p) => - h3(`a${p === id ? ".active" : ""}`, { href: `#/${p}` }, labels[p]) + h(`a${p === id ? ".active" : ""}`, { href: `#/${p}` }, labels[p]) ); - return h3("nav#navigation.col-md-3", [ - h3("label.drawer-close", { for: "drawer-control" }), + return h("nav#navigation.col-md-3", [ + h("label.drawer-close", { for: "drawer-control" }), ...menu, ]); }; ``` -Finally, the `Content` component optionally takes a string containing the HTML of the page content to render. If no content is provided, it will display a loading spinner, otherwise it will render the content by using the special `$html` attribute that can be used to essentially set the `innerHTML` of an element: +Finally, the `Content` component takes a string containing the HTML of the page content to render using the special `$html` attribute that can be used to essentially set the `innerHTML` property of an element: ```js const Content = (html) => { - const content = html - ? h3("div.content", { $html: html }) - : h3("div.spinner-container", h3("span.spinner")); - return h3("main.col-sm-12.col-md-9", [ - h3("div.card.fluid", h3("div.section", content)), + const content = h("div.content", { $html: html }); + return h("main.col-sm-12.col-md-9", [ + h( + "div.card.fluid", + h("div.section", { $onrender: () => Prism.highlightAll() }, content) + ), ]); }; ```

@@ -170,7 +185,9 @@ In a similar way, other well-known pages can easily be mapped to IDs, but it is also important to handle _unknown_ pages (technically I could even pass an URL to a different site containing a malicious markdown page and have it rendered!), and if a page passed in the URL fragment is not present in the `labels` Object, the Overview page will be rendered instead.

This feature is also handy to automatically load the Overview when no fragment is specified. -### Initialization and post-redraw operations +What is that weird `$onrender` property you ask? Well, that's a H3-specific callback that will be executed whenever the corresponding DOM node is rendered... that's essentially the perfect place to for executing operations that must be perform when the DOM is fully available, like highlighting our code snippets using _Prism_ in this case. + +### Initialization Done? Not quite. We need to initialize the SPA by passing the `Page` component to the `h3.init()` method to trigger the first rendering:

@@ -178,18 +195,12 @@ ```js

h3.init(Page); ``` -And that's it. Noooo wait, what about syntax highlighting? That needs to be applied _after_ the HTML markup is rendered. How can we manage that? - -Easy enough, add a handler to be executed whenever the SPA is redrawn: - -```js -h3.on("$redraw", () => Prism.highlightAll()); -``` +And that's it. Now, keep in mind that this is the _short_ version of initialization using a single component and a single route, but still, that's good enough for our use case. ### Next steps Made it this far? Good. Wanna know more? Have a look at the code of the [todo list example](https://github.com/h3rald/h3/tree/master/docs/example) and try it out [here](https://h3.js.org/example/index.html). -Once you feel more comfortable and you are ready to dive into a more complex application, featuring different routes, route components, forms, confirmation messages, plenty of third-party components etc., have a look at [LitePad](https://github.com/h3rald/litepad). You can see it in action here: [litepad.h3rald.com](https://litepad.h3rald.com/). +Once you feel more comfortable and you are ready to dive into a more complex application, featuring different routes, screens, forms, validation, confirmation messages, plenty of third-party components etc., have a look at [LitePad](https://github.com/h3rald/litepad). You can see it in action here: [litepad.h3rald.com](https://litepad.h3rald.com/). -Note: the LitePad online demo will store all its data in localStorage.+Note: the LitePad online demo will store all its data in localStorage.
M h3.jsh3.js

@@ -633,15 +633,17 @@ if (!this.route) {

throw new Error(`[Router] No route matches '${fragment}'`); } // Old route component teardown + let state = {}; if (oldRoute) { const oldRouteComponent = this.routes[oldRoute.def]; - oldRouteComponent.state = - oldRouteComponent.teardown && - (await oldRouteComponent.teardown(oldRouteComponent.state)); + state = + (oldRouteComponent.teardown && + (await oldRouteComponent.teardown(oldRouteComponent.state))) || + state; } // New route component setup const newRouteComponent = this.routes[this.route.def]; - newRouteComponent.state = {}; + newRouteComponent.state = state; newRouteComponent.setup && (await newRouteComponent.setup(newRouteComponent.state)); // Redrawing...
M h3.js.maph3.js.map

@@ -1,1 +1,1 @@

-{"version":3,"sources":["0"],"names":["checkProperties","obj1","obj2","key","equal","undefined","constructor","toString","String","Number","Boolean","includes","Array","length","i","selectorRegex","PATCH","INSERT","DELETE","$onrenderCallbacks","VNode","[object Object]","args","this","type","props","data","id","$html","$onrender","style","value","children","classList","eventListeners","Error","vnode","processSelector","from","processVNodeObject","selector","isArray","Function","processChildren","processProperties","slice","concat","a","b","attrs","Object","keys","filter","startsWith","forEach","match","classes","split","arg","map","c","document","createTextNode","node","createElement","p","setAttribute","removeAttribute","event","addEventListener","cssText","add","dataset","cnode","render","appendChild","push","innerHTML","newvnode","oldvnode","renderedNode","parentNode","replaceChild","remove","removeEventListener","childMap","mapChildren","resultMap","count","checkmap","redraw","childNodes","splice","insertBefore","removeChild","vtarget","target","newList","oldList","nIdx","op","oIdx","oldNodesFound","Store","events","state","dispatch","cb","Route","path","def","query","parts","params","name","decodeURIComponent","Router","element","routes","store","location","window","route","processPath","async","oldRoute","fragment","newURL","hash","replace","rawQuery","pathParts","routeParts","index","rP","pP","oldRouteComponent","teardown","newRouteComponent","setup","redrawing","firstChild","setRedraw","cbk","scrollTo","encodeURIComponent","join","h","h3","router","init","config","modules","preStart","postStart","/","body","Element","Promise","resolve","then","start","navigateTo","defineProperty","get","on","setRedrawing","screen","display","fn"],"mappings":"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","file":"h3.js"}+{"version":3,"sources":["0"],"names":["checkProperties","obj1","obj2","key","equal","undefined","constructor","toString","String","Number","Boolean","includes","Array","length","i","selectorRegex","PATCH","INSERT","DELETE","$onrenderCallbacks","VNode","[object Object]","args","this","type","props","data","id","$html","$onrender","style","value","children","classList","eventListeners","Error","vnode","processSelector","from","processVNodeObject","selector","isArray","Function","processChildren","processProperties","slice","concat","a","b","attrs","Object","keys","filter","startsWith","forEach","match","classes","split","arg","map","c","document","createTextNode","node","createElement","p","setAttribute","removeAttribute","event","addEventListener","cssText","add","dataset","cnode","render","appendChild","push","innerHTML","newvnode","oldvnode","renderedNode","parentNode","replaceChild","remove","removeEventListener","childMap","mapChildren","resultMap","count","checkmap","redraw","childNodes","splice","insertBefore","removeChild","vtarget","target","newList","oldList","nIdx","op","oIdx","oldNodesFound","Store","events","state","dispatch","cb","Route","path","def","query","parts","params","name","decodeURIComponent","Router","element","routes","store","location","window","route","processPath","async","oldRoute","fragment","newURL","hash","replace","rawQuery","pathParts","routeParts","index","rP","pP","oldRouteComponent","teardown","newRouteComponent","setup","redrawing","firstChild","setRedraw","cbk","scrollTo","encodeURIComponent","join","h","h3","router","init","config","modules","preStart","postStart","/","body","Element","Promise","resolve","then","start","navigateTo","defineProperty","get","on","setRedrawing","screen","display","fn"],"mappings":"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","file":"h3.js"}
M h3.min.jsh3.min.js

@@ -5,5 +5,5 @@ *

* @license MIT * For the full license, see: https://github.com/h3rald/h3/blob/master/LICENSE */ -const checkProperties=(e,t)=>{for(const r in e){if(!(r in t))return!1;if(!equal(e[r],t[r]))return!1}return!0},equal=(e,t)=>{if(null===e&&null===t||void 0===e&&void 0===t)return!0;if(void 0===e&&void 0!==t||void 0!==e&&void 0===t||null===e&&null!==t||null!==e&&null===t)return!1;if(e.constructor!==t.constructor)return!1;if("function"==typeof e&&e.toString()!==t.toString())return!1;if([String,Number,Boolean].includes(e.constructor))return e===t;if(e.constructor===Array){if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(!equal(e[r],t[r]))return!1;return!0}return checkProperties(e,t)},selectorRegex=/^([a-z][a-z0-9:_=-]*)?(#[a-z0-9:_=-]+)?(\.[^ ]+)*$/i,[PATCH,INSERT,DELETE]=[-1,-2,-3];let $onrenderCallbacks=[];class VNode{constructor(...e){if(this.type=void 0,this.props={},this.data={},this.id=void 0,this.$html=void 0,this.$onrender=void 0,this.style=void 0,this.value=void 0,this.children=[],this.classList=[],this.eventListeners={},0===e.length)throw new Error("[VNode] No arguments passed to VNode constructor.");if(1===e.length){let t=e[0];if("string"==typeof t)this.processSelector(t);else{if("function"!=typeof t&&("object"!=typeof t||null===t))throw new Error("[VNode] Invalid first argument passed to VNode constructor.");"#text"===t.type?(this.type="#text",this.value=t.value):this.from(this.processVNodeObject(t))}}else if(2===e.length){let[t,r]=e;if("string"!=typeof t)throw new Error("[VNode] Invalid first argument passed to VNode constructor.");if(this.processSelector(t),"string"==typeof r)return void(this.children=[new VNode({type:"#text",value:r})]);if("function"!=typeof r&&("object"!=typeof r||null===r))throw new Error("[VNode] The second argument of a VNode constructor must be an object, an array or a string.");Array.isArray(r)||r instanceof Function||r instanceof VNode?this.processChildren(r):this.processProperties(r)}else{let[t,r,s]=e;if(e.length>3&&(s=e.slice(2)),s=Array.isArray(s)?s:[s],"string"!=typeof t)throw new Error("[VNode] Invalid first argument passed to VNode constructor.");if(this.processSelector(t),r instanceof Function||r instanceof VNode||"string"==typeof r)s=[r].concat(s);else{if("object"!=typeof r||null===r)throw new Error("[VNode] Invalid second argument passed to VNode constructor.");this.processProperties(r)}this.processChildren(s)}}from(e){this.value=e.value,this.type=e.type,this.id=e.id,this.$html=e.$html,this.$onrender=e.$onrender,this.style=e.style,this.data=e.data,this.value=e.value,this.eventListeners=e.eventListeners,this.children=e.children,this.props=e.props,this.classList=e.classList}equal(e,t){return equal(e,void 0===t?this:t)}processProperties(e){this.id=this.id||e.id,this.$html=e.$html,this.$onrender=e.$onrender,this.style=e.style,this.value=e.value,this.data=e.data||{},this.classList=e.classList&&e.classList.length>0?e.classList:this.classList,this.props=e,Object.keys(e).filter(t=>t.startsWith("on")&&e[t]).forEach(t=>{if("function"!=typeof e[t])throw new Error(`[VNode] Event handler specified for ${t} event is not a function.`);this.eventListeners[t.slice(2)]=e[t],delete this.props[t]}),delete this.props.value,delete this.props.$html,delete this.props.$onrender,delete this.props.id,delete this.props.data,delete this.props.style,delete this.props.classList}processSelector(e){if(!e.match(selectorRegex)||0===e.length)throw new Error("[VNode] Invalid selector: "+e);const[,t,r,s]=e.match(selectorRegex);this.type=t,r&&(this.id=r.slice(1)),this.classList=s&&s.split(".").slice(1)||[]}processVNodeObject(e){if(e instanceof VNode)return e;if(e instanceof Function){let t=e();if("string"==typeof t&&(t=new VNode({type:"#text",value:t})),!(t instanceof VNode))throw new Error("[VNode] Function argument does not return a VNode");return t}throw new Error("[VNode] Invalid first argument provided to VNode constructor.")}processChildren(e){const t=Array.isArray(e)?e:[e];this.children=t.map(e=>{if("string"==typeof e)return new VNode({type:"#text",value:e});if("function"==typeof e||"object"==typeof e&&null!==e)return this.processVNodeObject(e);if(e)throw new Error("[VNode] Specified child is not a VNode: "+e)}).filter(e=>e)}render(){if("#text"===this.type)return document.createTextNode(this.value);const e=document.createElement(this.type);return this.id&&(e.id=this.id),Object.keys(this.props).forEach(t=>{"boolean"==typeof this.props[t]&&(this.props[t]?e.setAttribute(t,""):e.removeAttribute(t)),["string","number"].includes(typeof this.props[t])&&e.setAttribute(t,this.props[t]),e[t]=this.props[t]}),Object.keys(this.eventListeners).forEach(t=>{e.addEventListener(t,this.eventListeners[t])}),this.value&&(["textarea","input"].includes(this.type)?e.value=this.value:e.setAttribute("value",this.value)),this.style&&(e.style.cssText=this.style),this.classList.forEach(t=>{e.classList.add(t)}),Object.keys(this.data).forEach(t=>{e.dataset[t]=this.data[t]}),this.children.forEach(t=>{const r=t.render();e.appendChild(r),t.$onrender&&$onrenderCallbacks.push(()=>t.$onrender(r))}),this.$html&&(e.innerHTML=this.$html),e}redraw(e){let{node:t,vnode:r}=e;const s=r,o=this;if(o.constructor!==s.constructor||o.type!==s.type||o.type===s.type&&"#text"===o.type&&o!==s){const e=s.render();return t.parentNode.replaceChild(e,t),s.$onrender&&s.$onrender(e),void o.from(s)}o.id!==s.id&&(t.id=s.id||"",o.id=s.id),o.value!==s.value&&(o.value=s.value,["textarea","input"].includes(o.type)?t.value=s.value||"":t.setAttribute("value",s.value||"")),equal(o.classList,s.classList)||(o.classList.forEach(e=>{s.classList.includes(e)||t.classList.remove(e)}),s.classList.forEach(e=>{o.classList.includes(e)||t.classList.add(e)}),o.classList=s.classList),o.style!==s.style&&(t.style.cssText=s.style||"",o.style=s.style),equal(o.data,s.data)||(Object.keys(o.data).forEach(e=>{s.data[e]?s.data[e]!==o.data[e]&&(t.dataset[e]=s.data[e]):delete t.dataset[e]}),Object.keys(s.data).forEach(e=>{o.data[e]||(t.dataset[e]=s.data[e])}),o.data=s.data),equal(o.props,s.props)||(Object.keys(o.props).forEach(e=>{t[e]=s.props[e],"boolean"==typeof s.props[e]?(o.props[e]=s.props[e],s.props[e]?t.setAttribute(e,""):t.removeAttribute(e)):s.props[e]?s.props[e]&&s.props[e]!==o.props[e]&&(o.props[e]=s.props[e],["string","number"].includes(typeof s.props[e])&&t.setAttribute(e,s.props[e])):(delete o.props[e],t.removeAttribute(e))}),Object.keys(s.props).forEach(e=>{!o.props[e]&&s.props[e]&&(o.props[e]=s.props[e],t.setAttribute(e,s.props[e]))})),equal(o.eventListeners,s.eventListeners)||(Object.keys(o.eventListeners).forEach(e=>{s.eventListeners[e]?equal(s.eventListeners[e],o.eventListeners[e])||(t.removeEventListener(e,o.eventListeners[e]),t.addEventListener(e,s.eventListeners[e])):t.removeEventListener(e,o.eventListeners[e])}),Object.keys(s.eventListeners).forEach(e=>{o.eventListeners[e]||t.addEventListener(e,s.eventListeners[e])}),o.eventListeners=s.eventListeners);let i=mapChildren(o,s),n=[...Array(s.children.length).keys()];for(;!equal(i,n);){let e=-1;e:for(const r of i)if(e++,r!==e)switch(r){case PATCH:o.children[e].redraw({node:t.childNodes[e],vnode:s.children[e]});break e;case INSERT:{o.children.splice(e,0,s.children[e]);const r=s.children[e].render();t.insertBefore(r,t.childNodes[e]),s.children[e].$onrender&&s.children[e].$onrender(r);break e}case DELETE:o.children.splice(e,1),t.removeChild(t.childNodes[e]);break e;default:{const s=o.children.splice(r,1)[0];o.children.splice(e,0,s);const i=t.removeChild(t.childNodes[r]);t.insertBefore(i,t.childNodes[e]);break e}}i=mapChildren(o,s),n=[...Array(s.children.length).keys()]}equal(o.$onrender,s.$onrender)||(o.$onrender=s.$onrender),o.$html!==s.$html&&(t.innerHTML=s.$html,o.$html=s.$html,o.$onrender&&o.$onrender(t))}}const mapChildren=(e,t)=>{const r=t.children,s=e.children;let o=[];for(let e=0;e<r.length;e++){let t=PATCH;for(let i=0;i<s.length;i++)if(equal(r[e],s[i])&&!o.includes(i)){t=i;break}t<0&&r.length>=s.length&&o.length>=s.length&&(t=INSERT),o.push(t)}const i=o.filter(e=>e>=0);return s.length>r.length?[...Array(s.length-r.length).keys()].forEach(()=>o.push(DELETE)):i.length===s.length&&(o=o.map(e=>e<0?INSERT:e)),o};class Store{constructor(){this.events={},this.state={}}dispatch(e,t){if("$log"!==e&&this.dispatch("$log",{event:e,data:t}),this.events[e]){this.events[e].forEach(e=>{this.state={...this.state,...e(this.state,t)}})}}on(e,t){return(this.events[e]||(this.events[e]=[])).push(t),()=>{this.events[e]=this.events[e].filter(e=>e!==t)}}}class Route{constructor({path:e,def:t,query:r,parts:s}){if(this.path=e,this.def=t,this.query=r,this.parts=s,this.params={},this.query){this.query.split("&").forEach(e=>{const[t,r]=e.split("=");this.params[decodeURIComponent(t)]=decodeURIComponent(r)})}}}class Router{constructor({element:e,routes:t,store:r,location:s}){if(this.element=e,this.redraw=null,this.store=r,this.location=s||window.location,!t||0===Object.keys(t).length)throw new Error("[Router] No routes defined.");Object.keys(t);this.routes=t}setRedraw(e,t){this.redraw=()=>{e.redraw({node:this.element.childNodes[0],vnode:this.routes[this.route.def](t)}),this.store.dispatch("$redraw")}}async start(){const e=async e=>{const t=this.route,r=e&&e.newURL&&e.newURL.match(/(#.+)$/)&&e.newURL.match(/(#.+)$/)[1]||this.location.hash,s=r.replace(/\?.+$/,"").slice(1),o=r.match(/\?(.+)$/),i=o&&o[1]?o[1]:"",n=s.split("/").slice(1);let a={};for(let e of Object.keys(this.routes)){let t=e.split("/").slice(1),r=!0,o=0;for(a={};r&&t[o];){const e=t[o],s=n[o];e.startsWith(":")&&s?a[e.slice(1)]=s:r=e===s,o++}if(r){this.route=new Route({query:i,path:s,def:e,parts:a});break}}if(!this.route)throw new Error(`[Router] No route matches '${r}'`);if(t){const e=this.routes[t.def];e.state=e.teardown&&await e.teardown(e.state)}const l=this.routes[this.route.def];for(l.state={},l.setup&&await l.setup(l.state),redrawing=!0,this.store.dispatch("$navigation",this.route);this.element.firstChild;)this.element.removeChild(this.element.firstChild);const h=l(l.state),d=h.render();this.element.appendChild(d),this.setRedraw(h,l.state),redrawing=!1,h.$onrender&&h.$onrender(d),$onrenderCallbacks.forEach(e=>e()),$onrenderCallbacks=[],window.scrollTo(0,0),this.store.dispatch("$redraw")};window.addEventListener("hashchange",e),await e()}navigateTo(e,t){let r=Object.keys(t||{}).map(e=>`${encodeURIComponent(e)}=${encodeURIComponent(t[e])}`).join("&");r=r?"?"+r:"",this.location.hash=`#${e}${r}`}}export const h=(...e)=>new VNode(...e);export const h3={};let store=null,router=null,redrawing=!1;h3.init=e=>{let{element:t,routes:r,modules:s,preStart:o,postStart:i,location:n}=e;if(!r){if("function"!=typeof e)throw new Error("[h3.init] The specified argument is not a valid configuration object or component function");r={"/":e}}if(t=t||document.body,!(t&&t instanceof Element))throw new Error("[h3.init] Invalid element specified.");return store=new Store,(s||[]).forEach(e=>{e(store)}),store.dispatch("$init"),router=new Router({element:t,routes:r,store:store,location:n}),Promise.resolve(o&&o()).then(()=>router.start()).then(()=>i&&i())},h3.navigateTo=(e,t)=>{if(!router)throw new Error("[h3.navigateTo] No application initialized, unable to navigate.");return router.navigateTo(e,t)},Object.defineProperty(h3,"route",{get:()=>{if(!router)throw new Error("[h3.route] No application initialized, unable to retrieve current route.");return router.route}}),Object.defineProperty(h3,"state",{get:()=>{if(!store)throw new Error("[h3.state] No application initialized, unable to retrieve current state.");return store.state}}),h3.on=(e,t)=>{if(!store)throw new Error("[h3.on] No application initialized, unable to listen to events.");return store.on(e,t)},h3.dispatch=(e,t)=>{if(!store)throw new Error("[h3.dispatch] No application initialized, unable to dispatch events.");return store.dispatch(e,t)},h3.redraw=e=>{if(!router||!router.redraw)throw new Error("[h3.redraw] No application initialized, unable to redraw.");redrawing||(redrawing=!0,router.redraw(),redrawing=e||!1)},h3.screen=({setup:e,display:t,teardown:r})=>{if(!t||"function"!=typeof t)throw new Error("[h3.screen] No display property specified.");if(e&&"function"!=typeof e)throw new Error("[h3.screen] setup property is not a function.");if(r&&"function"!=typeof r)throw new Error("[h3.screen] teardown property is not a function.");const s=t;return e&&(s.setup=e),r&&(s.teardown=r),s};export default h3; +const checkProperties=(e,t)=>{for(const r in e){if(!(r in t))return!1;if(!equal(e[r],t[r]))return!1}return!0},equal=(e,t)=>{if(null===e&&null===t||void 0===e&&void 0===t)return!0;if(void 0===e&&void 0!==t||void 0!==e&&void 0===t||null===e&&null!==t||null!==e&&null===t)return!1;if(e.constructor!==t.constructor)return!1;if("function"==typeof e&&e.toString()!==t.toString())return!1;if([String,Number,Boolean].includes(e.constructor))return e===t;if(e.constructor===Array){if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(!equal(e[r],t[r]))return!1;return!0}return checkProperties(e,t)},selectorRegex=/^([a-z][a-z0-9:_=-]*)?(#[a-z0-9:_=-]+)?(\.[^ ]+)*$/i,[PATCH,INSERT,DELETE]=[-1,-2,-3];let $onrenderCallbacks=[];class VNode{constructor(...e){if(this.type=void 0,this.props={},this.data={},this.id=void 0,this.$html=void 0,this.$onrender=void 0,this.style=void 0,this.value=void 0,this.children=[],this.classList=[],this.eventListeners={},0===e.length)throw new Error("[VNode] No arguments passed to VNode constructor.");if(1===e.length){let t=e[0];if("string"==typeof t)this.processSelector(t);else{if("function"!=typeof t&&("object"!=typeof t||null===t))throw new Error("[VNode] Invalid first argument passed to VNode constructor.");"#text"===t.type?(this.type="#text",this.value=t.value):this.from(this.processVNodeObject(t))}}else if(2===e.length){let[t,r]=e;if("string"!=typeof t)throw new Error("[VNode] Invalid first argument passed to VNode constructor.");if(this.processSelector(t),"string"==typeof r)return void(this.children=[new VNode({type:"#text",value:r})]);if("function"!=typeof r&&("object"!=typeof r||null===r))throw new Error("[VNode] The second argument of a VNode constructor must be an object, an array or a string.");Array.isArray(r)||r instanceof Function||r instanceof VNode?this.processChildren(r):this.processProperties(r)}else{let[t,r,s]=e;if(e.length>3&&(s=e.slice(2)),s=Array.isArray(s)?s:[s],"string"!=typeof t)throw new Error("[VNode] Invalid first argument passed to VNode constructor.");if(this.processSelector(t),r instanceof Function||r instanceof VNode||"string"==typeof r)s=[r].concat(s);else{if("object"!=typeof r||null===r)throw new Error("[VNode] Invalid second argument passed to VNode constructor.");this.processProperties(r)}this.processChildren(s)}}from(e){this.value=e.value,this.type=e.type,this.id=e.id,this.$html=e.$html,this.$onrender=e.$onrender,this.style=e.style,this.data=e.data,this.value=e.value,this.eventListeners=e.eventListeners,this.children=e.children,this.props=e.props,this.classList=e.classList}equal(e,t){return equal(e,void 0===t?this:t)}processProperties(e){this.id=this.id||e.id,this.$html=e.$html,this.$onrender=e.$onrender,this.style=e.style,this.value=e.value,this.data=e.data||{},this.classList=e.classList&&e.classList.length>0?e.classList:this.classList,this.props=e,Object.keys(e).filter(t=>t.startsWith("on")&&e[t]).forEach(t=>{if("function"!=typeof e[t])throw new Error(`[VNode] Event handler specified for ${t} event is not a function.`);this.eventListeners[t.slice(2)]=e[t],delete this.props[t]}),delete this.props.value,delete this.props.$html,delete this.props.$onrender,delete this.props.id,delete this.props.data,delete this.props.style,delete this.props.classList}processSelector(e){if(!e.match(selectorRegex)||0===e.length)throw new Error("[VNode] Invalid selector: "+e);const[,t,r,s]=e.match(selectorRegex);this.type=t,r&&(this.id=r.slice(1)),this.classList=s&&s.split(".").slice(1)||[]}processVNodeObject(e){if(e instanceof VNode)return e;if(e instanceof Function){let t=e();if("string"==typeof t&&(t=new VNode({type:"#text",value:t})),!(t instanceof VNode))throw new Error("[VNode] Function argument does not return a VNode");return t}throw new Error("[VNode] Invalid first argument provided to VNode constructor.")}processChildren(e){const t=Array.isArray(e)?e:[e];this.children=t.map(e=>{if("string"==typeof e)return new VNode({type:"#text",value:e});if("function"==typeof e||"object"==typeof e&&null!==e)return this.processVNodeObject(e);if(e)throw new Error("[VNode] Specified child is not a VNode: "+e)}).filter(e=>e)}render(){if("#text"===this.type)return document.createTextNode(this.value);const e=document.createElement(this.type);return this.id&&(e.id=this.id),Object.keys(this.props).forEach(t=>{"boolean"==typeof this.props[t]&&(this.props[t]?e.setAttribute(t,""):e.removeAttribute(t)),["string","number"].includes(typeof this.props[t])&&e.setAttribute(t,this.props[t]),e[t]=this.props[t]}),Object.keys(this.eventListeners).forEach(t=>{e.addEventListener(t,this.eventListeners[t])}),this.value&&(["textarea","input"].includes(this.type)?e.value=this.value:e.setAttribute("value",this.value)),this.style&&(e.style.cssText=this.style),this.classList.forEach(t=>{e.classList.add(t)}),Object.keys(this.data).forEach(t=>{e.dataset[t]=this.data[t]}),this.children.forEach(t=>{const r=t.render();e.appendChild(r),t.$onrender&&$onrenderCallbacks.push(()=>t.$onrender(r))}),this.$html&&(e.innerHTML=this.$html),e}redraw(e){let{node:t,vnode:r}=e;const s=r,o=this;if(o.constructor!==s.constructor||o.type!==s.type||o.type===s.type&&"#text"===o.type&&o!==s){const e=s.render();return t.parentNode.replaceChild(e,t),s.$onrender&&s.$onrender(e),void o.from(s)}o.id!==s.id&&(t.id=s.id||"",o.id=s.id),o.value!==s.value&&(o.value=s.value,["textarea","input"].includes(o.type)?t.value=s.value||"":t.setAttribute("value",s.value||"")),equal(o.classList,s.classList)||(o.classList.forEach(e=>{s.classList.includes(e)||t.classList.remove(e)}),s.classList.forEach(e=>{o.classList.includes(e)||t.classList.add(e)}),o.classList=s.classList),o.style!==s.style&&(t.style.cssText=s.style||"",o.style=s.style),equal(o.data,s.data)||(Object.keys(o.data).forEach(e=>{s.data[e]?s.data[e]!==o.data[e]&&(t.dataset[e]=s.data[e]):delete t.dataset[e]}),Object.keys(s.data).forEach(e=>{o.data[e]||(t.dataset[e]=s.data[e])}),o.data=s.data),equal(o.props,s.props)||(Object.keys(o.props).forEach(e=>{t[e]=s.props[e],"boolean"==typeof s.props[e]?(o.props[e]=s.props[e],s.props[e]?t.setAttribute(e,""):t.removeAttribute(e)):s.props[e]?s.props[e]&&s.props[e]!==o.props[e]&&(o.props[e]=s.props[e],["string","number"].includes(typeof s.props[e])&&t.setAttribute(e,s.props[e])):(delete o.props[e],t.removeAttribute(e))}),Object.keys(s.props).forEach(e=>{!o.props[e]&&s.props[e]&&(o.props[e]=s.props[e],t.setAttribute(e,s.props[e]))})),equal(o.eventListeners,s.eventListeners)||(Object.keys(o.eventListeners).forEach(e=>{s.eventListeners[e]?equal(s.eventListeners[e],o.eventListeners[e])||(t.removeEventListener(e,o.eventListeners[e]),t.addEventListener(e,s.eventListeners[e])):t.removeEventListener(e,o.eventListeners[e])}),Object.keys(s.eventListeners).forEach(e=>{o.eventListeners[e]||t.addEventListener(e,s.eventListeners[e])}),o.eventListeners=s.eventListeners);let i=mapChildren(o,s),n=[...Array(s.children.length).keys()];for(;!equal(i,n);){let e=-1;e:for(const r of i)if(e++,r!==e)switch(r){case PATCH:o.children[e].redraw({node:t.childNodes[e],vnode:s.children[e]});break e;case INSERT:{o.children.splice(e,0,s.children[e]);const r=s.children[e].render();t.insertBefore(r,t.childNodes[e]),s.children[e].$onrender&&s.children[e].$onrender(r);break e}case DELETE:o.children.splice(e,1),t.removeChild(t.childNodes[e]);break e;default:{const s=o.children.splice(r,1)[0];o.children.splice(e,0,s);const i=t.removeChild(t.childNodes[r]);t.insertBefore(i,t.childNodes[e]);break e}}i=mapChildren(o,s),n=[...Array(s.children.length).keys()]}equal(o.$onrender,s.$onrender)||(o.$onrender=s.$onrender),o.$html!==s.$html&&(t.innerHTML=s.$html,o.$html=s.$html,o.$onrender&&o.$onrender(t))}}const mapChildren=(e,t)=>{const r=t.children,s=e.children;let o=[];for(let e=0;e<r.length;e++){let t=PATCH;for(let i=0;i<s.length;i++)if(equal(r[e],s[i])&&!o.includes(i)){t=i;break}t<0&&r.length>=s.length&&o.length>=s.length&&(t=INSERT),o.push(t)}const i=o.filter(e=>e>=0);return s.length>r.length?[...Array(s.length-r.length).keys()].forEach(()=>o.push(DELETE)):i.length===s.length&&(o=o.map(e=>e<0?INSERT:e)),o};class Store{constructor(){this.events={},this.state={}}dispatch(e,t){if("$log"!==e&&this.dispatch("$log",{event:e,data:t}),this.events[e]){this.events[e].forEach(e=>{this.state={...this.state,...e(this.state,t)}})}}on(e,t){return(this.events[e]||(this.events[e]=[])).push(t),()=>{this.events[e]=this.events[e].filter(e=>e!==t)}}}class Route{constructor({path:e,def:t,query:r,parts:s}){if(this.path=e,this.def=t,this.query=r,this.parts=s,this.params={},this.query){this.query.split("&").forEach(e=>{const[t,r]=e.split("=");this.params[decodeURIComponent(t)]=decodeURIComponent(r)})}}}class Router{constructor({element:e,routes:t,store:r,location:s}){if(this.element=e,this.redraw=null,this.store=r,this.location=s||window.location,!t||0===Object.keys(t).length)throw new Error("[Router] No routes defined.");Object.keys(t);this.routes=t}setRedraw(e,t){this.redraw=()=>{e.redraw({node:this.element.childNodes[0],vnode:this.routes[this.route.def](t)}),this.store.dispatch("$redraw")}}async start(){const e=async e=>{const t=this.route,r=e&&e.newURL&&e.newURL.match(/(#.+)$/)&&e.newURL.match(/(#.+)$/)[1]||this.location.hash,s=r.replace(/\?.+$/,"").slice(1),o=r.match(/\?(.+)$/),i=o&&o[1]?o[1]:"",n=s.split("/").slice(1);let a={};for(let e of Object.keys(this.routes)){let t=e.split("/").slice(1),r=!0,o=0;for(a={};r&&t[o];){const e=t[o],s=n[o];e.startsWith(":")&&s?a[e.slice(1)]=s:r=e===s,o++}if(r){this.route=new Route({query:i,path:s,def:e,parts:a});break}}if(!this.route)throw new Error(`[Router] No route matches '${r}'`);let l={};if(t){const e=this.routes[t.def];l=e.teardown&&await e.teardown(e.state)||l}const h=this.routes[this.route.def];for(h.state=l,h.setup&&await h.setup(h.state),redrawing=!0,this.store.dispatch("$navigation",this.route);this.element.firstChild;)this.element.removeChild(this.element.firstChild);const d=h(h.state),c=d.render();this.element.appendChild(c),this.setRedraw(d,h.state),redrawing=!1,d.$onrender&&d.$onrender(c),$onrenderCallbacks.forEach(e=>e()),$onrenderCallbacks=[],window.scrollTo(0,0),this.store.dispatch("$redraw")};window.addEventListener("hashchange",e),await e()}navigateTo(e,t){let r=Object.keys(t||{}).map(e=>`${encodeURIComponent(e)}=${encodeURIComponent(t[e])}`).join("&");r=r?"?"+r:"",this.location.hash=`#${e}${r}`}}export const h=(...e)=>new VNode(...e);export const h3={};let store=null,router=null,redrawing=!1;h3.init=e=>{let{element:t,routes:r,modules:s,preStart:o,postStart:i,location:n}=e;if(!r){if("function"!=typeof e)throw new Error("[h3.init] The specified argument is not a valid configuration object or component function");r={"/":e}}if(t=t||document.body,!(t&&t instanceof Element))throw new Error("[h3.init] Invalid element specified.");return store=new Store,(s||[]).forEach(e=>{e(store)}),store.dispatch("$init"),router=new Router({element:t,routes:r,store:store,location:n}),Promise.resolve(o&&o()).then(()=>router.start()).then(()=>i&&i())},h3.navigateTo=(e,t)=>{if(!router)throw new Error("[h3.navigateTo] No application initialized, unable to navigate.");return router.navigateTo(e,t)},Object.defineProperty(h3,"route",{get:()=>{if(!router)throw new Error("[h3.route] No application initialized, unable to retrieve current route.");return router.route}}),Object.defineProperty(h3,"state",{get:()=>{if(!store)throw new Error("[h3.state] No application initialized, unable to retrieve current state.");return store.state}}),h3.on=(e,t)=>{if(!store)throw new Error("[h3.on] No application initialized, unable to listen to events.");return store.on(e,t)},h3.dispatch=(e,t)=>{if(!store)throw new Error("[h3.dispatch] No application initialized, unable to dispatch events.");return store.dispatch(e,t)},h3.redraw=e=>{if(!router||!router.redraw)throw new Error("[h3.redraw] No application initialized, unable to redraw.");redrawing||(redrawing=!0,router.redraw(),redrawing=e||!1)},h3.screen=({setup:e,display:t,teardown:r})=>{if(!t||"function"!=typeof t)throw new Error("[h3.screen] No display property specified.");if(e&&"function"!=typeof e)throw new Error("[h3.screen] setup property is not a function.");if(r&&"function"!=typeof r)throw new Error("[h3.screen] teardown property is not a function.");const s=t;return e&&(s.setup=e),r&&(s.teardown=r),s};export default h3; //# sourceMappingURL=h3.js.map
M package.jsonpackage.json

@@ -8,10 +8,10 @@ "scripts": {

"test": "jest", "coverage": "jest --coverage ", "coveralls": "npm run coverage && cat ./coverage/lcov.info | coveralls", + "prebuild": "node scripts/release.js", "copy": "cp h3.js docs/js/h3.js && cp h3.js docs/example/assets/js/h3.js", - "release": "node scripts/release.js", "guide": "hastyscribe docs/H3_DeveloperGuide.md", - "regenerate": "npm run release && npm run copy && npm run guide" + "build": "npm run copy && npm run guide" }, "repository": { "type": "git",