heights of the groups are different and I need no gap between groups
flow
[
{
"id": "51ef46c8e45f5993",
"type": "tab",
"label": "Koemittarit",
"disabled": false,
"info": "",
"env": []
},
{
"id": "9ccc34f8f12f8d82",
"type": "ui-template",
"z": "51ef46c8e45f5993",
"group": "48106afca278ac11",
"page": "",
"ui": "",
"name": "T1",
"order": 4,
"width": "3",
"height": "3",
"head": "",
"format": "<template>\n <div class=\"hn-sng\">\n <div class=\"label\">{{label}}</div>\n <svg ref=\"hn-gauge\" width=\"100%\" height=\"100%\" viewBox=\"0 0 100 100\">\n <g>\n <path v-for=\"(item, index) in sectors\" :key=\"index\" :ref=\"'sector-' + index\" class=\"sector\" stroke-width=\"5\" d=\"M 10 90 A 47.5 47.5 25 1 1 90 90\"></path> \n </g>\n <g>\n <path class=\"tick-minor\" stroke-width=\"5\" d=\"M 10 90 A 47.5 47.5 25 1 1 90 90\"></path>\n <path ref=\"arc\" class=\"tick-major\" stroke-width=\"5\" d=\"M 10 90 A 47.5 47.5 25 1 1 90 90\"></path>\n </g> \n <g>\n <text v-for=\"(item, index) in numbers\" :key=\"index\" class=\"num\" text-anchor=\"middle\" y=\"-37\" :style=\"`rotate: ${item.r}deg;`\">{{item.n}}</text>\n </g>\n <g>\n <text class=\"measurement\" y=\"48\" x=\"50%\" text-anchor=\"middle\">{{measurement}}</text>\n <text class=\"unit\" y=\"75\" x=\"50%\" text-anchor=\"middle\">{{unit}}</text>\n <text class=\"value\" y=\"90\" x=\"50%\" text-anchor=\"middle\">{{formattedValue}}</text>\n <text v-if=\"showUpdate\" ref=\"update\" class=\"update\" y=\"100\" x=\"0\" text-anchor=\"left\">{{formattedUpdate}}</text>\n </g>\n <g ref=\"o-needle\" class=\"o-needle\">\n <path d=\"M 0,0 -1.5,0 -0.15,-40 0.15,-40 1.5,0 z\"></path>\n <circle cx=\"0\" cy=\"0\" r=\"3\"></circle>\n </g>\n </svg>\n </div>\n</template>\n<script>\nexport default{\n data(){\n return {\n //define me here\n min:0,\n max:100,\n unit:\"°C\",\n label:\"T1\",\n measurement:\"Lämpötila\",\n //updateLabel:\"Last update:\",\n sectors:[{start:0,end:40,color:\"lightblue\"},{start:40,end:54,color:\"orange\"},{start:54,end:80,color:\"#63cc25\"},{start:80,end:100,color:\"red\"}],\n //showUpdate:true,\n //no need to change\n value:0,\n updateTime:0 \n }\n },\n methods:{\n getElement: function(name,base){\n if(base){\n return this.$refs[name]\n }\n return this.$refs[name][0]\n },\n validate: function(data){\n let ret \n if(typeof data !== \"number\"){\n ret = parseFloat(data)\n if(isNaN(ret)){\n console.log(\"BAD DATA! gauge id:\",this.id,\"data:\",data)\n return null\n }\n } \n else{\n ret = data\n } \n return ret\n },\n range:function (n, p, r) { \n if (n < p.minIn) {\n n=p.minIn;\n }\n if (n > p.maxIn) {\n n = p.maxIn;\n }\n if(r){\n return Math.round(((n - p.minIn) / (p.maxIn - p.minIn) * (p.maxOut - p.minOut)) + p.minOut);\n }\n return ((n - p.minIn) / (p.maxIn - p.minIn) * (p.maxOut - p.minOut)) + p.minOut;\n },\n generateNumbers:function(min,max){ \n const nums = [{r:238,n:0},{r:262,n:0},{r:286,n:0},{r:310,n:0},{r:335,n:0},{r:360,n:0},{r:385,n:0},{r:409,n:0},{r:434,n:0},{r:458,n:0},{r:123,n:0}];\n let t = (max - min) / 10;\n let i = min;\n for (let e = 0; e < 11; ++e){\n nums[e].n = i\n i = parseFloat((i + t).toFixed(2)); \n }\n return nums \n },\n sectorData:function(full){ \n let params, pos, span, sec, str\n let ret = [] \n this.sectors.forEach((sector,idx) => {\n sec = {name:'sector-'+idx,color:sector.color}\n params = {minIn:this.min, maxIn:this.max, minOut:0, maxOut:full} \n pos = this.range(sector.start,params,false)\n params = {minIn:0, maxIn:this.max-this.min, minOut:0, maxOut:full}\n span = this.range(sector.end - sector.start,params,false) \n sec.css = \"0 \"+pos+\" \"+span+\" var(--dash)\"\n ret.push(sec)\n })\n return ret\n },\n rotation:function(v){\n const params = {minIn:this.min, maxIn:this.max, minOut:-123, maxOut:123};\n return this.range(v,params,false) +'deg' \n }\n\n },\n watch: {\n msg: function(){\n if(this.msg.payload != undefined){ \n const v = this.validate(this.msg.payload) \n if(v == null){\n return\n }\n this.value = v\n this.updateTime = Date.now() \n this.getElement('o-needle',true).style.rotate = this.rotation(this.value) \n }\n }\n },\n computed: {\n formattedValue: function () {\n return this.value.toFixed(1)\n },\n formattedUpdate:function (){\n return this.updateLabel+\" \"+this.time\n },\n time:function(){\n return new Intl.DateTimeFormat('default', {hour: 'numeric', minute: 'numeric',second: 'numeric'}).format(this.updateTime);\n },\n numbers:function(){\n return this.generateNumbers(this.min,this.max)\n } \n },\n mounted(){\n \n const dal = this.getElement('arc',true).getTotalLength()\n const sec = this.sectorData(dal) \n const gauge = this.getElement('hn-gauge',true)\n gauge.style.setProperty('--dash',dal)\n \n sec.forEach(s =>{\n const sector = this.getElement(s.name,false)\n sector.style.setProperty(\"stroke-dasharray\",s.css)\n sector.style.setProperty(\"stroke\",s.color)\n }) \n\n }\n\n }\n</script>\n<style>\n.hn-sng{\nposition:relative;\n}\n.hn-sng .label{\nposition:absolute;\nfont-size:1rem;\ncolor:currentColor;\ntext-align:center;\nwidth:100%;\noverflow: hidden;\nwhite-space: nowrap;\ntext-overflow: ellipsis;\n}\n.hn-sng .value {\nfill:currentColor;\n//fill:red;\nfont-weigth:600;\n}\n.hn-sng .unit {\nfill:currentColor;\nfont-size:0.4rem;\n}\n.hn-sng .measurement {\nfill:currentColor;\nfont-size:0.5rem;\n}\n.hn-sng .update {\nfill:currentColor;\nfill-opacity:0.6;\nfont-size:0.35rem;\n}\n.hn-sng .num{\ntransform-origin: center 64%;\ntransform: translate(50%, 64%);\nfill:currentColor;\nfill-opacity:0.6;\nfont-size:.35rem;\n}\n.hn-sng .tick-minor{\nstroke-dasharray: 0.5 0 0 3.53;\nstroke-dashoffset:-0.5;\nfill:none;\nstroke:currentColor;\nstroke-opacity:0.6;\n}\n.hn-sng .tick-major{\nstroke-dasharray: 2 0 0 18.1;\nfill:none;\nstroke:currentColor;\n}\n.hn-sng .sector{\nfill:none;\nstroke:transparent;\n}\n.hn-sng .o-needle{\ntransform-origin: center 64%;\ntransform: translate(50%, 64%);\ntransition:rotate .5s;\n}\n.hn-sng .o-needle path, .hn-sng .o-needle circle{\nfill:currentColor;\n//fill:red;\n}\n</style>",
"storeOutMessages": true,
"passthru": true,
"resendOnRefresh": true,
"templateScope": "local",
"className": "",
"x": 650,
"y": 80,
"wires": [
[]
]
},
{
"id": "1f97fc8ae5645bd0",
"type": "ui-template",
"z": "51ef46c8e45f5993",
"group": "1e0364d1c4ba9e0d",
"page": "",
"ui": "",
"name": "Salmonella",
"order": 2,
"width": "3",
"height": "1",
"head": "",
"format": "<template>\n <div ref=\"hng\" :class=\"icon ? 'ag-wrapper-2' : 'ag-wrapper-1'\" :style=\"`--line-color:${colors[0]};`\">\n <div v-if=\"icon\" class=\"ag-icon\">\n <v-icon aria-hidden=\"false\">{{icon}}</v-icon>\n </div>\n <div class=\"ag-content\">\n <div class=\"ag-text\">\n <span class=\"ag-label\">{{label}}</span>\n <span class=\"ag-value\">{{formattedValue}}<span class=\"ag-unit\">{{unit}}</span></span>\n </div>\n <div class=\"ag-track\" ref=\"agLine\">\n <div class=\"ag-track-background\"></div>\n <div class=\"ag-track-foreground\" :style=\"{'width': linesize +'%'}\"></div>\n </div>\n <div class=\"ag-limits\">\n <span class=\"ag-min\">{{min}}</span>\n <span class=\"ag-max\">{{max}}</span>\n </div>\n </div>\n </div>\n</template>\n\n<script>\n export default {\n data(){\n return {\n //Define me here\n \n label:\"Salmonella\", // The label\n //icon:\"mdi-account\", // (type: artless) (optional) the icon\n zeroCross:false,// (type: artless) line changes color depending on value being positive or negative (at least 2 colors must be defined)\n min:0, // Smallest expected value\n max:8, // Highest expected value\n unit:\"log10\",// The unit of the measurement \n animate:true, // Animating led's is not most performant thing in the world. \n \n // Define colors \n colors:[\n \"red\",\n \"red\", \n \"red\",\n \"red\", \n \"orange\",\n \"orange\",\n \"orange\",\n \"orange\",\n \"#0fb60f\"\n ], \n \n //no need to change those\n value:0, \n inited:false\n }\n },\n\n\n \n methods: { \n getElement: function(name,base){ \n if(base){\n return this.$refs[name]\n }\n return this.$refs[name][0]\n },\n validate(data){\n let ret\n if(typeof data !== \"number\"){\n ret = parseFloat(data)\n if(isNaN(ret)){\n console.log(\"BAD DATA! gauge type:\",this.type, \"id:\",this.id,\"data:\",data)\n return null\n } \n }\n else{\n ret = data\n } \n return ret\n },\n changeLine:function(){\n const line = this.getElement(\"agLine\",true);\n if(!line){\n console.log(\"no line found\")\n return \n }\n \n let c = Math.floor(this.colors.length * this.percentage / 100)\n if(c >= this.colors.length){\n c = this.colors.length - 1\n }\n if(c < 0){\n c = 0\n }\n if(this.zeroCross){\n c = this.value > 0 ? (this.colors.length - 1) : 0\n }\n line.style.setProperty('--line-color',this.colors[c])\n\n }\n },\n \n watch: {\n msg: function(){ \n if(this.msg.payload !== undefined){ \n const v = this.validate(this.msg.payload) \n if(v === null){\n return\n } \n this.value = v\n this.changeLine() \n }\n }\n },\n computed: {\n formattedValue: function () {\n return this.value.toFixed(2)\n },\n percentage: function(){\n return Math.floor(((this.value - this.min) / (this.max - this.min)) * 100);\n },\n linesize:function(){\n if(this.zeroCross){\n return Math.floor(((Math.abs(this.value) - this.min) / (this.max - this.min)) * 100); \n }\n else{\n return Math.max(0,this.percentage)\n }\n }\n },\n mounted(){\n const line = this.getElement(\"agLine\",true);\n line.style.setProperty('--line-color',this.colors[0])\n if(this.animate == true){\n if(!line){\n console.log(\"artless init() no line found\")\n return\n }\n line.style.transition = \"width 0.5s\";\n }\n \n this.inited = true;\n }\n}\n</script>\n\n<style>\n.ag-wrapper-2 {\ndisplay: grid;\ngrid-template-columns: 3em 1fr;\ngap:1em;\n}\n.ag-wrapper-1 {\ndisplay: grid;\ngrid-template-columns: 1fr;\n}\n.ag-icon{\nfont-size: 2em;\ndisplay: flex;\nflex-direction: column;\njustify-content: center;\n}\n.ag-content{\ndisplay: grid;\ngrid-template-rows: 1fr 7px 0.75em;\ngap: 2px;\n}\n.ag-text{\nfont-size: 1.25em;\nline-height: 1em;\nalign-self: end;\ndisplay: flex;\nflex-wrap: wrap;\njustify-content: space-between;\nuser-select: none;\n}\n.ag-value{\nfont-weight:bold;\n}\n.ag-unit{\nfont-size:.75em;\nfont-weight:normal;\npadding-inline-start: 0.15em;\n}\n.ag-limits{\ndisplay: flex;\njustify-content: space-between;\nfont-size: .75em;\nline-height: .75em;\nalign-content: center;\nflex-wrap: wrap;\nuser-select: none;\n}\n\n.ag-track{\nposition:relative;\ndisplay:flex;\nalign-items: center;\nwidth: 100%;\nborder-radius: 6px;\n}\n\n.ag-track-background{\nposition:absolute;\nbackground: var(--line-color,rgb(var(--v-theme-primary)));\nopacity: 0.45;\nwidth: 100%;\nheight: 50%;\nborder-radius:inherit;\n}\n.ag-track-foreground{\nposition:absolute;\nbackground-color: var(--line-color,rgb(var(--v-theme-primary)));\nwidth: 50%;\nheight: 100%;\nmax-width: 100%;\nborder-radius:inherit;\ntransition:inherit;\n}\n</style>",
"storeOutMessages": true,
"passthru": true,
"resendOnRefresh": true,
"templateScope": "local",
"className": "",
"x": 670,
"y": 180,
"wires": [
[]
]
},
{
"id": "897c7005ca8b3a31",
"type": "ui-template",
"z": "51ef46c8e45f5993",
"group": "1e0364d1c4ba9e0d",
"page": "",
"ui": "",
"name": "E.Coli",
"order": 3,
"width": "3",
"height": "1",
"head": "",
"format": "<template>\n <div ref=\"hng\" :class=\"icon ? 'ag-wrapper-2' : 'ag-wrapper-1'\" :style=\"`--line-color:${colors[0]};`\">\n <div v-if=\"icon\" class=\"ag-icon\">\n <v-icon aria-hidden=\"false\">{{icon}}</v-icon>\n </div>\n <div class=\"ag-content\">\n <div class=\"ag-text\">\n <span class=\"ag-label\">{{label}}</span>\n <span class=\"ag-value\">{{formattedValue}}<span class=\"ag-unit\">{{unit}}</span></span>\n </div>\n <div class=\"ag-track\" ref=\"agLine\">\n <div class=\"ag-track-background\"></div>\n <div class=\"ag-track-foreground\" :style=\"{'width': linesize +'%'}\"></div>\n </div>\n <div class=\"ag-limits\">\n <span class=\"ag-min\">{{min}}</span>\n <span class=\"ag-max\">{{max}}</span>\n </div>\n </div>\n </div>\n</template>\n\n<script>\n export default {\n data(){\n return {\n //Define me here\n \n label:\"E.Coli\", // The label\n //icon:\"mdi-account\", // (type: artless) (optional) the icon\n zeroCross:false,// (type: artless) line changes color depending on value being positive or negative (at least 2 colors must be defined)\n min:0, // Smallest expected value\n max:8, // Highest expected value\n unit:\"log10\",// The unit of the measurement \n animate:true, // Animating led's is not most performant thing in the world. \n \n // Define colors \n colors:[\n \"red\",\n \"red\", \n \"red\",\n \"red\", \n \"orange\",\n \"orange\",\n \"orange\",\n \"orange\",\n \"#0fb60f\"\n ], \n \n //no need to change those\n value:0, \n inited:false\n }\n },\n\n\n \n methods: { \n getElement: function(name,base){ \n if(base){\n return this.$refs[name]\n }\n return this.$refs[name][0]\n },\n validate(data){\n let ret\n if(typeof data !== \"number\"){\n ret = parseFloat(data)\n if(isNaN(ret)){\n console.log(\"BAD DATA! gauge type:\",this.type, \"id:\",this.id,\"data:\",data)\n return null\n } \n }\n else{\n ret = data\n } \n return ret\n },\n changeLine:function(){\n const line = this.getElement(\"agLine\",true);\n if(!line){\n console.log(\"no line found\")\n return \n }\n \n let c = Math.floor(this.colors.length * this.percentage / 100)\n if(c >= this.colors.length){\n c = this.colors.length - 1\n }\n if(c < 0){\n c = 0\n }\n if(this.zeroCross){\n c = this.value > 0 ? (this.colors.length - 1) : 0\n }\n line.style.setProperty('--line-color',this.colors[c])\n\n }\n },\n \n watch: {\n msg: function(){ \n if(this.msg.payload !== undefined){ \n const v = this.validate(this.msg.payload) \n if(v === null){\n return\n } \n this.value = v\n this.changeLine() \n }\n }\n },\n computed: {\n formattedValue: function () {\n return this.value.toFixed(2)\n },\n percentage: function(){\n return Math.floor(((this.value - this.min) / (this.max - this.min)) * 100);\n },\n linesize:function(){\n if(this.zeroCross){\n return Math.floor(((Math.abs(this.value) - this.min) / (this.max - this.min)) * 100); \n }\n else{\n return Math.max(0,this.percentage)\n }\n }\n },\n mounted(){\n const line = this.getElement(\"agLine\",true);\n line.style.setProperty('--line-color',this.colors[0])\n if(this.animate == true){\n if(!line){\n console.log(\"artless init() no line found\")\n return\n }\n line.style.transition = \"width 0.5s\";\n }\n \n this.inited = true;\n }\n}\n</script>\n\n<style>\n.ag-wrapper-2 {\ndisplay: grid;\ngrid-template-columns: 3em 1fr;\ngap:1em;\n}\n.ag-wrapper-1 {\ndisplay: grid;\ngrid-template-columns: 1fr;\n}\n.ag-icon{\nfont-size: 2em;\ndisplay: flex;\nflex-direction: column;\njustify-content: center;\n}\n.ag-content{\ndisplay: grid;\ngrid-template-rows: 1fr 7px 0.75em;\ngap: 2px;\n}\n.ag-text{\nfont-size: 1.25em;\nline-height: 1em;\nalign-self: end;\ndisplay: flex;\nflex-wrap: wrap;\njustify-content: space-between;\nuser-select: none;\n}\n.ag-value{\nfont-weight:bold;\n}\n.ag-unit{\nfont-size:.75em;\nfont-weight:normal;\npadding-inline-start: 0.15em;\n}\n.ag-limits{\ndisplay: flex;\njustify-content: space-between;\nfont-size: .75em;\nline-height: .75em;\nalign-content: center;\nflex-wrap: wrap;\nuser-select: none;\n}\n\n.ag-track{\nposition:relative;\ndisplay:flex;\nalign-items: center;\nwidth: 100%;\nborder-radius: 6px;\n}\n\n.ag-track-background{\nposition:absolute;\nbackground: var(--line-color,rgb(var(--v-theme-primary)));\nopacity: 0.45;\nwidth: 100%;\nheight: 50%;\nborder-radius:inherit;\n}\n.ag-track-foreground{\nposition:absolute;\nbackground-color: var(--line-color,rgb(var(--v-theme-primary)));\nwidth: 50%;\nheight: 100%;\nmax-width: 100%;\nborder-radius:inherit;\ntransition:inherit;\n}\n</style>",
"storeOutMessages": true,
"passthru": true,
"resendOnRefresh": true,
"templateScope": "local",
"className": "",
"x": 650,
"y": 220,
"wires": [
[]
]
},
{
"id": "f999d4f28598e2d0",
"type": "ui-template",
"z": "51ef46c8e45f5993",
"group": "1e0364d1c4ba9e0d",
"page": "",
"ui": "",
"name": "Listeria",
"order": 1,
"width": "3",
"height": "1",
"head": "",
"format": "<template>\n <div ref=\"hng\" :class=\"icon ? 'ag-wrapper-2' : 'ag-wrapper-1'\" :style=\"`--line-color:${colors[0]};`\">\n <div v-if=\"icon\" class=\"ag-icon\">\n <v-icon aria-hidden=\"false\">{{icon}}</v-icon>\n </div>\n <div class=\"ag-content\">\n <div class=\"ag-text\">\n <span class=\"ag-label\">{{label}}</span>\n <span class=\"ag-value\">{{formattedValue}}<span class=\"ag-unit\">{{unit}}</span></span>\n </div>\n <div class=\"ag-track\" ref=\"agLine\">\n <div class=\"ag-track-background\"></div>\n <div class=\"ag-track-foreground\" :style=\"{'width': linesize +'%'}\"></div>\n </div>\n <div class=\"ag-limits\">\n <span class=\"ag-min\">{{min}}</span>\n <span class=\"ag-max\">{{max}}</span>\n </div>\n </div>\n </div>\n</template>\n\n<script>\n export default {\n data(){\n return {\n //Define me here\n \n label:\"Listeria\", // The label\n //icon:\"mdi-account\", // (type: artless) (optional) the icon\n zeroCross:false,// (type: artless) line changes color depending on value being positive or negative (at least 2 colors must be defined)\n min:0, // Smallest expected value\n max:8, // Highest expected value\n unit:\"log10\",// The unit of the measurement \n animate:true, // Animating led's is not most performant thing in the world. \n \n // Define colors \n colors:[\n \"red\",\n \"red\", \n \"red\",\n \"red\", \n \"orange\",\n \"orange\",\n \"orange\",\n \"orange\",\n \"#0fb60f\"\n ], \n \n //no need to change those\n value:0, \n inited:false\n }\n },\n\n\n \n methods: { \n getElement: function(name,base){ \n if(base){\n return this.$refs[name]\n }\n return this.$refs[name][0]\n },\n validate(data){\n let ret\n if(typeof data !== \"number\"){\n ret = parseFloat(data)\n if(isNaN(ret)){\n console.log(\"BAD DATA! gauge type:\",this.type, \"id:\",this.id,\"data:\",data)\n return null\n } \n }\n else{\n ret = data\n } \n return ret\n },\n changeLine:function(){\n const line = this.getElement(\"agLine\",true);\n if(!line){\n console.log(\"no line found\")\n return \n }\n \n let c = Math.floor(this.colors.length * this.percentage / 100)\n if(c >= this.colors.length){\n c = this.colors.length - 1\n }\n if(c < 0){\n c = 0\n }\n if(this.zeroCross){\n c = this.value > 0 ? (this.colors.length - 1) : 0\n }\n line.style.setProperty('--line-color',this.colors[c])\n\n }\n },\n \n watch: {\n msg: function(){ \n if(this.msg.payload !== undefined){ \n const v = this.validate(this.msg.payload) \n if(v === null){\n return\n } \n this.value = v\n this.changeLine() \n }\n }\n },\n computed: {\n formattedValue: function () {\n return this.value.toFixed(2)\n },\n percentage: function(){\n return Math.floor(((this.value - this.min) / (this.max - this.min)) * 100);\n },\n linesize:function(){\n if(this.zeroCross){\n return Math.floor(((Math.abs(this.value) - this.min) / (this.max - this.min)) * 100); \n }\n else{\n return Math.max(0,this.percentage)\n }\n }\n },\n mounted(){\n const line = this.getElement(\"agLine\",true);\n line.style.setProperty('--line-color',this.colors[0])\n if(this.animate == true){\n if(!line){\n console.log(\"artless init() no line found\")\n return\n }\n line.style.transition = \"width 0.5s\";\n }\n \n this.inited = true;\n }\n}\n</script>\n\n<style>\n.ag-wrapper-2 {\ndisplay: grid;\ngrid-template-columns: 3em 1fr;\ngap:1em;\n}\n.ag-wrapper-1 {\ndisplay: grid;\ngrid-template-columns: 1fr;\n}\n.ag-icon{\nfont-size: 2em;\ndisplay: flex;\nflex-direction: column;\njustify-content: center;\n}\n.ag-content{\ndisplay: grid;\ngrid-template-rows: 1fr 7px 0.75em;\ngap: 2px;\n}\n.ag-text{\nfont-size: 1.25em;\nline-height: 1em;\nalign-self: end;\ndisplay: flex;\nflex-wrap: wrap;\njustify-content: space-between;\nuser-select: none;\n}\n.ag-value{\nfont-weight:bold;\n}\n.ag-unit{\nfont-size:.75em;\nfont-weight:normal;\npadding-inline-start: 0.15em;\n}\n.ag-limits{\ndisplay: flex;\njustify-content: space-between;\nfont-size: .75em;\nline-height: .75em;\nalign-content: center;\nflex-wrap: wrap;\nuser-select: none;\n}\n\n.ag-track{\nposition:relative;\ndisplay:flex;\nalign-items: center;\nwidth: 100%;\nborder-radius: 6px;\n}\n\n.ag-track-background{\nposition:absolute;\nbackground: var(--line-color,rgb(var(--v-theme-primary)));\nopacity: 0.45;\nwidth: 100%;\nheight: 50%;\nborder-radius:inherit;\n}\n.ag-track-foreground{\nposition:absolute;\nbackground-color: var(--line-color,rgb(var(--v-theme-primary)));\nwidth: 50%;\nheight: 100%;\nmax-width: 100%;\nborder-radius:inherit;\ntransition:inherit;\n}\n</style>",
"storeOutMessages": true,
"passthru": true,
"resendOnRefresh": true,
"templateScope": "local",
"className": "",
"x": 660,
"y": 260,
"wires": [
[]
]
},
{
"id": "48106afca278ac11",
"type": "ui-group",
"name": "T1",
"page": "4f1edd9ca629751a",
"width": "3",
"height": "3",
"order": 1,
"showTitle": false,
"className": "",
"visible": "true",
"disabled": "false"
},
{
"id": "1e0364d1c4ba9e0d",
"type": "ui-group",
"name": "Bakteerit",
"page": "4f1edd9ca629751a",
"width": "3",
"height": "3",
"order": 2,
"showTitle": false,
"className": "",
"visible": "true",
"disabled": "false"
},
{
"id": "4f1edd9ca629751a",
"type": "ui-page",
"name": "testi",
"ui": "e7bb0a62b3d9dd57",
"path": "/page1",
"icon": "home",
"layout": "grid",
"theme": "4d024ff372f88d1f",
"order": -1,
"className": "",
"visible": "true",
"disabled": "false"
},
{
"id": "e7bb0a62b3d9dd57",
"type": "ui-base",
"name": "UI Name",
"path": "/dashboard",
"includeClientData": true,
"acceptsClientConfig": [
"ui-notification",
"ui-control"
],
"showPathInSidebar": false
},
{
"id": "4d024ff372f88d1f",
"type": "ui-theme",
"name": "Theme Name",
"colors": {
"surface": "#979191",
"primary": "#0094ce",
"bgPage": "#eeeeee",
"groupBg": "#ffffff",
"groupOutline": "#cccccc"
},
"sizes": {
"pagePadding": "12px",
"groupGap": "12px",
"groupBorderRadius": "4px",
"widgetGap": "12px"
}
}
]