How to create slider in dashboard template

Old topic, but I have got my sliders to work within the UI template node.

I have some Shelly RGBW2 led controllers which I control using NodeRED. I have created a UI which controls the different RGB and White channels. So I treat the RGB as mixed and the White channel is a separate light source. I stole the UI of the iOS color picker. It looks nice and works as expected in the Safari (MacOS) browser.

My NodeRED implementation:
Slider Demo

The html angular code:

<style>
.slidecontainer {
	/* font-size: 80%; */
	width: 100%;
}
.slidecontainer input {
	margin-bottom:12px;
}
/* slider bar */
.slider {
  -webkit-appearance: none;
  width: 98%;
  height: 36px;
  border-radius: 18px;
  outline: none;
  padding: 0px 2px;
}

/* per color slides */
.red {
  background: linear-gradient(90deg, rgba(0,{{msg.payload.green}},{{msg.payload.blue}},1) 0%, rgba(255,{{msg.payload.green}},{{msg.payload.blue}},1) 100%);
}
.green {
  background: linear-gradient(90deg, rgba({{msg.payload.red}},0,{{msg.payload.blue}},1) 0%, rgba({{msg.payload.red}},255,{{msg.payload.blue}},1) 100%);
}
.blue {
  background: linear-gradient(90deg, rgba({{msg.payload.red}},{{msg.payload.green}},0,1) 0%, rgba({{msg.payload.red}},{{msg.payload.green}},255,1) 100%);
}
.gain {
  background: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba({{msg.payload.red}},{{msg.payload.green}},{{msg.payload.blue}},1) 100%);
}
.white {
  background: linear-gradient(90deg, rgba({{msg.payload.red}},{{msg.payload.green}},{{msg.payload.blue}},{{msg.payload.gain/100}}) 0%, rgba(253,253,240,1) 100%);
}

/* slider knob */
.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: rgba({{msg.payload.red}},{{msg.payload.green}},{{msg.payload.blue}},1);
  border: 2px solid white;
  cursor: pointer;
  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.white.slider::-webkit-slider-thumb {
  background: rgba(253,253,240,1);
}

/* 
	AAN / UIT knop
	er is een transitie van de knop
	- de knop is 26 pixel breed
	- dus de knop moet van links naar rechts bewegen
	- de hoeveelheid die de knop moet bewegen is de totale breedte min de breedte van de knop
*/

.switch {
  position: relative;
  display: inline-block;
  width: 72px;
  height: 36px;
  appearance: none;
  border: none;
  cursor: pointer;
}
/* remove original */
.switch input { 
  opacity: 0;
  width: 0;
  height: 0;
}

/* button background */
.button {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #ccc;
  -webkit-transition: .1s;
  transition: .1s;
}

/* button */
.button:before {
  -webkit-appearance: none;
  appearance: none;
    position: absolute;
  content: "";
  margin:1px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 2px solid white;
  cursor: pointer;
  background: rgba(253,253,240,0.6);
    -webkit-transition: .1s;
  transition: .1s;
}
/* button on */
input + .button {
    border:1px solid white;
}
input:checked + .button {
  background-color: rgba({{msg.payload.red}},{{msg.payload.green}},{{msg.payload.blue}},{{msg.payload.gain/100}});
}

/* transition to right */
input:checked + .button:before {
  -webkit-transform: translateX(36px);
  -ms-transform: translateX(36px);
  transform: translateX(36px);
}

/* Rounded input */
.button.round {
  border-radius: 36px;
}
/* layout on off */
.onoff {
  overflow: auto;
}
.onoff label{
  float: right;
}
</style>

<div class="slidecontainer">
    <div class="onoff">
        {{msg.payload.title}}
        <label class="switch"> <input type="checkbox"><span class="button round"></span></label>
    </div>


	Red ({{msg.payload.red}})
	<input type="range" min="0" max="255" value="{{msg.payload.red}}" class="slider red" ng-model="msg.payload.red" ng-mouseup="send(msg)">
	Green ({{msg.payload.green}})
	<input type="range" min="0" max="255" value="{{msg.payload.green}}" class="slider green" ng-model="msg.payload.green" ng-mouseup="send(msg)">
	Blue ({{msg.payload.blue}})
	<input type="range" min="0" max="255" value="{{msg.payload.blue}}" class="slider blue" ng-model="msg.payload.blue" ng-mouseup="send(msg)">
	Gain ({{msg.payload.gain}})
	<input type="range" min="0" max="100" value="{{msg.payload.gain}}" class="slider gain" ng-model="msg.payload.gain" ng-mouseup="send(msg)">

    
    White ({{msg.payload.white}})
	<input type="range" min="0" max="255" value="{{msg.payload.white}}" class="slider white" ng-model="msg.payload.white" ng-mouseup="send(msg)">
</div>

The node-red code:

[
    {
        "id": "2ffcec6.78cdf94",
        "type": "inject",
        "z": "4d2d578e.6cea28",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": "1",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 335,
        "y": 2440,
        "wires": [
            [
                "c9995b07.1f14f"
            ]
        ],
        "l": false
    },
    {
        "id": "c9995b07.1f14f",
        "type": "function",
        "z": "4d2d578e.6cea28",
        "name": "inject",
        "func": "\n// create a random colour\nmsg.payload = { \"title\":\"Demo\",\n                \"red\": Math.floor(Math.random() * (255) ),\n                \"green\": Math.floor(Math.random() * (255) ),\n                \"blue\": Math.floor(Math.random() * (255) ),\n                \"gain\": Math.floor(Math.random() * 101),\n                \"white\": Math.floor(Math.random() * 255)\n}\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 450,
        "y": 2440,
        "wires": [
            [
                "1467f9da.58d96e"
            ]
        ]
    },
    {
        "id": "1467f9da.58d96e",
        "type": "ui_template",
        "z": "4d2d578e.6cea28",
        "group": "fb4c245.8e22cd8",
        "name": "RGB & White sliders",
        "order": 2,
        "width": "0",
        "height": "0",
        "format": "<style>\n.slidecontainer {\n\t/* font-size: 80%; */\n\twidth: 100%;\n}\n.slidecontainer input {\n\tmargin-bottom:12px;\n}\n/* slider bar */\n.slider {\n  -webkit-appearance: none;\n  width: 98%;\n  height: 36px;\n  border-radius: 18px;\n  outline: none;\n  padding: 0px 2px;\n}\n\n/* per color slides */\n.red {\n  background: linear-gradient(90deg, rgba(0,{{msg.payload.green}},{{msg.payload.blue}},1) 0%, rgba(255,{{msg.payload.green}},{{msg.payload.blue}},1) 100%);\n}\n.green {\n  background: linear-gradient(90deg, rgba({{msg.payload.red}},0,{{msg.payload.blue}},1) 0%, rgba({{msg.payload.red}},255,{{msg.payload.blue}},1) 100%);\n}\n.blue {\n  background: linear-gradient(90deg, rgba({{msg.payload.red}},{{msg.payload.green}},0,1) 0%, rgba({{msg.payload.red}},{{msg.payload.green}},255,1) 100%);\n}\n.gain {\n  background: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba({{msg.payload.red}},{{msg.payload.green}},{{msg.payload.blue}},1) 100%);\n}\n.white {\n  background: linear-gradient(90deg, rgba({{msg.payload.red}},{{msg.payload.green}},{{msg.payload.blue}},{{msg.payload.gain/100}}) 0%, rgba(253,253,240,1) 100%);\n}\n\n/* slider knob */\n.slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 32px;\n  height: 32px;\n  border-radius: 50%;\n  background: rgba({{msg.payload.red}},{{msg.payload.green}},{{msg.payload.blue}},1);\n  border: 2px solid white;\n  cursor: pointer;\n  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);\n}\n.white.slider::-webkit-slider-thumb {\n  background: rgba(253,253,240,1);\n}\n\n/* \n\tAAN / UIT knop\n\ter is een transitie van de knop\n\t- de knop is 26 pixel breed\n\t- dus de knop moet van links naar rechts bewegen\n\t- de hoeveelheid die de knop moet bewegen is de totale breedte min de breedte van de knop\n*/\n\n.switch {\n  position: relative;\n  display: inline-block;\n  width: 72px;\n  height: 36px;\n  appearance: none;\n  border: none;\n  cursor: pointer;\n}\n/* remove original */\n.switch input { \n  opacity: 0;\n  width: 0;\n  height: 0;\n}\n\n/* button background */\n.button {\n  position: absolute;\n  cursor: pointer;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  background-color: #ccc;\n  -webkit-transition: .1s;\n  transition: .1s;\n}\n\n/* button */\n.button:before {\n  -webkit-appearance: none;\n  appearance: none;\n    position: absolute;\n  content: \"\";\n  margin:1px;\n  width: 28px;\n  height: 28px;\n  border-radius: 50%;\n  border: 2px solid white;\n  cursor: pointer;\n  background: rgba(253,253,240,0.6);\n    -webkit-transition: .1s;\n  transition: .1s;\n}\n/* button on */\ninput + .button {\n    border:1px solid white;\n}\ninput:checked + .button {\n  background-color: rgba({{msg.payload.red}},{{msg.payload.green}},{{msg.payload.blue}},{{msg.payload.gain/100}});\n}\n\n/* transition to right */\ninput:checked + .button:before {\n  -webkit-transform: translateX(36px);\n  -ms-transform: translateX(36px);\n  transform: translateX(36px);\n}\n\n/* Rounded input */\n.button.round {\n  border-radius: 36px;\n}\n/* layout on off */\n.onoff {\n  overflow: auto;\n}\n.onoff label{\n  float: right;\n}\n</style>\n\n<div class=\"slidecontainer\">\n    <div class=\"onoff\">\n        {{msg.payload.title}}\n        <label class=\"switch\"> <input type=\"checkbox\"><span class=\"button round\"></span></label>\n    </div>\n\n\n\tRed ({{msg.payload.red}})\n\t<input type=\"range\" min=\"0\" max=\"255\" value=\"{{msg.payload.red}}\" class=\"slider red\" ng-model=\"msg.payload.red\" ng-mouseup=\"send(msg)\">\n\tGreen ({{msg.payload.green}})\n\t<input type=\"range\" min=\"0\" max=\"255\" value=\"{{msg.payload.green}}\" class=\"slider green\" ng-model=\"msg.payload.green\" ng-mouseup=\"send(msg)\">\n\tBlue ({{msg.payload.blue}})\n\t<input type=\"range\" min=\"0\" max=\"255\" value=\"{{msg.payload.blue}}\" class=\"slider blue\" ng-model=\"msg.payload.blue\" ng-mouseup=\"send(msg)\">\n\tGain ({{msg.payload.gain}})\n\t<input type=\"range\" min=\"0\" max=\"100\" value=\"{{msg.payload.gain}}\" class=\"slider gain\" ng-model=\"msg.payload.gain\" ng-mouseup=\"send(msg)\">\n\n    \n    White ({{msg.payload.white}})\n\t<input type=\"range\" min=\"0\" max=\"255\" value=\"{{msg.payload.white}}\" class=\"slider white\" ng-model=\"msg.payload.white\" ng-mouseup=\"send(msg)\">\n</div>",
        "storeOutMessages": true,
        "fwdInMessages": false,
        "resendOnRefresh": true,
        "templateScope": "local",
        "x": 660,
        "y": 2440,
        "wires": [
            [
                "69180f62.347ca8"
            ]
        ]
    },
    {
        "id": "fb4c245.8e22cd8",
        "type": "ui_group",
        "name": "Slider demo",
        "tab": "9cb34fc7.ab81b8",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "9cb34fc7.ab81b8",
        "type": "ui_tab",
        "name": "Test",
        "icon": "dashboard",
        "order": 17,
        "disabled": false,
        "hidden": false
    }
]

The original iOS UI looks like this:

3 Likes