Jekyll2022-01-08T17:01:47+01:00http://localhost:4000/Dion SnoeijenDeveloperZoom to mouse2020-02-26T13:14:14+01:002020-02-26T13:14:14+01:00http://localhost:4000/development/css/javascript/2020/02/26/zoomtomouse<p>Zoom towards the mouse position. Easy enough, right? This proved to be more difficult than I thought.</p>
<p>I will simply go straight to the code and my explanation on youtube.</p>
<p class="codepen" data-height="351" data-theme-id="light" data-default-tab="js,result" data-user="octopus11" data-slug-hash="ZEpGeqq" style="height: 351px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="zoomtomousehow??">
<span>See the Pen <a href="https://codepen.io/octopus11/pen/ZEpGeqq">
zoomtomousehow??</a> by Dion Snoeijen (<a href="https://codepen.io/octopus11">@octopus11</a>)
on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>DionZooming towards the mouse position in html, css and javascript.Css and JavaScript based grid view2020-02-26T13:14:14+01:002020-02-26T13:14:14+01:00http://localhost:4000/development/css/javascript/2020/02/26/grid-with-css-and-javascript<p>Let me share some code that may be of use to somebody. For my application I need a grid. Not the kind for responsive websites but like a grid intended for visual guidelines. Much like a notebook.</p>
<p><img src="https://i.ebayimg.com/images/i/400910923911-0-1/s-l1000.jpg" alt="Writing block" width="300" /></p>
<p>I have some requirements that I want to support.</p>
<ul>
<li>Zoom</li>
<li>Panning</li>
<li>It must be infinite. So no max height or width.</li>
<li>No background images</li>
</ul>
<p>So, I came up with this.</p>
<p class="codepen" data-height="265" data-theme-id="light" data-default-tab="js,result" data-user="octopus11" data-slug-hash="yLNMzwg" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Infinite grid">
<span>See the Pen <a href="https://codepen.io/octopus11/pen/yLNMzwg">
Infinite grid</a> by Dion Snoeijen (<a href="https://codepen.io/octopus11">@octopus11</a>)
on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async="" src="https://static.codepen.io/assets/embed/ei.js"></script>
<p>It’s best viewed from within codepen because the embed widget is ignoring the scrollwheel and mouse events. Or at least, it works wonky like this.</p>
<p>You can use the scrollwheel, or whatever that you are using to scroll to zoom in and out. And if you hold down space, you can click and drag the grid in any direction.</p>
<p>The solution is relatively simple. It’s using a css gradient to draw the grid. Then we update the css on events and then we set the gradient css on the grid element.</p>
<p>So, first let’s create the html and css.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"grid"</span><span class="nt">></span>
<span class="nt"></div></span>
</code></pre></div></div>
<p>We really don’t need anything fancy, let’s just focus on the grid and give it minimal styling.</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">body</span><span class="o">,</span> <span class="nt">html</span> <span class="p">{</span>
<span class="nl">padding</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.grid</span> <span class="p">{</span>
<span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span>
<span class="nl">left</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">top</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">right</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">bottom</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.panning</span> <span class="p">{</span>
<span class="nl">cursor</span><span class="p">:</span> <span class="n">grab</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>I start with initializing some variables in JavaScript. Their use will be clear soon.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">grid</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">.grid</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">translate</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">scale</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="na">translateX</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="na">translateY</span><span class="p">:</span> <span class="mi">0</span>
<span class="p">};</span>
<span class="kd">let</span> <span class="nx">panning</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">pinnedMousePosition</span> <span class="o">=</span> <span class="p">{</span> <span class="na">x</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="na">y</span><span class="p">:</span> <span class="mi">0</span> <span class="p">};</span>
<span class="kd">const</span> <span class="nx">pinnedGridPosition</span> <span class="o">=</span> <span class="p">{</span> <span class="na">x</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="na">y</span><span class="p">:</span> <span class="mi">0</span> <span class="p">};</span>
</code></pre></div></div>
<p>Now I will introduce a <code class="highlighter-rouge">doGrid()</code> method so we can initialize the grid system.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">doGrid</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{};</span>
<span class="nx">doGrid</span><span class="p">();</span>
</code></pre></div></div>
<p>Right below that the method is called. The doGrid method will contain all event listeners.</p>
<h4 id="events-for-panning">Events for panning</h4>
<p><code class="highlighter-rouge">keydown</code> is needed to register if the spacebar is being pressed. With that info we can update <code class="highlighter-rouge">panning</code> with +1.</p>
<p><code class="highlighter-rouge">keyup</code> will cancel the spacebar press to set <code class="highlighter-rouge">panning</code> back with 1.</p>
<p><code class="highlighter-rouge">mousedown</code> will store all current parameters and will add +1 to panning.</p>
<p><code class="highlighter-rouge">mouseup</code> will take 1 of <code class="highlighter-rouge">panning</code>.</p>
<p><code class="highlighter-rouge">mousemove</code> to handle the changes made by <code class="highlighter-rouge">panning</code>.</p>
<h4 id="event-for-zooming">Event for zooming</h4>
<p><code class="highlighter-rouge">scrollwheel</code> It’s for registering a zoom in / out action.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nx">grid</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">wheel</span><span class="dl">'</span><span class="p">,</span> <span class="nx">event</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">translate</span><span class="p">.</span><span class="nx">scale</span> <span class="o">+=</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">deltaY</span> <span class="o">*</span> <span class="p">(</span><span class="nx">translate</span><span class="p">.</span><span class="nx">scale</span> <span class="o">/</span> <span class="mi">5000</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">translate</span><span class="p">.</span><span class="nx">scale</span> <span class="o">></span> <span class="mi">3</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">translate</span><span class="p">.</span><span class="nx">scale</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">translate</span><span class="p">.</span><span class="nx">scale</span> <span class="o"><</span> <span class="mf">0.4</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">translate</span><span class="p">.</span><span class="nx">scale</span> <span class="o">=</span> <span class="mf">0.4</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">update</span><span class="p">();</span>
<span class="p">});</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">keydown</span><span class="dl">'</span><span class="p">,</span> <span class="nx">event</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">key</span> <span class="o">===</span> <span class="dl">'</span><span class="s1"> </span><span class="dl">'</span> <span class="o">&&</span> <span class="o">!</span><span class="nx">panning</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">event</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
<span class="nx">grid</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">panning</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">panning</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">mousedown</span><span class="dl">'</span><span class="p">,</span> <span class="nx">event</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">pinnedGridPosition</span><span class="p">.</span><span class="nx">x</span> <span class="o">=</span> <span class="nx">translate</span><span class="p">.</span><span class="nx">translateX</span><span class="p">;</span>
<span class="nx">pinnedGridPosition</span><span class="p">.</span><span class="nx">y</span> <span class="o">=</span> <span class="nx">translate</span><span class="p">.</span><span class="nx">translateY</span><span class="p">;</span>
<span class="nx">pinnedMousePosition</span><span class="p">.</span><span class="nx">x</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">clientX</span><span class="p">;</span>
<span class="nx">pinnedMousePosition</span><span class="p">.</span><span class="nx">y</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">clientY</span><span class="p">;</span>
<span class="nx">panning</span><span class="o">++</span><span class="p">;</span>
<span class="p">});</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">mouseup</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">panning</span><span class="o">--</span><span class="p">;</span>
<span class="p">});</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">keyup</span><span class="dl">'</span><span class="p">,</span> <span class="nx">event</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">panning</span><span class="o">--</span><span class="p">;</span>
<span class="nx">grid</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">remove</span><span class="p">(</span><span class="dl">'</span><span class="s1">panning</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
<span class="nx">grid</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">mousemove</span><span class="dl">'</span><span class="p">,</span> <span class="nx">event</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">panning</span> <span class="o">===</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">diffX</span> <span class="o">=</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">clientX</span> <span class="o">-</span> <span class="nx">pinnedMousePosition</span><span class="p">.</span><span class="nx">x</span><span class="p">)</span> <span class="o">/</span> <span class="nx">translate</span><span class="p">.</span><span class="nx">scale</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">diffY</span> <span class="o">=</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">clientY</span> <span class="o">-</span> <span class="nx">pinnedMousePosition</span><span class="p">.</span><span class="nx">y</span><span class="p">)</span> <span class="o">/</span> <span class="nx">translate</span><span class="p">.</span><span class="nx">scale</span><span class="p">;</span>
<span class="nx">translate</span><span class="p">.</span><span class="nx">translateX</span> <span class="o">=</span> <span class="nx">pinnedGridPosition</span><span class="p">.</span><span class="nx">x</span> <span class="o">+</span> <span class="nx">diffX</span><span class="p">;</span>
<span class="nx">translate</span><span class="p">.</span><span class="nx">translateY</span> <span class="o">=</span> <span class="nx">pinnedGridPosition</span><span class="p">.</span><span class="nx">y</span> <span class="o">+</span> <span class="nx">diffY</span><span class="p">;</span>
<span class="nx">update</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">});</span>
</code></pre></div></div>
<p>It’s all about updating the <code class="highlighter-rouge">translate</code> object. Once updated, we need to call the method that’s doing all the real work. The <code class="highlighter-rouge">update</code> method which is also part of <code class="highlighter-rouge">doGrid</code>.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">const</span> <span class="nx">update</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">transform</span> <span class="o">=</span> <span class="dl">''</span><span class="p">;</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">translate</span><span class="p">).</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">property</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">transform</span> <span class="o">+=</span> <span class="nx">property</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">(</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">translate</span><span class="p">[</span><span class="nx">property</span><span class="p">]</span> <span class="o">+</span> <span class="p">(</span><span class="nx">property</span> <span class="o">!==</span> <span class="dl">'</span><span class="s1">scale</span><span class="dl">'</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">px</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">''</span><span class="p">)</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">) </span><span class="dl">'</span><span class="p">;</span>
<span class="p">});</span>
<span class="nx">grid</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">transform</span> <span class="o">=</span> <span class="nx">transform</span><span class="p">.</span><span class="nx">trim</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">vars</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">a</span><span class="p">:</span> <span class="mi">9</span> <span class="o">*</span> <span class="nx">translate</span><span class="p">.</span><span class="nx">scale</span><span class="p">,</span>
<span class="na">b</span><span class="p">:</span> <span class="mi">10</span> <span class="o">*</span> <span class="nx">translate</span><span class="p">.</span><span class="nx">scale</span><span class="p">,</span>
<span class="na">c</span><span class="p">:</span> <span class="o">-</span><span class="p">.</span><span class="mi">5</span> <span class="o">*</span> <span class="nx">translate</span><span class="p">.</span><span class="nx">scale</span><span class="p">,</span>
<span class="na">d</span><span class="p">:</span> <span class="nx">translate</span><span class="p">.</span><span class="nx">scale</span><span class="p">,</span>
<span class="na">e</span><span class="p">:</span> <span class="mf">99.5</span> <span class="o">*</span> <span class="nx">translate</span><span class="p">.</span><span class="nx">scale</span><span class="p">,</span>
<span class="na">f</span><span class="p">:</span> <span class="mi">100</span> <span class="o">*</span> <span class="nx">translate</span><span class="p">.</span><span class="nx">scale</span>
<span class="p">};</span>
<span class="kd">const</span> <span class="nx">backgroundPosition</span> <span class="o">=</span> <span class="nx">grid</span><span class="p">.</span><span class="nx">getBoundingClientRect</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">colors</span> <span class="o">=</span> <span class="p">{</span> <span class="na">a</span><span class="p">:</span> <span class="mi">57</span><span class="p">,</span> <span class="na">b</span><span class="p">:</span> <span class="mi">75</span><span class="p">,</span> <span class="na">c</span><span class="p">:</span> <span class="mi">80</span> <span class="p">};</span>
<span class="kd">const</span> <span class="nx">background</span> <span class="o">=</span> <span class="s2">`
repeating-linear-gradient(
0deg,
transparent 0,
transparent </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">a</span><span class="p">}</span><span class="s2">px,
rgb(</span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">b</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">b</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">b</span><span class="p">}</span><span class="s2">) </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">a</span><span class="p">}</span><span class="s2">px,
rgb(</span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">b</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">b</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">b</span><span class="p">}</span><span class="s2">) </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">b</span><span class="p">}</span><span class="s2">px
)
</span><span class="p">${</span><span class="nx">backgroundPosition</span><span class="p">.</span><span class="nx">x</span><span class="p">}</span><span class="s2">px </span><span class="p">${</span><span class="nx">backgroundPosition</span><span class="p">.</span><span class="nx">y</span><span class="p">}</span><span class="s2">px / </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">f</span><span class="p">}</span><span class="s2">px </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">f</span><span class="p">}</span><span class="s2">px repeat,
repeating-linear-gradient(
90deg,
transparent 0,
transparent </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">a</span><span class="p">}</span><span class="s2">px,
rgb(</span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">b</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">b</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">b</span><span class="p">}</span><span class="s2">) </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">a</span><span class="p">}</span><span class="s2">px,
rgb(</span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">b</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">b</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">b</span><span class="p">}</span><span class="s2">) </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">b</span><span class="p">}</span><span class="s2">px
)
</span><span class="p">${</span><span class="nx">backgroundPosition</span><span class="p">.</span><span class="nx">x</span><span class="p">}</span><span class="s2">px </span><span class="p">${</span><span class="nx">backgroundPosition</span><span class="p">.</span><span class="nx">y</span><span class="p">}</span><span class="s2">px / </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">f</span><span class="p">}</span><span class="s2">px </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">f</span><span class="p">}</span><span class="s2">px repeat,
repeating-linear-gradient(
0deg,
rgb(</span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">c</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">c</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">c</span><span class="p">}</span><span class="s2">) </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">c</span><span class="p">}</span><span class="s2">px,
rgb(</span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">c</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">c</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">c</span><span class="p">}</span><span class="s2">) </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">d</span><span class="p">}</span><span class="s2">px,
transparent </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">d</span><span class="p">}</span><span class="s2">px,
transparent </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">e</span><span class="p">}</span><span class="s2">px
)
</span><span class="p">${</span><span class="nx">backgroundPosition</span><span class="p">.</span><span class="nx">x</span><span class="p">}</span><span class="s2">px </span><span class="p">${</span><span class="nx">backgroundPosition</span><span class="p">.</span><span class="nx">y</span><span class="p">}</span><span class="s2">px / </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">f</span><span class="p">}</span><span class="s2">px </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">f</span><span class="p">}</span><span class="s2">px repeat,
repeating-linear-gradient(
90deg,
rgb(</span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">c</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">c</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">c</span><span class="p">}</span><span class="s2">) </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">c</span><span class="p">}</span><span class="s2">px,
rgb(</span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">c</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">c</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">c</span><span class="p">}</span><span class="s2">) </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">d</span><span class="p">}</span><span class="s2">px,
transparent </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">d</span><span class="p">}</span><span class="s2">px,
transparent </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">e</span><span class="p">}</span><span class="s2">px
)
</span><span class="p">${</span><span class="nx">backgroundPosition</span><span class="p">.</span><span class="nx">x</span><span class="p">}</span><span class="s2">px </span><span class="p">${</span><span class="nx">backgroundPosition</span><span class="p">.</span><span class="nx">y</span><span class="p">}</span><span class="s2">px / </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">f</span><span class="p">}</span><span class="s2">px </span><span class="p">${</span><span class="nx">vars</span><span class="p">.</span><span class="nx">f</span><span class="p">}</span><span class="s2">px repeat,
rgb(</span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">a</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">a</span><span class="p">}</span><span class="s2">, </span><span class="p">${</span><span class="nx">colors</span><span class="p">.</span><span class="nx">a</span><span class="p">}</span><span class="s2">);
`</span><span class="p">;</span>
<span class="nx">grid</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="dl">'</span><span class="s1">style</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">background: </span><span class="dl">'</span> <span class="o">+</span> <span class="nx">background</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As you can see the update method is used to generate the background css attribute. It needs to be set to the element by using setAttribute. <code class="highlighter-rouge">grid.style.background = background;</code> does not work and I honestly don’t know why to be honest ;).</p>
<p>Please check the full example on <a href="https://codepen.io/octopus11/pen/yLNMzwg" title="codepen" target="_blank">codepen</a>.</p>DionMaking an infinite grid based on css and made dynamic with JavaScriptHello switch2020-02-23T13:14:14+01:002020-02-23T13:14:14+01:00http://localhost:4000/concept/2020/02/23/hello-switch<div class="larger">
So, what is this switch all about. Let's start with an example about a bakery that wants to make cookies.
</div>
<p><img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-switch/cookie-factory.png" alt="Cookies" title="Cookies" /></p>
<p>If you look at the cookie bakery <strong>(1)</strong>. It needs things to be able to bake a cookie and give it to the cookie monster. So it’s being provided by all the providers that have the individual product. It also needs batter, which is produced by a machine. The machine needs ingredients as well.</p>
<p>The cookie bakery is happy and it can produce cookies without any problem. The cookie monster is happy. But the cookie bakery is getting smarter. They want to get the ingredients from a supplier that knows the bakeries needs. Luckily, there is one!</p>
<p><img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-switch/cookie-factory-with-supplier.png" alt="Cookies" title="Cookies" /></p>
<p>The supplier <strong>(1)</strong> has all kinds of products, in fact, it has everything the bakery needs. Also things the bakery does not need, but that doesn’t matter. The bakery has a special gate for the large pallet of ingredients coming from the supplier <strong>(2)</strong>.</p>
<p>Also, the color coding is letting you know what to expect from the connections.</p>
<ul>
<li><strong>White</strong> row. Normal output, directly what’s in the row. It cannot connect to the switch.</li>
<li><strong>Blue</strong> row. Gives produced output (The batter machine takes all ingredients and outputs batter). Blue rows cannot connect to the switch.</li>
<li><strong>Green</strong> row. Successful output. Can connect to the switch, driving it’s enabled state and passes on what the cookie bakery is needing.</li>
<li><strong>Red</strong> row. Unsuccessful output. Some ingredient was not present maybe.</li>
<li><strong>Yellow</strong> row. This row’s value is driven by input from the outside.</li>
</ul>
<p>Lets have a closer look at the supplier by selecting it.</p>
<p><img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-switch/supplier-with-menu.png" alt="Cookies" title="Cookies" /></p>
<p>On selection of the supplier <strong>(1)</strong>, we get a closer look at it’s internals. It shows all that it can supply. It has a lot of ingredients in stock. You can also choose from different types of ingredients. <strong>(2)</strong> For example salt, it can be changed to salt from the himalayas too. Or oil, change it to sunflower oil or olive oil.</p>
<p>There are also produced outputs <strong>(4)</strong>. Based on the ingredients that are selected it may be able to generate a produced output. After all, the baking supplier has it’s own batter machine. The checked circle in the menu behind the “batter” bar indicates that we are requiring the supplier to be able to provide batter. So once it can create batter with the ingredients, it continues with the green path.</p>
<p>Looking at the menu, you see the rows of ingredients it can have. The opened lock means you can change it’s contents from the menu. <strong>(3)</strong> Closing the eye would make it disappear from the node, just to keep the node small.</p>
<p>It means we could just as well disable all the rows because we are only needing the green and the red row.</p>
<p><img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-switch/clean-baking-supplies.png" alt="Cookies" title="Cookies" /></p>
<h3 id="flow">Flow</h3>
<p>Lets have a look at how the flow is determined.</p>
<p><img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-switch/looking-at-flow.png" alt="Cookies" title="Cookies" /></p>
<p>The baking supplies node is enabled, you can see that because of the green switch. The baking supplier has everything to create it’s batter as well. Since we required the node to be able to create batter, it will influence the way it will continue the flow. It means it will choose to continue the flow with the green path. The geen row is connected to the switch input of the bakery, and the bakery is happily supplied with everything it needs to bake it’s cookies. Therefore it will continue the flow with the green path as well. The flow reaches the cookie monster, resulting in a happy cookie monster.</p>
<h5 id="lets-make-the-cookie-monster-unhappy">Let’s make the cookie monster unhappy</h5>
<p>It’s simple. For example, we can just make the supplier devoid of a required ingredient for batter.</p>
<p><img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-switch/unhappy-supplier-flow.png" alt="Cookies" title="Cookies" /></p>
<p>Select the supplier node to bring out the menu again <strong>(1)</strong>. Use the <code class="highlighter-rouge">multiselect</code> to set it to have no value for flour.</p>
<p>Now the supplier <strong>(2)</strong> can’t produce flour, but we told it that it should be able to produce flour so the flow will continue with the red row. It will never reach the bakery, <strong>(4)</strong> but it’s no problem. The baking supplier is happy to report the sad news to the cookie monster <strong>(3)</strong>.</p>
<h5 id="can-the-bakery-make-the-cookie-monster-unhappy">Can the bakery make the cookie monster unhappy?</h5>
<p>Definitely. Let’s change a setting.</p>
<p><img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-switch/unhappy-bakery-flow.png" alt="Cookies" title="Cookies" /></p>
<p>If we select the supplier again, to bring up the supplier node menu again and disable the checkbox behind the <code class="highlighter-rouge">batter</code> output and we leave the flour <code class="highlighter-rouge">multiselect</code> to have nothing selected. The output of the supplier will be through the green row. But now the bakery cannot bake a cookie, thus it will continue on the red row <strong>(2)</strong> leaving the bakery to have to inform the cookie monster <strong>(3)</strong>.</p>
<p>In a real situation the flow is dependant on the request, therefore the flow can only been made visible by mock request.</p>
<p>There is a lot more to explore about the switch button. It will be explored later on. Thanks for reading!</p>
<div class="footnote">
This is part of a series of articles exploring a node based, no code, web development user interface.
<ul>
<li><a href="https://dionsnoeijen.nl/ui/dev/2020/02/15/leaving-projects-behind.html">Leaving projects behind</a></li>
<li><a href="https://dionsnoeijen.nl/concept/2020/02/17/rethinking-the-approach.html">Rethinking the approach</a></li>
<li><a href="https://dionsnoeijen.nl/concept/2020/02/17/say-hello-world.html">Say hello world</a></li>
<li><a href="https://dionsnoeijen.nl/concept/2020/02/23/hello-switch.html">Hello switch</a></li>
</ul>
</div>DionMore in depth on the switch button on the nodes. What is it? Why and how can it work?Managing time2020-02-22T21:22:22+01:002020-02-22T21:22:22+01:00http://localhost:4000/personal/2020/02/22/managing-time<div class="larger">
I am the worst at managing my time. You see, I don't have a lot if spare time to work on my personal projects. But the time that I have I find hard to manage efficiently! I'm mostly driven by a gut feeling. A surge of inspiration that may last for a time period ranging from one hour, to a couple weeks. But never enough to bring the project to an end. I already learned some tricks to handle that. But still, I suck at prioritizing. This leaves me with a lot(!) of unfinished projects. In fact, I never finished a significant project. So how do I intend to change that, so my node based programming interface sees the light of day any time in the future?
</div>
<h2 id="how-much-time-do-i-have">How much time do I have?</h2>
<p>All in all, like most people, I’m a pretty busy guy. I have a wife, children and a dayjob. Some social activity is desirable as well. I also go to the gym three times a week to stay in shape. And then there are hobbies that ask for my time too. And, not to forget, I need time to spend on spiritual activities as well, like meditation. This alone is enough to fill up a life. So how to build a project in my free time next to all that. It seems that prioritizing is what can be done much better. Let’s have a closer look at the main activities occupying my time.</p>
<h4 id="wife-and-children">Wife and children?</h4>
<p>No prioirity! … Ehm, all right, that’s nonsense of course. But I have to be honest. My schedule does impact time that I spend with them. And yes, this should be done better. Of course, they do have high priority! So there needs to be time set aside just to spend quality time with them.</p>
<h4 id="social-activity">Social activity</h4>
<p>You see, I’m quite social, but not super social. I spend enough time with friends but I’m a relatively private person and I like it that way. In the end, this does not impact my time in a way it becomes impairing to my projects. It’s balanced and I feel comfortable with.</p>
<h4 id="dayjob">Dayjob</h4>
<p>I need a job to provide for my family, and I like my job. You see, at work I have to be sharp as well. What I did, and it has been like this for a couple of years now, is to cut down on the time required for my dayjob. I just work one day less. It frees up valuable time.</p>
<h4 id="staying-in-shape">Staying in shape</h4>
<p>This is a clear non-negotiable activity. With this type of work it’s all too easy to move my body far too little. I’m thirty-seven years old, so sitting behind a desk every day of the week and expecting to feel healthy and fit is simply not going to happen. Not moving has a far reaching impact on my health and mental state. Rationally I look at it this way: Any activity that contributes to longevity and improved health, it by definition more important than anything that does not. In return I feel more energetic, sharper and even more self-confident. This pays back big time when it comes to my family, job and side-projects.</p>
<h4 id="hobbies">Hobbies</h4>
<p>No. As I mentioned there are those ‘hobbies’. You can also say it this way: I have a chronic lack of focus. I have way too many interests. Something has to give, but what? For example. Who makes a game like Factorio!! This is a major distraction! The truth is, that those things have to give. Playing a game every now and then… allright. But such massive time consumers as Factorio? There’s absolutely no need to send this rocket into space. (I just did it… ;)) Other hobbies, like making creative coding thingies or playing with 3d software like Blender can also be less. It’s just not contributing to the main goal. I also like to read, that is something I won’t quit as it provides me with important knowlegde about a lot of things. It really contributes to my well being.</p>
<p><img src="https://res.cloudinary.com/lmn/image/upload/fl_lossy,q_80/f_auto/v1/gameskinnyc/f/a/c/factorio-rocket-33433.jpg" alt="Rocket" title="Rocket" /></p>
<h4 id="spiritual-health">Spiritual health</h4>
<p>I’m not going in depth about what I regard as my spiritual growth or what it is that I believe exactly. I may do that some other time. But the fact remains that I need at least some time to set aside to practice meditation. This is also a non-negotiable activity like staying in shape because it contributes to my overall well-being. In truth, this is usually one of the first things that I forget or put aside for ‘later’ only to never really get to it. It should have higher priority though.</p>
<h3 id="conclusion">Conclusion</h3>
<p>The first thing I had to learn is how not to depend on the surge of inspiration. After having worked on so many projects as I did, you learn that the inspiration is only temporary. There will be a time it starts feeling like work again. There is no magic bullet to conquer that feeling. It’s just about discipline. Just pick up the project and continue working on it. Maybe it wasn’t much, but do something. Every step is a step. Oh, and if you keep at it, inspiration comes back. It comes and goes in waves. Just ride the waves. I know this because I managed to push myself far enough. But I still don’t master it, otherwise the priority issue wasn’t there.</p>
<p>It looks like there is some low hanging fruit that can be tackled right away so let’s start with that. A significant gain in time that can be spent on my side project comes down to cutting down time on playing useless games. And that really is a big time saver! Looking at the sixtysix hours I needed to send that Factorio rocket into space. Well, that was just a waste of time.</p>
<p>Other than that, there is some other areas besides personal projects that could benefit from more attention. Like my family and spirituality. But this seems doable! Let’s put it to the test. The amount of updates I manage to write about my node based user interface will be a testimony to my successful implementation of better time management.</p>DionHaving enough time to get things done is an art on itself. Some personal reflections on how I manage and prioritize my time.Say hello world2020-02-17T08:00:00+01:002020-02-17T08:00:00+01:00http://localhost:4000/concept/2020/02/17/say-hello-world<div class="larger">
Finally, I'm ready to talk about some of the real stuff. I really need to know how the ui is going to work. In detail! So I took my design application and think of things I would like to make. How would it work if it was a real application? I will write and think as if it's user documentation. As for all software, I will start with the hello world example.
</div>
<p>First I need to explain some of the user interface basics. As for now, it’s divided into a couple of regions.</p>
<p><img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-world/base-layout.png" alt="Base Layout" title="base layout" /></p>
<ul>
<li>A. A horizontal menu bar.</li>
<li>B. A vertical menu bar, containing a tree view.</li>
<li>C. Where the node interaction takes place.</li>
<li>D. Clicking a node will present a menu there.</li>
<li>E. A footer, containing contextual information.</li>
</ul>
<h3 id="hello-world">Hello World</h3>
<p>Lets get started with hello world! With this example, there are already some useful things to be said. This is the flow that responds to a route. We have configured a route that responds to <code class="highlighter-rouge">/hello</code>, something like this: <code class="highlighter-rouge">https://somedomain.com/hello</code>.</p>
<p><img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-world/hello-world.png" alt="Hello World" title="Hello World" /></p>
<p><span class="sub">So, as you may have guessed, this is displayed in the C region.</span></p>
<p>This will always leave you with at least two nodes. This is the request and response node <strong>(1, 3)</strong>.</p>
<p>We like to have a response that contains a json body with a <code class="highlighter-rouge">key</code> named <code class="highlighter-rouge">text</code> that contains the string <code class="highlighter-rouge">Hello world!</code> like this:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"text"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Hello world!"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>This will require is to add a string variable node to the flow <strong>(2)</strong>.</p>
<div class="row">
<div class="left">
<img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-world/text-var-node-step-1.png" />
</div>
<div class="right">
<img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-world/base-layout-c-region-highlight.png" />
</div>
</div>
<p>The nodes are added to the <strong>C</strong> region of the application.</p>
<p>Selecting the newly added node will have a form appear in the <strong>D</strong> region of the application.</p>
<div class="row">
<div class="left">
<img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-world/text-var-node-selected.png" />
</div>
<div class="right">
<img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-world/base-layout-d-region-highlight.png" />
</div>
</div>
<p>This form. allows extra options for this node to be enabled. First of all, give it a text value: “Hello world!”. Now enable the <code class="highlighter-rouge">key</code> checkbox and give the key a value of <code class="highlighter-rouge">text</code>.</p>
<p><img style="display:block; margin: 0 auto;" src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-world/text-var-node-step-2.png" width="50%" /></p>
<p>This is number <strong>2</strong> in the hello world example.</p>
<p>As you can see, there is only one connection between the output of the text node and the body of the response node. <strong>(3)</strong></p>
<p>This will produce the hello world response where the key name is determined by the nodes key variable and the text by the text output.</p>
<p>There are still more things that could be said about this example. Like what the switch buttons do or why there is no connection needed between the request and text variable node. But this will become apparent in the next examples.</p>
<h3 id="hello-you">Hello you!</h3>
<p>Let’s make the example a little more interesting by adding a segment variable to the request url.</p>
<p><img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-world/hello-you.png" alt="Hello World" title="Hello World" /></p>
<h4 id="1-change-the-route-for-the-request-node">1. Change the route for the request node</h4>
<p>By adding a segment variable to the request route we can exchange the word “world” by whatever that was added to this segment.</p>
<p>Selecting the request node <strong>(1)</strong> in the <strong>C</strong> region will open up a request node form in the <strong>D</strong> region of the application.</p>
<p><img style="display:block; margin: 0 auto;" src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-world/base-layout-d-region-highlight.png" width="50%" /></p>
<p>Through this form you can configure the rout to accept a segment variable. This will look like this: <code class="highlighter-rouge">/hello/{name}</code>. This will add a row to the request node with an output connector on it.</p>
<h4 id="2-change-the-text-var-node-to-accept-a-variable">2. Change the text var node to accept a variable</h4>
<p>We also need to select the text node again and make a change to the “Hello world!” text. Change it to: “Hello {name}!”. This will add a row to the text node with an input connector. After making those changes the text node should look like this:</p>
<p><img style="display:block; margin: 0 auto;" src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-world/text-var-node-step-3.png" width="50%" /></p>
<h4 id="3-add-connection">3. Add connection</h4>
<p>Now we can add a connection between the request node <strong>(1)</strong> and the text node <strong>(2)</strong>. Note that the connection is drawn between the two newly introduced rows that take care of the <code class="highlighter-rouge">{name}</code>.</p>
<hr />
<p>Now, when making a request like this: <code class="highlighter-rouge">https://somedomain.com/hello/Bert</code> will result in a json response like this one:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"text"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Hello Bert!"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>You may have noticed that the connection from the text node <strong>(2)</strong> to the response node <strong>(3)</strong> has changed as the input is connected to the switch button of the response node. This changes the switch button position to the middle and changes the color to yellow, indicating it’s driven by an outside factor which is the text node.</p>
<p>In this particular example, this change is unnecessary as it will produce the exact same output. It is worth noticing that the body row of the response node is still yellow, indicating that it’s driven by an outside factor.</p>
<p>The same goes for the connection between the request node <strong>(1)</strong> and the text node <strong>(2)</strong>. If we were to make the connection between the green row on the request node <strong>(1)</strong> with the word <code class="highlighter-rouge">GET</code> and the switch button of the text node <strong>(2)</strong> the result of the output would be exactly the same.</p>
<p>For the next examples of the hello world document this is all that is relevant. If you want to dive more deeply into the subject of the switch button and it’s connections between colored rows here: “SOME LINK”.</p>
<h3 id="hello-you-in-a-random-language">Hello you! In a random language</h3>
<p>Let’s make it more interesting by returning the word “Hello” in a random language.</p>
<p><img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-world/random-language-hello.png" alt="Hello World" title="Hello World" /></p>
<h4 id="1-add-a-new-action-node">1. Add a new <code class="highlighter-rouge">action node</code>.</h4>
<p>This is a <code class="highlighter-rouge">random</code> node. It will output true for one of it’s output connections.</p>
<h4 id="2-add-two-more-text-nodes">2. Add two more text nodes.</h4>
<p>And make sure each one has the word “hello” in it’s own language. Let’s add one for French and another one for Spanish. So now we have a node for <code class="highlighter-rouge">Hello {name}!</code>, <code class="highlighter-rouge">Bonjour {name}!</code> and <code class="highlighter-rouge">Hola {name}!</code>. Also make sure each of those nodes have the <code class="highlighter-rouge">key</code> set to the value <code class="highlighter-rouge">text</code>.</p>
<h4 id="3-connect-the-nodes">3. Connect the nodes</h4>
<p>From the request node, connect the output of the row with the segment variable <code class="highlighter-rouge">{name}</code> to the input rows of the text nodes with the row for the variable <code class="highlighter-rouge">{name}</code> as we did before.</p>
<p>Now, connect the output connector of the <code class="highlighter-rouge">random</code> action node <strong>(1)</strong> to the switch buttons of the three text nodes <strong>(2)</strong>.</p>
<p>And last, connect the output of the text nodes to the body row of the response node.</p>
<p>Here also goes that the output row of the text nodes can also be connected to the switch of the response node. If you do so, make sure it’s all three on the switch, or all three on the body row. For more information on the switch: “SOME LINK”.</p>
<hr />
<p>Calling this setup with: <code class="highlighter-rouge">https://somedomain.com/hello/Bert</code> will result in one of the following json output!</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"text"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Hello Bert!"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"text"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Bonjour Bert!"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"text"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Hola Bert!"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<h4 id="enhancement-by-setting-the-key-with-a-variable">Enhancement by setting the key with a variable</h4>
<p>As you can see, the keys of the text nodes are being set by hand for every node. In a real application this can easily lead to mistakes. It would be better to control the name of the key from a single point. This can be easily solved by using the input connector on the key row like this:</p>
<p><img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-world/random-language-hello-var-key.png" alt="Hello World" title="Hello World" /></p>
<p>Simply add a text node <strong>(1)</strong> and make sure it’s value is <code class="highlighter-rouge">text</code>. Now connect the output of the text row to the key inputs <strong>(2)</strong> of the hello output text nodes.</p>
<p>Now the key name is driven by this text node.</p>
<h3 id="the-power-of-groups">The power of groups</h3>
<p>As you can see with the random language example, the amout of connections can build up quickly. This can easily lead to clutter, obscuring clarity.</p>
<p>To solve this issue, nodes can be grouped. This way you can introduce your own nodes that handle logic inside.</p>
<p><img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-world/random-grouped.png" alt="Hello World" title="Hello World" /></p>
<p>By selecting a set of nodes with the rectangular selection box and pressing the group button. A rectangle will appear around the selected nodes.</p>
<p>The group button is located in the <strong>A</strong> region of the application and is only active whenever there is more then one node selected.</p>
<p><img style="display:block; margin: 0 auto;" src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-world/base-layout-a-region-highlight.png" width="50%" /></p>
<p>You can give the group a custom name by changing the text in the top left corner of the group rectangle <strong>(1)</strong>. Let’s name it “Random hello”.</p>
<p>Whenever the group is in an opened state, every node added is automatically added to the group. We need to change the text node setup as displayed in the image.</p>
<p>As you can see, there is a node on the left <strong>(2)</strong> that is connected to the left outside of the group rectangle. It’s output is connected to the three hello text nodes. This node is the node that passes on the <code class="highlighter-rouge">{name}</code> from the request node to one of the hello text nodes. The active hello text node. Which one is active is determined by the random node.</p>
<p>As you may notice, the key row is not present anymore in the hello text nodes. This is moved to the text node on the right side of the group <strong>(3)</strong>. This will be the node that contains one of the thee selected texts and it will act as an intermediate node so we can expose it’s output to the outside of the group by dragging the node output to the right side edge of the group.</p>
<p>Collapsing the group, by pressing the arrow button in the right top corner of the group rectangle will result in the group being displayed as a node like this:</p>
<p><img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/nodes/hello-world/closed-group.png" alt="Hello World" title="Hello World" /></p>
<p>As you can see, the collapsed group <strong>(1)</strong> has one row. Where the left side represents the connection made inside the group to the left side of the group rectangle. And the right side the connection made to the right side of the group rectangle.</p>
<p>Now we can connect the request node to the group and the group to the response node as displayed in the image. This will leave us with the same functionality as we had in the random hello response example but in a much less cluttered way. Grouping is a very important feature to help reduce the spaghetti problem.</p>
<h3 id="conclusion">Conclusion</h3>
<p>This document has shown the most basic functionality of the node system. But there’s a lot more required to make this into the powerhouse it’s supposed to be. Next week there will be more.</p>
<div class="footnote">
This is part of a series of articles exploring a node based, no code, web development user interface.
<ul>
<li><a href="https://dionsnoeijen.nl/ui/dev/2020/02/15/leaving-projects-behind.html">Leaving projects behind</a></li>
<li><a href="https://dionsnoeijen.nl/concept/2020/02/17/rethinking-the-approach.html">Rethinking the approach</a></li>
<li><a href="https://dionsnoeijen.nl/concept/2020/02/17/say-hello-world.html">Say hello world</a></li>
<li><a href="https://dionsnoeijen.nl/concept/2020/02/23/hello-switch.html">Hello switch</a></li>
</ul>
</div>DionHow could this node based no code platform for backend web applications work? Let's start with the most basic example.Rethinking my approach2020-02-17T07:00:00+01:002020-02-17T07:00:00+01:00http://localhost:4000/concept/2020/02/17/rethinking-the-approach<div class="larger">
So, in my <a href="https://dionsnoeijen.nl/ui/dev/2020/02/15/leaving-projects-behind.html">last post</a> I describe how my first attempt failed. But I'm not done yet.
</div>
<p>I have taken some time to rethink what I’m doing. First of all, there needs be be a bigger plan. I mean, when I zoom out a little, and think about the concept itself. How is this thing even going to work at all?
Ok, the idea is to have a node based system to build backend web applications without having to write code. I have written about this idea extensively in my first posts some years ago. However, as I find out while exploring the idea, I conclude that working that way wil lead to spaghetti, almost literal spaghetti if you look at the final design of the concept.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/everything.PNG" alt="Completed flow" /></p>
<p>At this point, the solution to this problem is still quite vague. In fact, I’m exploring the solution right now. Maybe this should have been the first thing to do, but I don’t like it when such a thing keeps me from building. So, also in this case. I already have a brand new, working ui engine that facilitates a lot of the basic requirements. It’s much better than the last one. But for clarity, I will not talk about it to much yet. If I would write about my projects the way I work, it would be really hard to follow. Let me start at the beginning as if I’m a structured person.</p>
<h2 id="first-things-first">First things first</h2>
<p>I need to outline the big steps that are required to take.</p>
<ol>
<li>The concept</li>
<li>The engine</li>
<li>The backend</li>
<li>The business</li>
<li>The hosting</li>
</ol>
<p>Those steps break it down in the largest possible chunks I can think of.</p>
<h3 id="the-concept">The concept</h3>
<p>Really, al I had in mind before starting the development on the new engine were some vague ideas. I need grouping, to prevent spaghetti. And I need a clear starting point of the flow. Which is, in basis a route. But of course. There is much more to it. This is where I am now. I will start showing the conceptual flows of the application to come to a workable starting point.</p>
<h3 id="the-engine">The engine</h3>
<p>A lot of work has been done regarding the reworking of the engine. And a lot of the basic functionality stands already. But, after the concept is finished, a lot of work still needs to be done. The choices of software have been way different. For example, this is a React with Redux application. Canvas is involved, but in a very different manner. I will show all about it soon.</p>
<h3 id="the-backend">The backend</h3>
<p>See, this is another huge thing that needs to be tackled. I can have a massive and beautiful ui. But a backend is required as well. What else is going to run and interpret all the connections and configuration? I may already have a big chunk of this backend in my open source project SexyField. Much more on this later as well.</p>
<h3 id="the-business">The business</h3>
<p>Before I can even start thinking about how to host this thing, I have to know how to bring this project to market. Will it be a saas platform, or rather a licence based system. Maybe open source even? I really don’t know yet. I hope things will become more clear later on.</p>
<h3 id="the-hosting">The hosting</h3>
<p>Say it will be a saas platform, how to host? And if not, which is most likely going to be the case, how will I make sure it’s widely supported. Will I have to make an installer, remote updates? There’s a lot to decide on this side of the project.</p>
<h2 id="mvp">Mvp</h2>
<p>This brings me to another point. This thing has the potency to explode right in my face when it comes to the amount of work. Honestly I don’t even know if it’s reasonable to keep at it by myself. But as for now, there really is no other option. I will have to make sure not to implement every little feature I can think of. In fact, I should not even try to work towards a real working mvp at first. The first thing I will go for, is a small promo movie, tho show what it should do. I may fake some things, just for the movie. But that’s fine. That way I will finally be able to really show what it’s supposed to do!</p>
<p>Thanks for reading this far! The next post will really go into detail about the concept!</p>
<div class="footnote">
This is part of a series of articles exploring a node based, no code, web development user interface.
<ul>
<li><a href="https://dionsnoeijen.nl/ui/dev/2020/02/15/leaving-projects-behind.html">Leaving projects behind</a></li>
<li><a href="https://dionsnoeijen.nl/concept/2020/02/17/rethinking-the-approach.html">Rethinking the approach</a></li>
<li><a href="https://dionsnoeijen.nl/concept/2020/02/17/say-hello-world.html">Say hello world</a></li>
<li><a href="https://dionsnoeijen.nl/concept/2020/02/23/hello-switch.html">Hello switch</a></li>
</ul>
</div>DionA new no code node based platform is under construction. How am I going to start. What are the basic ideas?Leaving projects behind2020-02-15T11:12:12+01:002020-02-15T11:12:12+01:00http://localhost:4000/ui/dev/2020/02/15/leaving-projects-behind<div class="larger">
I don't write here often, as testified by the lack of posts on my developers blog. But I never stopped looking for the user interface of my dreams. The interface that should be able to relieve us developers of the monotonous tasks we all face all to often. But it should not just work for developers. How many of us have an idea that involves tech that they would love to try, but just don't have the skills, time or people around to make it into a reality. Mind you, this is not about a lack of intelligence or determination. Coding is just hard to master. If it's not your day job, it may be too much. What I'm after, is taking this steep learning curve out of the equation. Actually, I'm after taking coding out of the equation as a whole. But only the code, not the power. This is not a small task, it probably proves to be way harder than I expected but at some point, it will work.
</div>
<p>In the mean time, I haven’t been sitting still. In fact, over a year ago I made a pretty big application that facilitates a lot of what the user interface must be able to do as I outlined some years ago. It was a massive endeavour. I spent a big part of a year working on the thing, every free moment I had. Let me show you some of it before I explain why this failed miserably.</p>
<video width="100%" controls="">
<source src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/dionsnoeijen/continuing/octopus-1.mp4" type="video/mp4" />
</video>
<p>I know, it looks kind of crappy, but it was still in the middle of delopment.</p>
<p>During development, some things became clear to me and the conclusion that followed was. This is nog going to work. Not so much the system itself, but the way I was programming it. My mistake revolves around a single choice I made.</p>
<h2 id="canvas-element">Canvas element</h2>
<p>Somehow, I always thought it to be obvious this thing had to be made with the html canvas element. My reasoning was that the canvas element would allow me to make anything I want. Especially regarding some ideas I had that weren’t even planned for the first version. But most of all, I was under the impression that the connections between the elements, which are bezier curves, would be way to hard to draw otherwise. But also zooming and panning seemed to be pointing into the direction of canvas too. All things considered, that is the route I took. It’s all based on the canvas element. This is probably the main reason why I eventually stopped working on this attempt.</p>
<p>The thing is, with the canvas element, all of the arguments I had for using it are true. But man, it’s a lot of work to implement. I mean, every single detail you take for granted with normal html elements are not available. And I’m not just talking about the obvious lack of events. There are much more things to consider when taking the canvas route.</p>
<p>Let me sum up some of the things that were taking up a lot of extra time.</p>
<ul>
<li>State management</li>
<li>Event handling</li>
<li>Performance</li>
</ul>
<p>Let me elaborate a little on those issues.</p>
<h3 id="state-management">State management</h3>
<p>Managing a state is always a thing to consider carefully of course. At the beginning of this project I made the choice not to use a framework like react, because I figured it wouldn’t make a lot of sense in combination with using canvas. But I also did not use a separate state management system. This on itself has been fine, I just made a very simple implementation of a state manager that does the trick. However, using canvas does bring in extra work in another way because drawing the elements is something very different from the abstract state of the elements.</p>
<p>What I did was make a clear separation between the state of an element, it’s config if you will, and the drawing of an element. This, theoretically also allowed me to implement different renderers. I mand two renderers, one svg renderer, just for experimentation. And of course a canvas renderer. The renderer contains draw instructions for the canvas context. So if the renderer queries the state and it encounters a rectangle, it will call it’s rectangle draw component and voilla, rectangle. So, to be clear. For a rectangle I would have a state component describing it’s properties. And I would have a renderer component that can interpret the properties from the state and convert it to something drawn on the canvas.</p>
<h3 id="event-handling">Event handling</h3>
<p>Obviously, you have no event handling for elements you draw on the canvas. For those that do not know, the canvas element in 2d context really is just an image with a pretty low level graphics api. So drawing something on the canvas comes down to some simple instructions aling the lines of:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">context</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">red</span><span class="dl">'</span><span class="p">;</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">();</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">rect</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="mi">200</span><span class="p">);</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">fill</span><span class="p">();</span>
<span class="nx">context</span><span class="p">.</span><span class="nx">closePath</span><span class="p">();</span>
</code></pre></div></div>
<p>Doing such a thing inside of an animation callback loop will simply draw a region of pixels red sixty times per second. Your browser does not care about that region at all.</p>
<p>This means you have to implement all of that yourself. What that comes down to is comparing positions of the mouse click and drawn elements. Including their z index.</p>
<p>So, did click on position <code class="highlighter-rouge">{ x: 120, : y:150 }</code> fall within this element that is in the state positioned at: <code class="highlighter-rouge">{ x: 100, y: 100 }</code> with a width of <code class="highlighter-rouge">{ width: 200, height: 200 }</code>? Yes? Dispatch an event.</p>
<p>This comes with a lot of interesting gotchas when you have overlapping elements for example.</p>
<h3 id="performance">Performance</h3>
<p>The html canvas can be pretty fast under some circumstances. But I did learn a couple of things I didn’t really expect. Of course, drawing a lot of elements has an impact on the performance. But that got an issue pretty quickly. A single node element was quickly an accumulation of many rectangles, circles, lines and text. Having a hundred of those nodes on the canvas without optimizing is definitely something the processor is going to notice. I took measure to make the performance as good as possible. But not just performance that you would notice in the sense of jitter when you are dragging things around or zooming. I also wanted to keep overall processor activity down as much as possible.</p>
<p>Several measurements were taken to make this into a reality. First thing was to stop the animation loop when nothing is happening. In many cases using a canvas you will just have the animation callback running for the whole time the canvas is visible. But this was heating up my laptop significantly. In fact, the fans on my laptop became somewhat of a benchmark for me. I didn’t want to hear my laptop while running the app in my browser at any time. This was a pretty easy fix, just stop updating when there’s no user interaction.</p>
<p>The other thing was to make sure all that is rendered is what is in the viewport. Actually, I already had that before I was optimizing performance. This will save you a huge amount of draw calls of course.</p>
<p>The last thing was something I was trying to prevent, but eventually had to implement anyway. When panning or moving, draw less. So instead of showing all details of the nodes, I just show a simple rectangle on the place where the nodes are. This reduced the amount of items that needed to be drawn during performance heavy operations with about 99%.</p>
<p>In the end, those measurements were working really well.</p>
<h2 id="why-stop">Why stop?</h2>
<p>So, all in all, would this have worked? I’m sure it would! But still, I didn’t want to continue due to all the extra work that kept coming up making things you usually take for granted work. It took me some time to accept the fact that I wasted a lot of time on this thing. But sure, I learned a lot as well! And not just about taming a canvas based web application.</p>
<p><img src="https://dionsnoeijen.s3-eu-west-1.amazonaws.com/dionsnoeijen/continuing/bloodborne.png" title="Bloodborne" alt="Bloodborne" /></p>
<p>You see. Gaming may be something that is easily regarded as being benign, or even an infantile waste of time. And to be honest, a lot of times it is. However, there are games that have actually thought me something in a way no great self-help book could have done.</p>
<p>Anyone that made it to the end of a ‘souls’ game may know what I’m talking about. I see this loss as a boss I didn’t yet defeat. But persistence will lead to the win. Just like all bosses in Bloodborne eventually went down, this ui will come into existence. In fact, I’m working hard on my next attempt. In the following posts on this blog I will attempt to describe the journey. Maybe I will see you along the journey sometime! Thank you for reading!</p>
<div class="footnote">
This is part of a series of articles exploring a node based, no code, web development user interface.
<ul>
<li><a href="https://dionsnoeijen.nl/ui/dev/2020/02/15/leaving-projects-behind.html">Leaving projects behind</a></li>
<li><a href="https://dionsnoeijen.nl/concept/2020/02/17/rethinking-the-approach.html">Rethinking the approach</a></li>
<li><a href="https://dionsnoeijen.nl/concept/2020/02/17/say-hello-world.html">Say hello world</a></li>
<li><a href="https://dionsnoeijen.nl/concept/2020/02/23/hello-switch.html">Hello switch</a></li>
</ul>
</div>DionMy first attempt of the node based no code user interface failed. How to continue this project? A new node based no code development platform.Flow based user interface for complex structures (part 2)2016-09-14T16:12:00+02:002016-09-14T16:12:00+02:00http://localhost:4000/ui/dev/2016/09/14/flow-based-user-interface-for-complex-structures-part-2<p><a href="https://dionsnoeijen.nl/ui/dev/2016/08/11/flow-based-user-interface-for-complex-structures-part-1.html" title="Part 1">Part 1</a> was the introduction to this article. Read it if you like a little more background on the subject. Otherwise just skip it and dive in head first.</p>
<p>To explore this idea I have for a node or flow based ui for web development I need to define from where I’m reasoning.</p>
<p>First, I speak of web development, but my main goal is to create a user interface that enables a simplified way of creating a website that’s content managed. From there on I see a lot of other possibilities but for now I stick to the plan.</p>
<p>Second, this is a concept and the fictional website is intentionally simplified.</p>
<p>Last, I try to create a user interface that’s simple, but not too simple. I favor flexibility over simplicity.</p>
<p>The fictional website I’m aiming to build is nothing more than website containing products, showing a list of products and a product detail page. Those products are managed through a backend that’s behind a user login page. Containing a product list for visitors and a way to add and edit products for content editors. To spice things up a little it’s a multi lingual setup containing only two languages namely dutch and english. Adhering to my last point, the product has only the most essential fields. There is no need for more because I only want to convey the idea.</p>
<p>First, I imagine, I would want to model the product. The product is mainly made up of fields. Those fields are more than a simple column in the database. In fact they are much more like a field as seen in some content management systems like ExpressionEngine or Craft where a field is a way of defining much more than just a table column. It also provides all the ui elements needed for the content editors. The content’s of the field can easily be used inside a template. It depends on the field type what it can provide.</p>
<p>Let’s start by creating a breadboard on the grid.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/breadboard-product-1.PNG" alt="Breadboard product model" /></p>
<p><span class="sub">The breadbox should be resizable at any time</span></p>
<p>Surely I would like to have a name for this breadboard, and, I think I need types for a breadboard in certain cases. I will get back to that later on. But this is a “model” type breadboard.</p>
<p>I think those type of settings should be available by double clicking or tapping it. It would open a dialogue box that contains fields for the name and type.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/breadboard-dialog.PNG" alt="Breadboard model type dialogue" /></p>
<p>For this product we probably need a name, let’s start by adding a input text field. Again, this field would need some settings. Double clicking on the field gives a dialogue belonging to this specific input field. A name and a handle would be essential. Maybe an optional default text. The field on the breadbox shows it’s label by default. I think there should be one block reserved for the icon belonging to the field. To the left we would have a menu containing all elements you can stick to the breadboard or onto the grid itself. More on the menu later on.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/breadboard-product-2.PNG" alt="Breadboard with input text attached" /></p>
<p>To the left we have an input circle available. It’s meant to accept connecting wires. To the right we have an output circle available, you can drag a connection wire from it.</p>
<p>This field on itself does not contain enough functionality on it’s own. I need extra functionality to make this work in a real world context. For starters, I would like it to be a required field. But I also want the input length limited and from this field I would like to generate an anchor or slug that I can use later on. (If the name is “Super product” the anchor would be <code class="highlighter-rouge">super-product</code>) At this point the breadboard comes in handy. Instead of already creating wires I can simply stick modifiers to the board enabling those simple modifications for this field.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/breadboard-product-3.PNG" alt="Breadboard with modified input text output" /></p>
<p>So, instead of having the output of the field connected to the edge of the breadboard I sized it down to the middle. First I connected a required modifier. Then I have a length modifier and that output is on the right edge of the breadboard making an output circle on the breadboard or node level. Below I have an anchor modifier. The output from the length modifier is also fed into the anchor modifier that’s bringing it’s own output to the right edge of the node. Now we have a defined a required input field, limited characters and we defined an anchor based on this field.</p>
<p>The next field is a description field. In this case I imagine a rich text editor type of field, also limited but not required.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/breadboard-product-4.PNG" alt="Added the description field to the breadboard" /></p>
<p>The product also requires a price. I would like to min / max the price input, just because I can. And this field is required. Also, I feel I want output in different currencies because we are creating a multi lingual setup. The required modifier provides output, we can make connectors on the breadboard splitting up the output to other modifiers. In this case it goed directly to the euro output and I route it to a math modifier so I can make up for the currency difference and direct it to the dollar output.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/breadboard-product-5.PNG" alt="Added the price field to the breadboard" /></p>
<p><span class="sub">The MM modifier is for limiting the numeric input to only contain a value between 0 and 100</span></p>
<p>Now I just add two date fields, containing the creation and update date. Both are required. On creation both are set to the creation date. The last modifier is an output modifier. Later on it will become clear what it’s used for.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/breadboard-product-6.PNG" alt="Added the creation and update date fields" /></p>
<p>Let’s start by using this model for some basic crud functionality for the content editors. We need a list of products and a way to add a new product. Since I have two languages a tabbed interface might do just fine. But I need a way to have the same fields two times in my layout. Actually I need a way to make groups of fields so I can differentiate between them while creating the template for the create / edit page. So I would create two new breadboards of the same size and add a grouping modifier to it. This modifier accepts input from fields, duplicating the field setup.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/the-language-field-groups.PNG" alt="The language field groups" /></p>
<p><span class="sub">It would be quite awesome to overrule specific values like the label.</span></p>
<p>At this point I’m starting to think about metadata that should be coming from the breadboard. Metadata should be sent along the flow, providing context where needed. In this case I think I would at least need a group identification so I can keep apart the fields in my template. Speaking of templates, it’s time to add a render node. Again I create a breadboard and I stick a render action to it. To it’s output I add a post modifier that I can use to act upon post data.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/first-render-node.PNG" alt="The render node" /></p>
<p><span class="sub">The render node I deem simple enough to leave out the breadboard title. The filename, icon and P modifier speak for themselves. Having that said, you should be able to add a title to a breadboard at any time.</span></p>
<p>Let’s wire things up to see what we are getting at.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/wired-product-admin-boards.PNG" alt="Wired breadboards" /></p>
<p>Ok, we defined fields, grouped them and I have a template (*Product add edit) that produces post data, handled by the P variable attached to the template output. We use this post data to create or update the product. We need a “save” action, that comes equipped with an if block. That way we have the ability to add output on success and on failure. It know’s failure or success because the stream of data provides context. It has post data related to the model that also resides in the stream. The model has validation, that’s what the outcome is based upon.</p>
<p>On success we render a new page, telling the content editor everything is fine. Also, we drag two lines back to the model date fields. Depending on if this is a new entry, we need a creation date. And if it’s an update, we need the updated date. This immediately poses some questions. Mainly, is the date updated after the fact? I think not, the connections to the model should only indicate that we also need to generate one of these dates. The system behind the interpretation of this entanglement should be smart enough to know how to act.</p>
<p>Now, we have a very basic setup on how the data should flow on the administrator side of the application, but we are missing some essential parts. For starters, there’s no routing available. And if we have routing, we need a way of authentication and authorization aswel. So let’s start with that.</p>
<p>First I would need an entrance point, something representing a user making a request. This is represented by a circle with a cloud. It needs a simple output node. In case of the content editors part, we would need to see if the user is logged in. If so, lead the request straight to the admin routes. Depending on the information in the request it chooses it’s route. Say we were to visit the admin/products route. First we need to fetch the products. This can be done with the products query. We continue to link the route output to the sql input of the products query node. This is a breadboard made up of a SQL action and an IF action. The sql action is responsible for the query. On success it immediately goes on to connect to the render node. Responsible for rendering the products overview template for the administrator view. The query is using an offset and a limit to accommodate for pagination. How can it know? Maybe we should introduce some get variables like this: ?offset=0&limit=25 but I can imagine more elegant ways to do so. This is for a later article where I explore ways to make this entire setup even more elegant. For now this kind of detail only obfuscates the general idea.</p>
<p>This completes the flow from a request to a certain route, fetching the items and rendering an overview page.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/without-user-and-frontend.PNG" alt="Completed flow to product admin overview" /></p>
<p>How does it know how we are expecting to return to the admin page you might ask yourself by now? This is because of the route. Every step the flow progresses is aware of the previous steps meaning it’s aware of the route that was requested. Based on that the system knows where to go. This brings up the need for metadata again. The breadboards need a way to know where they belong so to speak. The route and the render node should know they belong to the admin side of things.</p>
<p>We probably want to know about the logged in user aswell. To do that we have to make a user model.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/user-model.PNG" alt="User model" /></p>
<p>The user model is connected to a query breadboard, fetching the user if not already present. When unable to fetch the user it will redirect to the login page. Otherwise the information is passed to the template aswel. Making available a lot of useful data we can use in our template. I imagine a twig template, that at this point would have access to the products, user and the request. In a pseudo Json form it might look like something like this.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
"products": [
{
"name": "Lorem Ipsum",
"description": "Dolor Sit",
"price_euro": "€200,00",
"price_dollar": "$224,23",
"creation_date": "YYYY-MM-DDThh:mmTZD"
},
{
"...":"..."
}
...
],
"user": {
"name": "Henkie",
"email": "henkie@example.me",
"...":"..."
},
"request": {
"url": "admin/product/super-cool/edit",
"...": "..."
}
}
</code></pre></div></div>
<p><span class="sub">This is much simplified</span></p>
<p>The other routes for the admin part probably start to make sense on their own now. So I leave it for you to see if you can make something of it. We haven’t covered what happends when a user attempts to login but fails. The user may have to create an account. however, I have ideas on how to make that flow more sense using groups like I discussed in the previous article. I will come back to that in the next article. Now it’s enough to discuss an over simplified login flow.</p>
<p>If the request comes in and finds itself to need a valid user whilst it hasn’t. The user node flows to the User routes. Triggering an action to the login page. The post values are used to query the user table and return to the admin routes on success.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/included-user-flow.PNG" alt="User login flow" /></p>
<p>Now! We also need the front-end, for user to browse the products and gaze at their relentless beauty. This flow is much simpler. A lot of what we need is already in place. We have the models and queries already in place. All we need is an extra block with routes. This block is accompanied with a languages block. The languages block has the ability to give output in a variable type of way. Making the {lang} usable in the routes blok. Prefixing the routes with language. I can imagine more elegant ways, to make routes totally dependent on the language. More on that in the next article ;). I think the front-end flow speaks for itself. One thing worth to mention is het product model. I made a modifier that duplicates it’s output nodes. That way you have a way better organize the flow, again preventing too many lines all over the place.</p>
<p>The menu should be simple, a matter of dragging and dropping stuff on the grid. The inner workings of the breadboard, especially on the ux side needs some thinking still.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/the-menu.PNG" alt="The menu" /></p>
<p>Here is what the user interface looks like at this point.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/everything.PNG" alt="Completed flow" /></p>
<p>Here is a pdf file with the design: <a href="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/nodes.pdf">PDF</a></p>
<p>As you can see, we still have a lot going on and the risk of making an unintelligible mess is all too prevalent. In fact there still are a lot of open ends to the current status of this concept. I have a lot of ideas that I want to explore in the next article. Hopefully I can gather input that I can use for the refinement needed to make this a potentially powerful method of building logical and working application flows. Thank you!</p>DionPart 1 was the introduction to this article. Read it if you like a little more background on the subject. Otherwise just skip it and dive in head first.Flow based user interface for complex structures (part 1)2016-08-11T17:07:03+02:002016-08-11T17:07:03+02:00http://localhost:4000/ui/dev/2016/08/11/flow-based-user-interface-for-complex-structures-part-1<p>For quite some time now I have this idea slumbering in the back of my mind. I think it started some years ago when a colleague of mine, not a developer, said he was certain that coding would become redundant. There would be user interfaces that can create logics like I do with my code at the same level of user friendliness Adobe Photoshop has to offer.</p>
<p>Listening to that comment from a developers perspective can be somewhat provocative. I mean, coding for input output type of projects, yes, I can see how our work will become redundant. But not by means of a user interface. More in an automated ai or machine learning type of solution. We became used or even attached to our carefully crafted code. Once you “know” how to code the ease of use argument loses any ground to stand on. In my opinion the entire statement also reveals a fundamental misunderstanding of what coding is. At the very least there is a fundamental misunderstanding about what it solves, and how. And, while I’m already burning down this comment from my well meaning colleague: Frankly, Photoshop isn’t that easy to use!</p>
<p>To be fair, at the time I was mostly building websites. Some large some small, but nothing exceedingly complicated. A lot of those websites were powered by a content management system. We were using ExpressionEngine most of the time. The entire idea of coding through a UI is nothing new, and in my humble opinion it sucks for anything serious most of the time. There definitely are some interesting attempts that I shouldn’t leave unmentioned: <a href="http://noflojs.org" title="NoFlow">NoFlow</a>. It even has a Jekyll example <a href="https://github.com/the-grid/noflo-jekyll">Jekyll</a> (this site is running on Jekyll). It looks promising but, if I understand correctly what they are doing it’s an entirely different beast. I have tried such flow diagrams on my drawing board before, but in the context of web development. More specifically in the context of a content managed website. I failed, I kept struggling to keep the resemblance of an ironical pile of spaghetti down to a minimum. I mean, even in the case of a simple input/output type of website there is quite a lot going on. We have a user, this user might have access to some parts of the website and some not. There are cases that should trigger a 404, we might need ways to restrict and validate user input and the list goes on.</p>
<p>I still think it’s a bad idea to go about coding through flow diagrams. In my opinion it usually adds complexity, while this is exactly what it’s trying to solve. And that for the sake of not having to write code. Really, just learn how to write code instead and you will be better off. But! For the act of configuration, I can definitely see a lot of added value by using a flow diagram type of system. Still, the challenge remains. How not to get a big pile of spaghetti. (spaghetti makes more sense than mud here) So, let me take you through my thought process while trying to tackle this problem.</p>
<p>First, let me show you some of the examples I have seen in the past. They are coming from 3d and animation software. Some of them contain this type of configuration / logics system for many years already. 3d animation packages have a lot going on. Think of modeling, mapping, animation, material editing, lighting, rendering and whatnot. For animation and materials the node or flow based structures are a really powerful tool to make more complicated structures without the need for coding.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/blender.png" alt="Blender" title="Blender" /></p>
<p><span class="sub">Blender, an open source 3d animation tool has a really nice implementation.</span></p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/blender-grouped.png" alt="Blender grouped" title="Blender grouped" /></p>
<p><span class="sub">It also contains this awesome idea, grouping. You can make input and output available through connections made to the left and right edges.</span></p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/modo.png" alt="Modo" title="Modo" /></p>
<p><span class="sub">Modo, also a 3d animation tool containing a very good implementation.</span></p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/modo-modifiers.png" alt="Modo modifiers" title="Modo modifiers" /></p>
<p><span class="sub">I like the way you can integrate simple logics.</span></p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/modo-grouped.png" alt="Modo grouped" title="Modo grouped" /></p>
<p><span class="sub">Also, with grouping in place.</span></p>
<p>There’s a lot of inspiration I can draw from those examples. I see a lot of use for the grouping method, it should really help reducing the messy effect that too many connecting lines have. I would like to add another idea to the table, breadboards. Lately I have been playing around with my Arduino, I bought some components to make an ultrasonic radar. Mostly just for fun. I have the starters kit and it contains a small breadboard.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/arduino-breadboard.jpg" alt="Arduino Breadboard" title="Arduino Breadboard" /></p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/arduino-breadboard-2.jpg" alt="Arduino Breadboard" title="Arduino Breadboard" /></p>
<p><span class="sub">In short, you can connect stuff that’s on the board with resistors and wires</span></p>
<p>It gave me an idea. We need to get simple logics embedded in the node itself by making use of the input and output connections on the node itself. Like this. First, you would drag out a desired size for your breadboard on a grid.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/node-breadboard.png" alt="Node Breadboard" title="Node Breadboard" /></p>
<p>Then, you would drag elements on top of them, snapping to the predefined grid on the board. Let’s drag one out, a numeric value.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/node-breadboard-num.png" alt="Node Breadboard" title="Node Breadboard" /></p>
<p>Maybe we want to multiply this value before we give output on the node level.</p>
<p><img src="https://s3-eu-west-1.amazonaws.com/dionsnoeijen/nodes/node-breadboard-mod.png" alt="Node Breadboard" title="Node Breadboard" /></p>
<p>The input circle would be able to take a value for the numeric field. That field has output available on the board when the size of the element doesn’t reach the edge. Otherwise it would create an output circle for the node. The node output is created by the multiplication modifier. This is the most basic exemplary use case I can think of. In a real world application this would require a lot more sophistication. But I will get into that in the next article since here’s a lot more to think about from here on. How the interaction should work and how to make use of the breadboards in a meaningful way. For now I will leave it here. In the next part I will explore a fictional project that makes use of the ideas explored here.</p>
<p>Feel free to leave a comment!</p>
<p><a href="https://dionsnoeijen.nl/ui/dev/2016/09/14/flow-based-user-interface-for-complex-structures-part-2.html" title="Go to Part 2">Part 2</a></p>DionFor quite some time now I have this idea slumbering in the back of my mind. I think it started some years ago when a colleague of mine, not a developer, said he was certain that coding would become redundant. There would be user interfaces that can create logics like I do with my code at the same level of user friendliness Adobe Photoshop has to offer.