Traffic Clock

Here is a prototype to represent time in a traffic signal theme. We will implement this in VTAP using step-by-step to understand designing of components and re-use the same.

Before implementing the application, identifying the data and visual scope make its easy to structure the application. We have time (hour, minute, second) to be displayed as text (left-side) and boxes (right-side).

The boxes further should highlight the elapsed value and remaining value.

So our data structure would be like

elapsed: {
    hour: A,
    minute: B,
    second: C,
}

Box UI can be created using span element:

.box {
    display: inline-block; /* to make it work without content */
    width: 16px;
    height: 16px;
    margin: 2px;
    border: 1px solid black;
}

We will be using VueJS to enable binding the Visual and Data rendering.

Now lets create traffic_clock application in VTAP. Navigate Main Menu > Platform > App Creator > Add App

Fill the Form And Save

Clicking on TrafficClock App will open an editor where you can customise your App

Iteration - 1

Let us first get the timer display on HTML that changes every second.

views/index.html

<!DOCTYPE html>
<html>
    <head>
        <title>Traffic Clock</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="resources/index.css">
    </head>
    <body>
        <div id="app" v-cloak>
            <div class="hour">{{elapsed.hour}}</div>
            <div class="minute">{{elapsed.minute}}</div>
            <div class="second">{{elapsed.second}}</div>
        </div>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
         <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.14/vue.min.js"></script>
        <script src="resources/index.js"></script>
    </body>
</html>

Click Ctrl+S to Save the changes

resources/index.js

window.addEventListener('DOMContentLoaded', function() {
	new Vue({
		el: "#app",
		data: function() {
                                        /* Initialization */
			var d = new Date();
			return {
				elapsed: {
					hour: d.getHours(),
					minute: d.getMinutes(),
					second: d.getSeconds()
				}
			}
		},
		mounted: function() {
			setInterval( () => {
				var d = new Date();
				this.elapsed.hour = d.getHours();
				this.elapsed.minute = d.getMinutes();
				this.elapsed.second = d.getSeconds();
			}, 1000);
		}
	});
});

Click Ctrl+S to Save the changes

Publish the changes. Launch the application.

Iteration - 2

Let us now get the boxes displayed representing elapsed and remaining value of hour, minute, second.

views/index.html

<!DOCTYPE html>
<html>
    <head>
        <title>Traffic Clock</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<link rel="stylesheet" href="resources/index.css">
    </head>
    <body>
        <div id="app" v-cloak>
            <div class="hour">
               {{elapsed.hour}}
               <span class="box hour elapsed" v-for="h in elapsed.hour"></span>
               <span class="box hour" v-for="h in (24 - elapsed.hour)"></span>
            </div>
            <div class="minute">
               {{elapsed.minute}}
               <span class="box minute elapsed" v-for="m in elapsed.minute"></span>
               <span class="box minute" v-for="m in (60 - elapsed.minute)"></span>
            </div>
            <div class="second">
               {{elapsed.second}}
               <span class="box second elapsed" v-for="s in elapsed.second"></span>
               <span class="box second" v-for="s in (60 - elapsed.second)"></span>
             </div>
        </div>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.14/vue.min.js"></script>
        <script src="resources/index.js"></script>
    </body>
</html>

Click Ctrl+S to Save the changes

resources/index.css

.box {
	display: inline-block;
	width: 16px;
	height: 16px;
	margin: 2px;
	border: 1px solid black;
}

.hour.elapsed {
	background: red;
}

.minute.elapsed {
	background: orange;
}

.second.elapsed {
	background: green;
}

Click Ctrl+S to Save the changes

Publish the changes. Launch the application.

Iteration - 3

Let us now build a custom VueJS boxes component which takes care of representing elapsed and remaining value. This will help us reduce HTML repetition as the component can be reused for hour, minute, second by varying the inputs to the component through attribute (type, value and max).

views/index.html

<!DOCTYPE html>
<html>
    <head>
        <title>Traffic Clock</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<link rel="stylesheet" href="resources/index.css">
    </head>
    <body>
        <div id="app" v-cloak>
            <div class="hour">
               {{elapsed.hour}}
               <boxes type="hour" :value="elapsed.hour" :max="24"></boxes>
            </div>
            <div class="minute">
               {{elapsed.minute}}
               <boxes type="minute" :value="elapsed.minute" :max="60"></boxes>
            </div>
            <div class="second">
               {{elapsed.second}}
               <boxes type="second" :value="elapsed.second" :max="60"></boxes>
             </div>
        </div>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.14/vue.min.js"></script>
        <script src="resources/index.js"></script>
    </body>
</html>

Click Ctrl+S to Save the changes

resources/index.js

window.addEventListener('DOMContentLoaded', function() {

    Vue.component("boxes", {
        props: ["type", "value", "max"],
        template: `
            <div style="display:inline-block">
                <span :class="type" class="box elapsed" v-for="v in value"></span>
                <span :class="type" class="box" v-for="v in (max - value)"></span>
            </div>
        `
    });

    new Vue({
        el: "#app",
        data: function() {
            /* Initialization */
            var d = new Date();
            return {
                elapsed: {
                    hour: d.getHours(),
                    minute: d.getMinutes(),
                    second: d.getSeconds()
                }
            }
        },
        mounted: function() {
            setInterval( () => {
                var d = new Date();
                this.elapsed.hour = d.getHours();
                this.elapsed.minute = d.getMinutes();
                this.elapsed.second = d.getSeconds();
            }, 1000);
        }
    });
});

Click Ctrl+S to Save the changes

Publish the changes. Launch the application.

Iteration - 4

Continue the journey to match the expected representation.