@hotnipi/node-red-dashboard-2-ui-edgewise-meter
Version:
An edgewise meter gauge node for @flowfuse/node-red-dashboard
486 lines (444 loc) • 21.3 kB
HTML
<script type="text/javascript">
RED.nodes.registerType('ui-edgewise-meter', {
category: RED._('@flowfuse/node-red-dashboard/ui-base:ui-base.label.category'),
color: RED._('@flowfuse/node-red-dashboard/ui-base:ui-base.colors.dark'),
defaults: {
name: { value: "" },
group: { type: 'ui-group', required: true },
order: { value: 0 },
width: {
value: 0,
validate: function (v) {
const width = v || 0
const currentGroup = $('#node-input-group').val() || this.group
const groupNode = RED.nodes.node(currentGroup)
const valid = !groupNode || +width <= +groupNode.width
$('#node-input-size').toggleClass('input-error', !valid)
return valid
}
},
height: { value: 0 },
min: {
value: 0,
required: true,
validate:function(v) {
const max=Number($("#node-input-max").length ? $("#node-input-max").val() : this.max)
return (RED.validators.number()(v) && Number(v) < max)
},
},
max: {
value: 10,
required:true,
validate:function(v) {
const min=Number($("#node-input-min").length ? $("#node-input-min").val() : this.min)
return (RED.validators.number()(v) && Number(v) > min)
},
},
label: {value: "Edgewise Meter"},
count:{
value:20,
required: true,
validate:function(v){
const min=Number($("#node-input-min").length ? $("#node-input-min").val() : this.min)
const max=Number($("#node-input-max").length ? $("#node-input-max").val() : this.max)
const c = Number(v)
return(RED.validators.number()(v) && Number(v) >= 10 && getPrecision(1,{min:min,max:max,count:c}) > 0)
},
},
displaysize:{value:700,required: true},
sectors:{value:[]},
dark:{value:false},
smallDigits:{value:true},
miniDisplay:{value:"none"},
miniDisplayProps:{value:"bottom-left"},
animation:{value:"ease"},
precision:{value:1},
logo:{value:""},
unit:{value:""},
myclass: {value: ""},
property: { value: "payload", required: true },
},
inputs: 1,
outputs: 0,
icon: "font-awesome/fa-ellipsis-h",
paletteLabel: "edgewise meter",
label: function() {
return this.name || "edgewise meter";
},
oneditprepare: function () {
let node = this
$('#node-input-size').elementSizer({
width: '#node-input-width',
height: '#node-input-height',
group: '#node-input-group'
});
if (this.property === undefined) {
$("#node-input-property").val("payload");
}
$("#node-input-property").typedInput({ default: 'msg', types: ['msg'] });
$("#node-input-min").typedInput({
types: ["num"]
});
$("#node-input-max").typedInput({
types: ["num"]
});
$("#node-input-count").typedInput({
types: ["num"]
});
$("#node-input-displaysize").typedInput({
types: ["num"]
});
$("#node-input-unit").typedInput({
types: ["str"]
});
$('input:radio[name="appearance"]').filter('[value='+node.miniDisplayProps+']').attr('checked', true);
$('#emw-appearance-select-row').css('display',node.miniDisplay == "none" ? "none" : "block");
$("#node-input-unit-group").css('display',node.miniDisplay == "none" ? "none" : "inline");
$('#node-input-miniDisplay').change(function(){
$('#emw-appearance-select-row').css('display',$(this).val() == "none" ? "none" : "block");
$("#node-input-unit-group").css('display',$(this).val() == "none" ? "none" : "inline");
})
let sectorsList = $("#node-input-sectors-container")
sectorsList.editableList({
addItem: function(container, i, sector) {
let newAddition = false
if (!sector.hasOwnProperty("start")) {
newAddition = true
sector = {start: "", color: ""}
if (i === 0) {
sector.start = Math.min(Number($("#node-input-min").val()), Number($("#node-input-max").val())) || "0"
}
}
let row = $('<div/>', {
class: 'form-row',
style: 'display: flex; margin-bottom: 0;'
}).appendTo(container)
let startField = $('<input/>', {
type: 'text',
name: i,
required: true,
style: 'width: 140px; margin-left: 5px; margin-bottom: 0;',
class: 'node-input-sector-start-value',
value:sector.start
}).appendTo(row).typedInput({types: ['num']});
startField.on("change keyup paste", function() {
validateSectors(sectorsList)
});
let colorField = $('<input/>', {
type: 'color',
style: 'width:80px; margin-left: 5px; margin-bottom: 0;',
class: 'node-input-sector-color-value'
}).appendTo(row)
colorField.val(sector.color)
let transparentField = $('<input/>', {
type: 'checkbox',
style: 'margin-left: 5px; margin-bottom: 0; width:14px;',
class: 'node-input-sector-transparent-value',
id: "node-input-sector-transparent-value-"+i
}).appendTo(row)
transparentField.prop('checked', sector.color == "transparent");
colorField.css('display',sector.color == "transparent" ? "none" : "block")
transparentField.on('change',function(){
colorField.css('display',$(this).is(":checked") ? "none" : "block")
})
$(`<label/>`, {
style:'margin-left: 5px; margin-block: auto;',
for: "node-input-sector-transparent-value-"+i,
text:"Transparent"
}).appendTo(row)
if (newAddition) {
validateSectors(sectorsList)
}
},
sortable: true,
removable: true,
height: 'auto',
scrollOnAdd: true,
header: $('<div>').append(
$.parseHTML(
"<div style='width:40%; margin-left:10%; display:inline-grid;'>Start</div><div style='width:30%; display:inline-grid;'>Colour</div>"
)
),
addButton: 'add sector',
removeItem: function(data) {
renumberSectors(sectorsList)
validateSectors(sectorsList)
},
sortItems: function(items) {
renumberSectors(sectorsList)
validateSectors(sectorsList)
}
})
this.sectors = this.sectors || []
sectorsList.editableList('addItems', this.sectors)
validateSectors(sectorsList)
},
oneditsave: function() {
let node = this;
let sectors = $("#node-input-sectors-container").editableList('items');
node.sectors = [];
sectors.each(function(i) {
node.sectors.push({
start: $(this).find('.node-input-sector-start-value').typedInput('value'),
color: $(this).find('.node-input-sector-transparent-value').is(":checked") ? "transparent" : $(this).find('.node-input-sector-color-value').val()
})
});
node.precision = getPrecision(1)
let appearance = $('input:radio[name="appearance"]')
console.log(appearance)
appearance.each(function(){
if($(this).is(":checked")){
node.miniDisplayProps = $(this).val()
}
})
}
})
function getPrecision (p, opt){
const comp = [0,1,2,5]
const max = $("#node-input-max").val() ?? opt.max;
const min = $("#node-input-min").val() ?? opt.min
const count = $("#node-input-count").val() ?? opt.count
const distance = Math.abs(min-max)
let t = distance * p / count
let valid = comp.includes((t * p) % count)
//console.log("distance", distance,t,count,(t * p) % count)
if(valid){
return p
}
else{
for(let i=0;i<4;++i){
p *= 10;
t = distance * p / count
valid = comp.includes((t * p) % count)
if (valid){
break
}
}
}
if(valid){
return p
}
else{
return 0
}
}
function renumberSectors(sectorsList) {
let index = 0
sectorsList.find(".node-input-sector-start-value").each(function() {
this.name = index++
})
}
function validateSectors(sectorsList) {
const startValueFields = sectorsList.find(".node-input-sector-start-value")
let values = []
startValueFields.each(function() {
values.push(Number(this.value))
})
startValueFields.each(function() {
validateSectorStartField(this, values)
})
}
function validateSectorStartField(startField, values) {
const startValue = startField.value;
const thisIndex = Number(startField.name)
let valid = true
if (!RED.validators.number()(startValue)) {
console.log(`not number`)
valid = false
} else {
if (thisIndex > 0 && Number(startValue) <= values[thisIndex-1]) {
console.log(`a`)
valid = false
} else {
if (thisIndex < values.length-1 && Number(startValue) >= values[thisIndex+1])
{
console.log(`b`)
valid = false
}
}
}
if (!valid) {
$(startField).closest(".form-row").find(".red-ui-typedInput-container").css('outline', '1px solid rgb(214, 97, 95)');
}
else {
$(startField).closest(".form-row").find(".red-ui-typedInput-container").css('outline', '');
}
}
</script>
<style>
.ewm.form-row label .fa{
margin-right: 4px;
}
.ewm.form-row a .fa{
margin-right: 4px;
}
.ewm.form-row .ewm-radios label:not(.emw-radios-main-label) {
width:unset;
}
.ewm-radios{
display:flex;
}
.ewm-radios label {
cursor: pointer;
position: relative;
}
.ewm-radios label + label {
margin-left: 6px;
}
.ewm-radios input[type="radio"] {
opacity: 0;
position: absolute;
width:unset;
}
.ewm-radios input[type="radio"] + span {
display: inline-block;
position:relative;
border-radius: 4px;
outline: 2px solid var(--red-ui-form-input-border-color);;
height: 23px;
aspect-ratio: 9/6;
padding-inline: 6px;
}
.ewm-radios input[type="radio"] + span:before{
content:"";
position:absolute;
display:block;
width:35%;
height:35%;
border-radius: 2px;
outline:1px solid var(--red-ui-form-input-border-color);;
}
.ewm-radios input[type="radio"] + span.ewm-big:before{
height:20px;
top:2px;
}
.ewm-radios input[type="radio"] + span.ewm-top:before{
top:2px;
}
.ewm-radios input[type="radio"] + span.ewm-bottom:before{
bottom:2px;
}
.ewm-radios input[type="radio"] + span.ewm-left:before{
left:2px;
}
.ewm-radios input[type="radio"] + span.ewm-right:before{
right:2px;
}
.ewm-radios input[type="radio"]:checked + span {
background-color: var(--red-ui-form-button-background);
outline-color: var(--red-ui-form-input-border-selected-color);
}
.ewm-radios input[type="radio"]:checked + span:before {
outline-color: var(--red-ui-form-input-border-selected-color);
background-color: var(--red-ui-form-input-background)
}
</style>
<script type="text/html" data-template-name="ui-edgewise-meter">
<div class="form-row ewm">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
<div class="form-row ewm">
<label for="node-input-group"><i class="fa fa-table"></i> <span data-i18n="ui-edgewise-meter.label.group"></label>
<input type="text" id="node-input-group">
</div>
<div class="form-row nr-db-ui-element-sizer-row">
<label><i class="fa fa-object-group"></i> <span data-i18n="ui-edgewise-meter.label.size"></label>
<input type="hidden" id="node-input-width">
<input type="hidden" id="node-input-height">
<button class="editor-button" id="node-input-size"></button>
</div>
<div class="form-row ewm">
<label for="node-input-label"><i class="fa fa-i-cursor"></i> <span data-i18n="ui-edgewise-meter.label.label"></span></label>
<input type="text" id="node-input-label">
</div>
<div class="form-row ewm">
<label for="node-input-property"><i class="fa fa-arrow-right"></i> <span data-i18n="ui-edgewise-meter.label.input"></span></label>
<input type="text" id="node-input-property" style="min-width:261px;"/>
</div>
<div class="form-row ewm">
<label for="node-input-min" ><i class="fa fa-expand"></i> <span data-i18n="ui-edgewise-meter.label.limits"></span></label>
<label for="node-input-min" style="width:auto" data-i18n="ui-edgewise-meter.label.min"></label>
<input type="text" id="node-input-min" style="width:90px">
<label for="node-input-max" style="width:auto; margin-left:20px;" data-i18n="ui-edgewise-meter.label.max"></label>
<input type="text" id="node-input-max" style="width:90px">
</div>
<div class="form-row ewm">
<label for="node-input-count"><i class="fa fa-ellipsis-h"></i> <span data-i18n="ui-edgewise-meter.label.count"></span></label>
<input type="text" id="node-input-count" style="width:90px; margin-right:5px;">
<label for="node-input-smallDigits"><span data-i18n="ui-edgewise-meter.label.smalldigits"></span></label>
<input type="checkbox" id="node-input-smallDigits" style="width:unset; margin-block: auto;">
</div>
<div class="form-row ewm">
<label for="node-input-displaysize"><i class="fa fa-arrows-h"></i> <span data-i18n="ui-edgewise-meter.label.displaysize"></span></label>
<input type="text" id="node-input-displaysize" style="width:90px;">
</div>
<div class="form-row ewm">
<label><i class="fa fa-list fa-sort-numeric-asc"></i> <span data-i18n="ui-edgewise-meter.label.sectors"></label>
<ol id="node-input-sectors-container"></ol>
</div>
<div class="form-row ewm">
<label for='node-input-miniDisplay'><i class='fa fa-newspaper-o'></i><span data-i18n="ui-edgewise-meter.label.exactvaluedisplay"></label>
<select id='node-input-miniDisplay' style='width:180px !important'>
<option value='none' data-i18n="ui-edgewise-meter.label.dontshow"></option>
<option value='hover' data-i18n="ui-edgewise-meter.label.hover"></option>
<option value='permanent' data-i18n="ui-edgewise-meter.label.permanent"></option>
</select>
<div id="node-input-unit-group">
<label for="node-input-unit" style="width:auto; margin-left:8px;"><span data-i18n="ui-edgewise-meter.label.unit"></span></label>
<input type="text" id="node-input-unit" style="width:90px">
</div>
</div>
<div class="form-row ewm" id="emw-appearance-select-row">
<div class="ewm-radios" id="emw-appearance-select">
<label class="emw-radios-main-label" for="emw-appearance-select"><i class="fa fa-wrench"></i> <span data-i18n="ui-edgewise-meter.label.appearance"></span></label>
<label for="ewm-bottom-left" >
<input type="radio" name="appearance" id="ewm-bottom-left" value="bottom-left"/>
<span class="ewm-bottom ewm-left"></span>
</label>
<label for="ewm-top-left" >
<input type="radio" name="appearance" id="ewm-top-left" value="top-left"/>
<span class="ewm-top ewm-left"></span>
</label>
<label for="ewm-top-right" >
<input type="radio" name="appearance" id="ewm-top-right" value="top-right"/>
<span class="ewm-top ewm-right"></span>
</label>
<label for="ewm-bottom-right" >
<input type="radio" name="appearance" id="ewm-bottom-right" value="bottom-right"/>
<span class="ewm-bottom ewm-right"></span>
</label>
<label for="ewm-big-left" >
<input type="radio" name="appearance" id="ewm-big-left" value="big-left"/>
<span class="ewm-big ewm-left"></span>
</label>
<label for="ewm-big-right" >
<input type="radio" name="appearance" id="ewm-big-right" value="big-right"/>
<span class="ewm-big ewm-right"></span>
</label>
</div>
</div>
<div class='form-row ewm'>
<label for='node-input-animation'><i class='fa fa-sign-language'></i><span data-i18n="ui-edgewise-meter.label.animation"></label>
<select id='node-input-animation' style='width:200px !important'>
<option value='ease' data-i18n="ui-edgewise-meter.label.ease"></option>
<option value='cubic' data-i18n="ui-edgewise-meter.label.cubic"></option>
<option value='none' data-i18n="ui-edgewise-meter.label.none"></option>
</select>
</div>
<div class="form-row ewm">
<label for="node-input-dark"><i class='fa fa-paint-brush'></i><span data-i18n="ui-edgewise-meter.label.dark"></span></label>
<input type="checkbox" id="node-input-dark" style="width:unset; margin-block: auto;">
</div>
<div class="form-row ewm">
<label for="node-input-logo"><i class="fa fa-smile-o"></i> <span data-i18n="ui-edgewise-meter.label.logo"></span></label>
<input type="text" id="node-input-logo">
</div>
<div class="form-row ewm">
<label for="node-input-myclass"><i class="fa fa-font"></i><span data-i18n="ui-edgewise-meter.label.class"></label>
<input type="text" id="node-input-myclass">
</div>
<div class="form-row ewm">
<a href="https://buymeacoffee.com/hotnipi" target="_blank"><i class="fa fa-coffee"></i><span data-i18n="ui-edgewise-meter.label.coffee"></span></label></a>
</div>
</script>