UIBUILDER + VueJS Dynamic Floorplan - assistance needed

Thanks, I'll take a look as soon as I can.

First issue: <script src="/_tunnel_static/auth.js?v=1"></script></head> in index.html is loaded without a defer attribute which means that it is loaded out of order. When using defer in the head section, ensure that all of your scripts use it or it gets very confusing and will result in bugs. In reality, you probably want to load that before the uibuilder client library anyway so it probably did load in the correct order - but it is still confusing and easy to get wrong.

As that resource isn't in your repo, I've not loaded it.

2nd issue - when loading routes, take care over your HTML structure. You've copied the floorplan html but that contains an <h1> tag and you should only ever have a single h1 in your document. This doesn't cause any errors in normal use but it is a pain for people using screen readers.

3rd issue - when loading routes - the default is, once a route is loaded, it stays in memory unless you set unload: true in the config. If not unloading, you should make sure that all HTML ID's and script variables/function names are unique. Otherwise, you will get duplicate definitions which may cause really hard to find bugs.

OK, I have the floorplan route working now. Here is the simplified code:

index.html

<!doctype html>
<html lang="en"><head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" href="../uibuilder/images/node-blue.ico">

    <title>FE Router - Node-RED uibuilder</title>
    <meta name="description" content="Node-RED uibuilder - FE Router">
    

    <!-- Your own CSS (defaults to loading uibuilders css)-->
    <link type="text/css" rel="stylesheet" href="./index.css" media="all">

    <!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / -->
    <!-- <script defer src="/_tunnel_static/auth.js?v=1"></script></head> -->
    <script defer src="../uibuilder/uibuilder.iife.min.js"></script>
    <script defer src="../uibuilder/utils/uibrouter.iife.min.js"></script>
    <script defer src="./index.js"></script>
    <!-- #endregion -->

<body class="uib">
    <header>
        <h1 class="with-subtitle">An example of a framework-less front-end router</h1>
        <div role="doc-subtitle">Using the UIBUILDER IIFE library.</div>

        <h2>Current Route Title: <uib-var variable="uibrouter_CurrentTitle"></uib-var></h2>

        <nav id="main-menu" class="horizontal" aria-labelledby="primary-navigation">
            <h2 id="primary-navigation">Menu:</h2>
            <ul id="routemenu" role="menubar" aria-describedby="main-menu">
                <!-- <li><a href="#route01" onclick="router.doRoute(event)">#1</a></li> -->
                <li data-route="route01" tabindex="0" role="menuitem"><a href="#route01">Home</a></li>
                <li data-route="route02" tabindex="0" role="menuitem"><a href="#route02">SVG1</a></li>
                <li data-route="route03" tabindex="0" role="menuitem"><a href="#route03">SVG2</script></a></li>
            </ul>
        </nav>
    </header>

    <main>
        <div id="more"><!-- '#more' is used as a parent for dynamic HTML content in examples --></div>

        <div id="uibroutecontainer"><!-- router content will appear here --></div>
    </main>
</body></html>

index.js

uibuilder.logLevel = 4

const routerConfig = {
  routeContainer: '#routecontainer',
  hide: true,
  // unload: true,
  routes: [
    // {
    //   id: 'route01', src: 'part/home.html', type: 'url',
    //   title: 'Route 1 home', description: 'home'
    // },
    {
      id: 'route02', src: 'part/floorplan.html', type: 'url',
      title: 'Route 2 svg1', description: 'svg1'
    },
    {
      id: 'route03', src: 'part/route03.html', type: 'url',
      title: 'Route 3 svg2', description: 'svg2'
    },
  ],
}
const router = new UibRouter(routerConfig)

part/floorplan.html

<template id="bulb-template">
    <svg id="mybulb" class="bulb" height="3rem" viewBox="0 0 1024 1024" version="1.1"
        xmlns="http://www.w3.org/2000/svg">
        <defs>
            <filter id="shadow">
                <feDropShadow dx="1" dy="1" stdDeviation="5" flood-opacity="50%" />
            </filter>
            <filter id="glow" filterUnits="userSpaceOnUse" x="-50%" y="-50%" width="200%" height="200%">
                <!-- blur the text at different levels-->
                <feGaussianBlur in="SourceGraphic" stdDeviation="5" result="blur5" />
                <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur10" />
                <feGaussianBlur in="SourceGraphic" stdDeviation="20" result="blur20" />
                <feGaussianBlur in="SourceGraphic" stdDeviation="30" result="blur30" />
                <feGaussianBlur in="SourceGraphic" stdDeviation="50" result="blur50" />
                <!-- merge all the blurs except for the first one -->
                <feMerge result="blur-merged">
                    <feMergeNode in="blur10" />
                    <feMergeNode in="blur20" />
                    <feMergeNode in="blur30" />
                    <feMergeNode in="blur50" />
                </feMerge>
                <!-- recolour the merged blurs red-->
                <feColorMatrix result="red-blur" in="blur-merged" type="matrix" values="1 0 0 0 0
                                            0 0.06 0 0 0
                                            0 0 0.44 0 0
                                            0 0 0 1 0" />
                <feMerge>
                    <!--<feMergeNode in="red-blur"/>        largest blurs coloured red -->
                    <feMergeNode in="blur-merged" />
                    <feMergeNode in="blur5" /> <!-- smallest blur left white -->
                    <feMergeNode in="SourceGraphic" /> <!-- original -->
                </feMerge>
            </filter>
        </defs>
        <title>TITLE</title>
        <path name="icon"
            d="M511.549861 803.293331H408.419043a73.232959 73.232959 0 0 1-67.1862-41.991375 59.795719 59.795719 0 0 1-6.71862-30.569722 207.60536 207.60536 0 0 0-33.593101-113.88061 196.519637 196.519637 0 0 0-27.882273-33.5931A463.248853 463.248853 0 0 1 217.274302 504.314738a399.086031 399.086031 0 0 1-36.95241-75.248544 242.542184 242.542184 0 0 1-15.116895-77.264131 349.032312 349.032312 0 0 1 8.062344-84.990544 314.76735 314.76735 0 0 1 51.733375-114.888403A367.172586 367.172586 0 0 1 361.724634 34.011334 327.532728 327.532728 0 0 1 433.949799 8.144647 369.524103 369.524103 0 0 1 528.682342 0.418234a333.579486 333.579486 0 0 1 126.310057 29.225997 326.860866 326.860866 0 0 1 70.881442 44.678824A382.625412 382.625412 0 0 1 808.848799 168.383736a314.095488 314.095488 0 0 1 41.991375 105.146403 312.751764 312.751764 0 0 1 6.382689 92.045095 275.799353 275.799353 0 0 1-20.15586 76.256338 449.139751 449.139751 0 0 1-61.139443 107.16199 497.513815 497.513815 0 0 1-33.5931 39.639858 160.575019 160.575019 0 0 0-31.241583 48.038134 215.331773 215.331773 0 0 0-18.812136 55.428615c-1.679655 11.757585 0 23.179239-2.687448 33.5931a171.660742 171.660742 0 0 1-3.695241 25.194826 69.873649 69.873649 0 0 1-33.593101 40.647651 74.576683 74.576683 0 0 1-39.639858 10.07793zM490.050277 88.768088c-11.085723 0-22.171446 2.351517-33.5931 4.031172a210.96467 210.96467 0 0 0-74.240752 26.538549 244.221839 244.221839 0 0 0-55.428616 44.342893 222.386324 222.386324 0 0 0-43.335099 63.82689 230.784599 230.784599 0 0 0-19.483998 94.732543 28.218204 28.218204 0 0 0 33.5931 28.890066 28.890066 28.890066 0 0 0 22.171446-26.202618v-13.773171a167.965501 167.965501 0 0 1 9.406068-49.045927 184.762052 184.762052 0 0 1 64.834684-83.98275 167.965501 167.965501 0 0 1 93.72475-33.593101 142.770676 142.770676 0 0 0 18.140274 0 23.851101 23.851101 0 0 0 19.148067-15.452826 33.5931 33.5931 0 0 0 0-19.483998 23.51517 23.51517 0 0 0-20.491791-18.140274 122.950747 122.950747 0 0 0-15.116895 0zM647.601917 943.040628a15.788757 15.788757 0 0 1-13.773171 15.116895H400.356699a17.468412 17.468412 0 0 1-16.460619-8.734206 18.812136 18.812136 0 0 1 0-20.15586 16.124688 16.124688 0 0 1 16.460619-4.703034h227.089358a19.148067 19.148067 0 0 1 19.148067 20.827722zM405.731595 899.369598a18.140274 18.140274 0 0 1-16.460619-12.765378 17.804343 17.804343 0 0 1 15.452826-23.851101H635.508401a18.812136 18.812136 0 0 1 17.804343 13.773171 19.819929 19.819929 0 0 1-10.749792 21.499584 24.187032 24.187032 0 0 1-8.734206 0H423.535938zM437.64504 1022.992207a17.132481 17.132481 0 0 1-15.452826-9.406068 18.140274 18.140274 0 0 1 15.116895-26.202618h139.411367a19.819929 19.819929 0 0 1 19.483998 17.804343 16.124688 16.124688 0 0 1-8.734206 15.788757 19.148067 19.148067 0 0 1-9.741999 3.023379H442.348074z" />
        <!-- <circle name="default" cx="50" cy="50" r="50"></circle> -->
    </svg>
</template>

<h2 class="with-subtitle">Dynamic SVG Example</h2>

<h3>My House, Ground Floor</h3>
<div id="floorplan" class="plan"><!-- Bulb icons dynamically inserted here --></div>

<script>
    console.log('floorplan.html route script running')

    function doMe(event) {
        uibuilder.eventSend(event)
    }

    /** Insert a clone of a template tag
    * NB: Template should have only a single direct child tag, nothing other than that tag and its contents will be
    cloned.
    * @param {HTMLTemplateElement} template The template to clone and insert
    * @param {HTMLElement} parent The parent within which to insert the clone
    * @param {object} [ui] Optional uib UI object that will apply changes to the cloned element (e.g. attribs, slot)
    */
    function htmlClone(template, parent, ui) {
        console.log(ui)
        if (!template || !(template instanceof Element)) {
            console.error('Template HTMLElement not provided or is not an HTML Element')
            return
        }
        if (!parent || !(parent instanceof Element)) {
            console.error('Parent HTMLElement not provided or is not an HTML Element')
            return
        }
        if (!ui) ui = {}
        if (!ui.position) ui.position = 'last'

        const clone = template.cloneNode(true)

        // Oops! Fns starting with `_` should not have been used - sorry. This fn no longer available directly.
        // Will add an equivalent in a future release (post v6.4.1) probably called `uiEnhanceElement`
        // uibuilder._uiComposeComponent(clone, ui)
        clone.id = ui.id
        clone.classList.add(ui.attributes['data-posn'])
        clone.dataset.state = ui.attributes['data-state']
        clone.dataset.posn = ui.attributes['data-posn']
        clone.onclick = doMe //uibuilder.eventSend

        if (ui.position === 'first') {
            // Insert new el before the first child of the parent. Ref:
            https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore#example_3
            parent.insertBefore(clone, parent.firstChild)
        } else if (Number.isInteger(Number(ui.position))) {
            elParent.insertBefore(clone, parent.children[ui.position])
        } else {
            // Append to the required parent
            parent.appendChild(clone)
        }

    }

    const x = {
        "id": "bulb1",
        "attributes": {
            "class": "bulb posn1",
            "data-state": "off",
            "data-posn": "posn1"
        },
        "events": {
            "click": "uibuilder.eventSend"
        },
        "position": "last"
    }

    htmlClone($('#bulb-template'), $('#floorplan'), {
        id: 'bulb1',
        attributes: {
            class: 'bulb posn1',
            'data-state': 'off',
            'data-posn': 'posn1'
        },
        events: {
            click: 'uibuilder.eventSend'
        }
    })

    htmlClone($('#bulb-template'), $('#floorplan'), {
        id: 'bulb2',
        attributes: {
            class: 'bulb posn2',
            'data-state': 'off',
            'data-posn': 'posn2'
        },
        events: {
            click: 'uibuilder.eventSend'
        }
    })

    htmlClone($('#bulb-template'), $('#floorplan'), {
        id: 'bulb3',
        attributes: {
            class: 'bulb posn3',
            'data-state': 'off',
            'data-posn': 'posn3'
        },
        events: {
            click: 'uibuilder.eventSend'
        }
    })

    htmlClone($('#bulb-template'), $('#floorplan'), {
        id: 'bulb4',
        attributes: {
            class: 'bulb posn4',
            'data-state': 'off',
            'data-posn': 'posn4'
        },
        events: {
            click: 'uibuilder.eventSend'
        },
    })
</script>

I really appreciate your support!
Will add this edits to myself and work with issues.

I think it's clear to me so far. Im really happy that it is moving..
Next I will work on UI design and svg pages of real architecture.

1 Like