Found a couple of really cool simple time pickers

The first one:
https://www.cssscript.com/simplest-time-picker-for-text-field/
A single click to select the hour and
image
a second click to select the minutes.
image
Quite minimal CSS

div.timepicker {
  position: absolute;
  z-index: 100;
}
div.timepicker > ul {
  list-style-type: none;
  margin: 0;
  padding: 0;
  width: 80px;
  border: 1px solid rgba(0, 0, 0, 0.1);
  font-size: 13px;
  max-height: 290px;
  overflow: scroll;
  box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.1);
  background: #fff;
  overflow-x: hidden;
}
div.timepicker > ul > li {
  height: 40px;
  cursor: pointer;
  text-align: center;
  height: 100%;
  line-height: 300%;
  display: block;
  text-decoration: none;
  color: #363636;
}
div.timepicker > ul > li.hover {
  background: #e8edeb;
}

HTML

<input type="text" name="timepicker" data-toggle="timepicker">

js

var timepicker = {
    defaults: {
        interval: 15,
        defaultHour: null,
        defaultMinute: null,
        start: 0,
        end: 24
    },
    options: null,
    time: {
        hour: null,
        minute: null
    },
    active: false,
    source: null,
    load: function(options)
    {
        var self        = this;
        this.options    = options;

        self.each(function(timepicker)
        {
            if (self.option('defaultHour') != null)
            {
                timepicker.value = self.pad(self.option('defaultHour'));
                self.time.hour = self.option('defaultHour');

                if (self.option('defaultMinute') != null)
                {
                    timepicker.value += ':'+ self.pad(self.option('defaultMinute'));
                    self.time.minute = self.option('defaultMinute');
                }
                else
                {
                    timepicker.value += ':00';
                    self.time.minute = 0;
                }
            }

            timepicker.onclick = function()
            {
                self.source = this;

                if (self.active == true)
                    return;

                self.showHour(this, 1, self.option('start'), self.option('end'), function(list, selected)
                {
                    self.time.hour = selected;
                    self.source.value = self.time.hour +':00';

                    self.showMinute(list, self.option('interval'), 60, self.time.hour, function(selected)
                    {
                        self.time.minute = selected;
                        self.source.value = self.time.hour +':'+ self.time.minute;
                    });
                });
            };
        });
    },
    option: function(option)
    {
        if (this.options === null)
            return this.defaults[option];

        if (this.options[option] === undefined)
            return this.defaults[option];

        return this.options[option];
    },
    each: function(callback)
    {   
        var timepickers = document.querySelectorAll('[data-toggle="timepicker"]');

        for (var i = 0; i < timepickers.length; i++)
        {
            callback(timepickers[i]);
        }
    },
    make: function(timepicker)
    {
        this.active = true;
        div         = document.createElement('div');
        ul          = document.createElement('ul');

        ul.setAttribute('data-value', 'null');

        div.setAttribute('class', 'timepicker');
        div.setAttribute('data-name', timepicker.getAttribute('name'));

        div.appendChild(ul);

        timepicker.parentNode.insertBefore(div, timepicker.nextSibling);

        document.addEventListener('mouseup', this.onClickEvent);
        document.addEventListener('keydown', this.onKeyEvents);

        return ul;
    },
    remove: function()
    {
        document.removeEventListener('mouseup', this.onClickEvent);
        document.removeEventListener('keydown', this.onKeyEvents);
        timepicker = document.querySelector('.timepicker');
        timepicker.parentNode.removeChild(timepicker);
        
        this.active = false;
    },
    clear: function(list)
    {
        all = list.querySelectorAll('li');

        for (var i = 0; i < all.length; i++)
            all[i].parentNode.removeChild(all[i]);
    },
    showHour: function(timepicker, interval, min, max, callback)
    {
        var height = null;
        ul         = this.make(timepicker);

        for (var i = min; i < max; i = i + interval)
        {
            li = document.createElement("li");
            li.setAttribute('href', '#');
            li.setAttribute('data-value', this.pad(i));
            li.appendChild(document.createTextNode(this.pad(i) +':00'));
            ul.appendChild(li);

            if (height == null)
                height = li.clientHeight;

            li.onmouseover = function()
            {
                all = document.querySelectorAll('div.timepicker ul li');

                for (var j = 0; j < all.length; j++)
                    all[j].classList.remove('hover');

                this.classList.add('hover');
            };

            li.onmouseout = function()
            {
                this.classList.remove('hover');
            };

            li.onclick = function() {
                callback(ul, this.getAttribute('data-value'));
            };
        }

        if (this.time.hour == null)
            ul.scrollTop = height * this.option('default');
        else
            ul.scrollTop = this.time.hour * height;
    },
    showMinute: function(list, interval, max, hour, callback)
    {
        self = this;
        this.clear(list);

        for (var i = 0; i < max; i = i + interval)
        {
            li = document.createElement("li");
            li.setAttribute('href', '#');
            li.setAttribute('data-value', this.pad(i));
            li.appendChild(document.createTextNode(hour +':'+ this.pad(i)));
            ul.appendChild(li);

            li.onmouseover = function()
            {
                all = document.querySelectorAll('div.timepicker ul li');

                for (var j = 0; j < all.length; j++)
                    all[j].classList.remove('hover');

                this.classList.add('hover');
            };

            li.onmouseout = function()
            {
                this.classList.remove('hover');
            };
            
            li.onclick = function()
            {
                callback(this.getAttribute('data-value'));

                self.remove();
            };
        }
    },
    pad: function(number)
    {
        var s = String(number);

        while (s.length < 2)
        {
            s = '0' + s;
        }

        return s;
    },
    onClickEvent: function()
    {
        if (event.target.attributes['data-value'] === undefined)
            timepicker.remove();
    },
    onKeyEvents: function()
    {
        switch(event.keyCode)
        {
            case 27:
                timepicker.remove();
                break;

            case 40:
                item  = document.querySelector('div.timepicker ul li.hover');
                list  = document.querySelectorAll('div.timepicker ul li');
                index = Array.prototype.indexOf.call(list, item);

                if (index == -1)
                {
                    list[0].classList.add('hover');
                    break;
                }

                if (index < list.length - 1)
                {
                    item.classList.remove('hover');
                    list[index + 1].classList.add('hover');
                }

                break;

            case 38:
                item  = document.querySelector('div.timepicker ul li.hover');
                list  = document.querySelectorAll('div.timepicker ul li');
                index = Array.prototype.indexOf.call(list, item);

                if (index > 0)
                {
                    item.classList.remove('hover');
                    list[index - 1].classList.add('hover');
                }

                break;

            case 13:
                item = document.querySelector('div.timepicker ul li.hover');
                item.click();
                break;
        }
    }
};

The second one:
https://www.cssscript.com/demo/minimal-timer-picker-pure-javascript/
image
CSS

.js-t{
	font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
	position: absolute;
	z-index: 9999;
	top: 200px;
	top: -9999px;
	left: 500px;
	overflow: hidden;
	width: 262px;
	padding: 10px;
	opacity: 0;
	border: 1px solid #ccc;
	border-bottom-color: #bbb;
	-webkit-transition: opacity 300ms ease-out;
	-moz-transition: opacity 300ms ease-out;
	-ms-transition: opacity 300ms ease-out;
	transition: opacity 300ms ease-out;
	background: #fff;
	box-shadow: 0 5px 15px -5px rgba(0,0,0,.5);
}

.js-t.visible{
	overflow: visible;
	opacity: 1;
}

.js-t:after{
	clear: both;
}

.js-t .js-t-hour-container,
.js-t .js-t-minute-container{
	position: relative;
	float: left;
	width: 164px;
	height: 100%;
}

.js-t .js-t-minute-container{
	width: 87px;
	margin-left: 10px;
}

.js-t .js-t-hour,
.js-t .js-t-minute{
	font-size: 14px;
	line-height: 16px;
	float: left;
	width: 21px;
	height: 20px;
	padding: 3px;
	cursor: pointer;
	text-align: center;
}

.js-t .js-t-hour.active,
.js-t .js-t-minute.active,
.js-t .js-t-hour:hover,
.js-t .js-t-minute:hover {
	color: #fff;
	border-radius: 3px;
	background: #ff8000;
}

HTML

<input type="text" name="picker-3" style="position: absolute;    left: 800px;    top: 500px;"/>

JS

(function (root) {

    "use strict";

    /**
     * Common object params
     * @type {Object}
     */
    var common = {
            publicMethods: ['show', 'hide', 'getTime', 'setTime', 'destroy'],
            className: 'JsTimepicker'
        },
        /**
         * Main constructor
         * @return {Object} - this handle
         */
        Protected = function (handle, options) {

            var n;

            this.name = 'js-timepicker' + Math.floor(Math.random() * 99999);

            this.settings = {
                hourLeadingZero: true,
                hourStep: 1,
                minuteLeadingZero: true,
                minuteStep: 5
            };

            //apply options to settings 
            if (options) {
                for (n in options) {
                    if (options.hasOwnProperty(n)) {
                        this.settings[n] = options[n];
                    }
                }
            }

            this.handle = handle;

            this.isVisible = false;
            this.hourArray = this.getHoursArray(this.settings.hourStep, this.settings.hourLeadingZero);
            this.minuteArray = this.getMinutesArray(this.settings.minuteStep, this.settings.minuteLeadingZero);
            this.timepicker = this.makeTimepickerHtml();
            this.setEvents();
            
            return this;
        };



    /**
     * Main prototype
     * @type {Object}
     */
    Protected.prototype = {

        event_outClick: function (e) {
            var self = this,
                parentElem = e.target,
                closeList = true;

            if (this.isVisible && (parentElem !== self.timepicker) && (parentElem !== this.handle)) {

                while (parentElem && parentElem.parentNode) {

                    if (parentElem === self.timepicker) {
                        closeList = false;
                        break;
                    }

                    parentElem = parentElem.parentNode;
                }
                closeList && self.hide();
            }
        },
        event_clickOnPicker: function (e) {

            if (e.target && e.target.getAttribute('data-hour')) {
                this.timepicker.querySelector('[data-hour].active') && this.timepicker.querySelector('[data-hour].active').classList.remove('active');
                e.target.classList.add('active');
                this.setHour(e.target.getAttribute('data-hour'));
                return;
            }

            if (e.target && e.target.getAttribute('data-minute')) {
                this.timepicker.querySelector('[data-minute].active') && this.timepicker.querySelector('[data-minute].active').classList.remove('active');
                e.target.classList.add('active');
                this.setMinute(e.target.getAttribute('data-minute'));
                return;
            }
        },
        setEvents: function () {
            var self = this;
            self.show = this.show.bind(this);
            self.event_outClick = this.event_outClick.bind(this);
            self.event_clickOnPicker = this.event_clickOnPicker.bind(this);
            
            Array.prototype.forEach.call(['click'], function (evt) {
                self.handle.addEventListener(evt, self.show);
            });

            document.addEventListener('click', self.event_outClick);
            this.timepicker.addEventListener('click', self.event_clickOnPicker);
        },
        unsetEvents: function () {
            var self = this;
            Array.prototype.forEach.call(['click'], function (evt) {
                self.handle.removeEventListener(evt, self.show);
            });
            document.removeEventListener('click', self.event_outClick);
            this.timepicker.removeEventListener('click', self.event_clickOnPicker);
        },
        getHoursArray: function (step) {
            var i, h = 24, retData = [];

            step = step || 1;

            for (i = 0; i < h; i += step) {
                retData.push(this.leadingZeroHour(i));
            }

            return retData;
        },
        getMinutesArray: function (step) {
            var i, h = 60, retData = [];

            step = step || 5;

            for (i = 0; i < h; i += step) {
                retData.push(this.leadingZeroMinute(i));
            }

            return retData; 
        },
        getPosition: function (element) {
            var xPosition = 0,
                yPosition = 0;
          
            while(element) {
                xPosition += (element.offsetLeft - element.scrollLeft + element.clientLeft);
                yPosition += (element.offsetTop - element.scrollTop + element.clientTop);
                element = element.offsetParent;
            }

            return {
                x: xPosition,
                y: yPosition
            };
        },
        getElementTime: function (asString) {
            var timeObj = this.parseTime(this.handle.value);
            return (asString ? timeObj.hour + ':' + timeObj.minute : timeObj);
        },
        parseTime: function (timeString) {
            var timeObj = {
                hour: timeString.split(':')[0] || '',
                minute: timeString.split(':')[1] || ''
            }

            timeObj.hour = this.leadingZeroHour(timeObj.hour);
            timeObj.minute = this.leadingZeroMinute(timeObj.minute);
            return timeObj;
        },
        setHour: function (hour) {
            this.handle.value = this.leadingZeroHour(hour) + ':' + this.getElementTime().minute;
        },
        setMinute: function (minute) {
            this.handle.value = this.getElementTime().hour + ':' + this.leadingZeroMinute(minute);
        },
        leadingZeroMinute: function (num) {
            return ((num === '') ? '' : (this.settings.minuteLeadingZero ? ('0' + num.toString()).substr(-2) : num.toString()));
        },
        leadingZeroHour: function (num) {
            return ((num === '') ? '' : (this.settings.hourLeadingZero ? ('0' + num.toString()).substr(-2) : num.toString()));
        },
        validateTime: function (timeString) {
            return new RegExp(/^\d{1,2}:\d{1,2}$/).test(timeString);
        },
        makeTimepickerHtml: function () {

            var container = document.createElement('div'),
                hourContainer = document.createElement('div'),
                minuteContainer = document.createElement('div'),
                elem;

            container.setAttribute('class', 'js-t');
            container.setAttribute('id', this.name + '-' + this.handle.name);
            hourContainer.setAttribute('class', 'js-t-hour-container');
            minuteContainer.setAttribute('class', 'js-t-minute-container');

            Array.prototype.forEach.call(this.hourArray, function (hour) {
                elem = document.createElement('div');
                elem.setAttribute('class', 'js-t-hour');
                elem.setAttribute('data-hour', hour.toString());
                elem.innerHTML = hour;
                hourContainer.appendChild(elem);
            });

            Array.prototype.forEach.call(this.minuteArray, function (minute) {
                elem = document.createElement('div');
                elem.setAttribute('class', 'js-t-minute');
                elem.setAttribute('data-minute', minute.toString());
                elem.innerHTML = minute;
                minuteContainer.appendChild(elem);
            });

            container.appendChild(hourContainer);
            container.appendChild(minuteContainer);

            return container;
            
        },
        show: function () {

            if (this.isVisible) {
                return false;
            }

            document.body.appendChild(this.timepicker);
            
            Array.prototype.forEach.call(this.timepicker.querySelectorAll('.active'), function (activeElem) {
                activeElem.classList.remove('active');
            });

            var handlePos = this.getPosition(this.handle),
                timeObj = this.getElementTime(),
                activeHour = this.timepicker.querySelector('[data-hour="' + timeObj.hour + '"]'),
                activeMinute = this.timepicker.querySelector('[data-minute="' + timeObj.minute + '"]');

            activeHour && activeHour.classList.add('active');
            activeMinute && activeMinute.classList.add('active');
            this.timepicker.style.left = handlePos.x.toString() + 'px';
            this.timepicker.style.top = (handlePos.y + this.handle.offsetHeight).toString() + 'px';
            this.timepicker.classList.add('visible');
            this.isVisible = true;

            return true;
        },
        hide: function () {
 
            if (!this.isVisible) {
                return false;
            }

            !this.validateTime(this.getElementTime(true)) && (this.handle.value = '');
            this.timepicker.classList.remove('visible');
            this.timepicker.style.top = '';
            this.isVisible = false;
            this.timepicker.remove();
            return true;
        },
        destroy: function () {
            this.hide();
            this.unsetEvents();
        },
        getTime: function () {
            return this.getElementTime(true);
        },
        setTime: function (hourOrTimeString, minute) {
            var timeObj = !minute ? this.parseTime(hourOrTimeString) : this.parseTime(hourOrTimeString.toString() + ':' + minute.toString());
            
            // check to step value
            if ((timeObj.minute % this.settings.minuteStep)) {
                console.error('Invalid time. Number does not match the minute step');
                return;
            }
            if ((timeObj.hour % this.settings.hourStep)) {
                console.error('Invalid time. Number does not match the hour step');
                return;
            }

            this.setHour(timeObj.hour);
            this.setMinute(timeObj.minute);
            this.handle.value = timeObj.hour + ':' + timeObj.minute;

            this.isVisible && this.hide() && this.show();
        }

    };
    

    root[common.className] = function () {

        function construct(constructor, args) {

            function Class() {
                return constructor.apply(this, args);
            }
            Class.prototype = constructor.prototype;
            return new Class();
        }

        var original = construct(Protected, arguments),
            Publicly = function () {};
        
        Publicly.prototype = {};
        Array.prototype.forEach.call(common.publicMethods, function (member) {
            Publicly.prototype[member] = function () {
                return original[member].apply(original, arguments);
            };
        });

        return new Publicly(arguments);
    };

}(this));

The HTML and CSS are easily installed with ui_templates, one in the head and the second as Widget in group. I have found that removing most of the css initially allows one to see the input fields and move forwards from there.

I see that the second time picker has an immediate function declaration, but I cannot figure out how to adapt it.

The big problem I find is that help from outside the NR community has little utility because of the unknown constraints that are required for a widget to function in the dashboard (unknown to me and so impossible to explain to others). Within the NR community I have been directed twice to search for answers elsewhere i.e. CSS, html and general web development.

These two examples cannot be much simpler but my attempts at understanding js keep getting undermined by the horrible inconsistenses that exist within the language. Fourty years of real-time embedded C do not help at all infact it is a hinderance.

It really should not be this hard to build a ui_template time picker when you have all the working parts as a starting point.

Please, does any one know how to integrate these simple js pickers into ui_templates to finish the job ?

Not sure if I should reply to myself to show progress ?

Any way I have one of them working - just css to polish but I cannot get it to display above and beyond the confines of the dashboard size settings for the ui_template like the colour picker does or like a pop up dialog.

image image

[{"id":"a1fcc1e6.9be5f","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"e0fac713.df56b8","type":"ui_template","z":"a1fcc1e6.9be5f","group":"b14ff375.57abb","name":"tp-slider","order":11,"width":"2","height":"1","format":"<div>\n<!--diliberately emtpy - only need the script below -->\n</div>\n\n<script type=\"text/javascript\">\nvar tp = {\n    defaults: {\n        interval: 15,\n        defaultHour: null,\n        defaultMinute: null,\n        start: 0,\n        end: 24\n    },\n    options: null,\n    time: {\n        hour: null,\n        minute: null\n    },\n    active: false,\n    source: null,\n    load: function(tp,options)\n    {   // Conversion Note: ///original code\n        var self        = tp;   ///this;\n        tp.options    = options;\n\n        self.each(function(timepicker)\n        {\n            if (self.option('defaultHour') != null)\n            {\n                timepicker.value = self.pad(self.option('defaultHour'));\n                self.time.hour = self.option('defaultHour');\n\n                if (self.option('defaultMinute') != null)\n                {\n                    timepicker.value += ':'+ self.pad(self.option('defaultMinute'));\n                    self.time.minute = self.option('defaultMinute');\n                }\n                else\n                {\n                    timepicker.value += ':00';\n                    self.time.minute = 0;\n                }\n            }\n\n            timepicker.onclick = function()\n            {\n                self.source = this;\n\n                if (self.active == true)\n                    return;\n\n                self.showHour(this, 1, self.option('start'), \n\t\t\t\t\t\t\t\t\t\tself.option('end'), \n\t\t\t\t\t\t\t\t\t\tfunction(list, selected)\n                {\n                    self.time.hour = selected;\n                    self.source.value = self.time.hour +':00';\n\n                    self.showMinute(list, self.option('interval'), 60, self.time.hour, function(selected)\n                    {\n                        self.time.minute = selected;\n                        self.source.value = self.time.hour +':'+ self.time.minute;\n                    });\n                });\n            };\n        });\n    },\n    option: function(option)\n    {\n        if (this.options === null)\n            return this.defaults[option];\n\n        if (this.options[option] === undefined)\n            return this.defaults[option];\n\n        return this.options[option];\n    },\n    each: function(callback)\n    {   \n        var timepickers = document.querySelectorAll('[data-toggle=\"timepicker\"]');\n\n        for (var i = 0; i < timepickers.length; i++)\n        {\n            callback(timepickers[i]);\n        }\n    },\n    make: function(timepicker)\n    {\n        this.active = true;\n        div         = document.createElement('div');\n        ul          = document.createElement('ul');\ndiv.setAttribute(\"z-index\", 9999);\n        ul.setAttribute('data-value', 'null');\n\n        div.setAttribute('class', 'timepicker');\n        div.setAttribute('data-name', timepicker.getAttribute('name'));\n\n        div.appendChild(ul);\n\n        timepicker.parentNode.insertBefore(div, timepicker.nextSibling);\n\n        document.addEventListener('mouseup', this.onClickEvent);\n        document.addEventListener('keydown', this.onKeyEvents);\n\n        return ul;\n    },\n    remove: function()\n    {\n        document.removeEventListener('mouseup', this.onClickEvent);\n        document.removeEventListener('keydown', this.onKeyEvents);\n        timepicker = document.querySelector('.timepicker');\n        timepicker.parentNode.removeChild(timepicker);\n        \n        this.active = false;\n    },\n    clear: function(list)\n    {\n        all = list.querySelectorAll('li');\n\n        for (var i = 0; i < all.length; i++)\n            all[i].parentNode.removeChild(all[i]);\n    },\n    showHour: function(timepicker, interval, min, max, callback)\n    {\n        var height = null;\n        ul         = this.make(timepicker);\n\n        for (var i = min; i < max; i = i + interval)\n        {\n            li = document.createElement(\"li\");\n            li.setAttribute('href', '#');\n            li.setAttribute('data-value', this.pad(i));\n            li.appendChild(document.createTextNode(this.pad(i) +':00'));\n            ul.appendChild(li);\n\n            if (height == null)\n                height = li.clientHeight;\n\n            li.onmouseover = function()\n            {\n                all = document.querySelectorAll('div.timepicker ul li');\n\n                for (var j = 0; j < all.length; j++)\n                    all[j].classList.remove('hover');\n\n                this.classList.add('hover');\n            };\n\n            li.onmouseout = function()\n            {\n                this.classList.remove('hover');\n            };\n\n            li.onclick = function() {\n                callback(ul, this.getAttribute('data-value'));\n            };\n        }\n\n        if (this.time.hour == null)\n            ul.scrollTop = height * this.option('default');\n        else\n            ul.scrollTop = this.time.hour * height;\n    },\n    showMinute: function(list, interval, max, hour, callback)\n    {\n        self = this;\n        this.clear(list);\n\n        for (var i = 0; i < max; i = i + interval)\n        {\n            li = document.createElement(\"li\");\n            li.setAttribute('href', '#');\n            li.setAttribute('data-value', this.pad(i));\n            li.appendChild(document.createTextNode(hour +':'+ this.pad(i)));\n            ul.appendChild(li);\n\n            li.onmouseover = function()\n            {\n                all = document.querySelectorAll('div.timepicker ul li');\n\n                for (var j = 0; j < all.length; j++)\n                    all[j].classList.remove('hover');\n\n                this.classList.add('hover');\n            };\n\n            li.onmouseout = function()\n            {\n                this.classList.remove('hover');\n            };\n            \n            li.onclick = function()\n            {\n                callback(this.getAttribute('data-value'));\n\n                self.remove();\n            };\n        }\n    },\n    pad: function(number)\n    {\n        var s = String(number);\n\n        while (s.length < 2)\n        {\n            s = '0' + s;\n        }\n\n        return s;\n    },\n    onClickEvent: function()\n    {\n        if (event.target.attributes['data-value'] === undefined)\n            timepicker.remove();\n    },\n    onKeyEvents: function()\n    {\n        switch(event.keyCode)\n        {\n            case 27:\n                timepicker.remove();\n                break;\n\n            case 40:\n                item  = document.querySelector('div.timepicker ul li.hover');\n                list  = document.querySelectorAll('div.timepicker ul li');\n                index = Array.prototype.indexOf.call(list, item);\n\n                if (index == -1)\n                {\n                    list[0].classList.add('hover');\n                    break;\n                }\n\n                if (index < list.length - 1)\n                {\n                    item.classList.remove('hover');\n                    list[index + 1].classList.add('hover');\n                }\n\n                break;\n\n            case 38:\n                item  = document.querySelector('div.timepicker ul li.hover');\n                list  = document.querySelectorAll('div.timepicker ul li');\n                index = Array.prototype.indexOf.call(list, item);\n\n                if (index > 0)\n                {\n                    item.classList.remove('hover');\n                    list[index - 1].classList.add('hover');\n                }\n\n                break;\n\n            case 13:\n                item = document.querySelector('div.timepicker ul li.hover');\n                item.click();\n                break;\n        }\n    }\n};    \n\n(function($scope) {\n//debugger\n\n    //cause a small delay while things load \n    //ideally this would be an init event or on all parts of document loaded\n    //(may not be necessary!)\n    setTimeout(function() {\n        //debugger\n        $scope.init();\n    },100);\n    \n    //SETTINGS...\n    var TIME_CLASS = \".ck-time\";\n    \n    /** \n     * Initialise all buttons with class BUTTON_CLASS\n    */\n    $scope.init = function () {\n        //debugger\n        console.log(\"$scope.init called. Adding event handlers to all elements with class '\" + TIME_CLASS + \"'.\");\n        var clickTimePickers = $(TIME_CLASS) \n        if(tp.source == null && clickTimePickers.length>0){\n            //debugger\n            //  console.log(\"Collecting state:\" + clickButtons.length);\n            //    colour_pickers=clickColours;\n            //queryAll(timepicker,\"_init_\",clickTimePickers);\n            console.log(\"calling tp.load\");\n//            tp.time.hour=6;\n//            tp.time.minute=30;\n            tp.load(tp,{});\n            $scope.send(tp.time);\n        } \n/*\nvar reference = (function thename(){\n    //function body\n    return thename; //return the function itself to reference\n}()); //auto-run\nreference(); //call it again\nreference(); //and again\n*/\n        clickTimePickers.click(function(e){\n            if(e.target.nodeName !== \"INPUT\") {\n                // drop click events for other elements (.on and .off).\n                return;\n            tp.source = $(this)\n            if(tp.active==true) \n                return;\n            console.log(\"tp-click\")\n            tp.showHour(tp.source, 1,tp.option.start,tp.option.end,\n                function(list, selected)\n                {\n                    tp.time.hour = selected;\n                    tp.source.value = tp.time.hour +':00';\n\n                    tp.showMinute(list, tp.option('interval'), 60, tp.time.hour, function(selected)\n                    {\n                        tp.time.minute = selected;\n                        tp.source.value = tp.time.hour +':'+ tp.time.minute;\n                    });\n                });\n            \n            return;\n            }\n        });\n    };\n})(scope);\n    \n\n</script>\n<style>\n    \n</style>\n\n<input style=\"z-index: 1;\" class=\"ck-time\" type=\"text\" name=\"timepicker\" data-toggle=\"timepicker\">\n\n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":160,"y":180,"wires":[[]]},{"id":"778ad9bf.fb32b8","type":"ui_template","z":"a1fcc1e6.9be5f","group":"65bb6bb.2b6e094","name":"ck-time CSS - SCROLL","order":1,"width":0,"height":0,"format":"/!--\n\n-->\n<style>\n/* Scroll type time picker */\ndiv.timepicker {\n  position: absolute;\n/*  \nposition:relative;\nfloat:left;\n*/\nz-index: 9999;\nleft:50%;\noverflow-x: hidden;\n}\ndiv.timepicker > ul {\n\n  list-style-type: none;\n  margin: 0;\n  padding: 0;\n/*  width: 80px;*/\nwidth:100%;\n  border: 1px solid rgba(0, 0, 0, 0.1);\n  font-size: 13px;\n  max-height: 290px;\n  overflow: scroll;\n  box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.1);\n  background: #fff;\n  overflow-x: hidden;\n}\ndiv.timepicker > ul > li {\n\n  height: 40px;\n  cursor: pointer;\n  text-align: center;\n  height: 100%;\n/*  line-height: 300%;*/\nline-height: 110%;\n  display: block;\n  text-decoration: none;\n  color: #363636;\n}\ndiv.timepicker > ul > li.hover {\n  background: #eeeeee;\n}\n\n</style>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":false,"templateScope":"global","x":210,"y":140,"wires":[[]]},{"id":"b14ff375.57abb","type":"ui_group","name":"Time picker - scroll","tab":"485a5637.1779c8","order":2,"disp":true,"width":"4","collapse":true},{"id":"65bb6bb.2b6e094","type":"ui_group","name":"ck button - dashboard@2.24.1","tab":"485a5637.1779c8","order":1,"disp":true,"width":"4","collapse":false},{"id":"485a5637.1779c8","type":"ui_tab","name":"Pad Port 1999","icon":"dashboard","order":1,"disabled":false,"hidden":false}]

Any ideas here ?

Hey @ozpos have you tried just using the browsers built in time picker?

the html is...

<input type="time" id="mytime" >

looks like this in chrome desktop...
WPs071cegM

And looks like this in a mobile...

Demo flow that sends the selected value back to node-red...

image

[{"id":"8e64b44c.2f7cc8","type":"ui_template","z":"42ea7bd7.2e3c24","group":"dce9e7a2.d20c78","name":"","order":5,"width":"7","height":"2","format":"<div>\n    <input type=\"time\" id=\"timeFrom\" class=\"timepicker\" >\n    <input type=\"time\" id=\"timeTo\" class=\"timepicker\" >\n</div>\n\n<script>\n(function(scope) {\n    var timepicker = $(\".timepicker\")\n    timepicker.on('change', function(evt) {\n        scope.send({ topic: this.id, payload: this.value });\n    });\n})(scope);\n</script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":420,"y":580,"wires":[["cc14d877.0648c8"]]},{"id":"e1f2906e.5967f","type":"debug","z":"42ea7bd7.2e3c24","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":750,"y":557,"wires":[]},{"id":"cc14d877.0648c8","type":"switch","z":"42ea7bd7.2e3c24","name":"","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"timeFrom","vt":"str"},{"t":"eq","v":"timeTo","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":570,"y":580,"wires":[["e1f2906e.5967f"],["77d33f4b.d5cd7"]]},{"id":"77d33f4b.d5cd7","type":"debug","z":"42ea7bd7.2e3c24","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":750,"y":604,"wires":[]},{"id":"dce9e7a2.d20c78","type":"ui_group","name":"Object detection","tab":"5132060d.4cde48","order":1,"disp":true,"width":"7","collapse":false},{"id":"5132060d.4cde48","type":"ui_tab","name":"Home","icon":"dashboard","disabled":false,"hidden":false}]
3 Likes

Won't do anything nice in IE though :rofl:

1 Like

was about to kick off there till I seen the smillie haha

Anyone not corporate, that is still using IE, deserves nothing anyhow :grin:

That's why we can't have nice things!

Actually, I've been on a campaign to get Chromium Edge as our default browser. Nearly there now, in final UAT. My next browser campaign will be to eliminate Chrome altogether as well. Of course, IE stays in the background as a fallback for legacy sites. There are still a few that need it - you know unimportant things like the Finance system that handles ÂŁ150bn a year!

1 Like

Even with IE excluded from equation, the look and feel can never adjusted to match with your design.

I hear you. lets not forget silverlight in SC. We also have a Flash based web service that was bought and installed only a few years ago. Die IE, DIE!

Oh yes. Flash - found out today that the Department of Health are still building training courses in Flash - just released a bunch of new ones - on the same day we announced its imminent removal from our managed desktop environment. -sigh-

I will give it a try, thank you.

I would like to set the minutes interval differently.

Is this chromes implementation of html5 time type ?

Hi @hotNipi thanks for your input.

Are you saying that CSS cannot be added to blend in the picker with dashboard widgets ?

Edit: Silly me, must be bed time. The browser I suppose you mean ?

Sorry I seem to be a little out of sync. with replies.

That looks better but I cannot quite make out all the CSS fixes you have made.

Could you please post the flow.

No wonder the NHS is always asking for more money. :money_mouth_face:

For HTML input element you can't adjust too much with CSS. For custom made stuff, you are in charge.
Creating the custom stuff for Node-RED dashboard, you are building it into ui_template and that comes with some rules.
So your question was -

The image I posted was just reference from where the rule is coming - the .nr-dashboard-template class which sets the overflow-y property to be 'auto'.
I didn't do any customization's but just turned the rule off using the browser developer tools. Just for making the image - to show you the reason.

F.Y.I. it doesn't work in Safari
Screen Shot 2020-12-01 at 5.27.49 AM
or FireFox
Screen Shot 2020-12-01 at 5.27.34 AM
although in FireFox you can type in the information, just not 'pick' it
Screen Shot 2020-12-01 at 5.29.01 AM

Unsurprising :wink:

A system I maintain (that must work on IE and other rubbish browsers) uses foundation-datepicker.js it works very well across the various browsers we use (demo)

1 Like

Why is it that these browsers cannot render basic html input elements ?

For NR + debugging with embedded apps on pi what other browsers apart from chromium are relevant ?

Hi again.

This does not work on buster with chromium on my pi. Just like the built in time input node works on the desktop but falls back to 1 line scroll box.

Another requirement I have is for 1x1 dashboard grid footprint.
Making progress with the second table type popup.
image

Not that pretty but it works on a pi touch screen and this is not a main feature for me. More just an efficient way of editing more volatile settings.