Skip to content

Commit

Permalink
resizable thermometer implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
gayanSandamal committed Oct 17, 2024
1 parent 2f20bb7 commit 24ebdba
Show file tree
Hide file tree
Showing 5 changed files with 295 additions and 4 deletions.
4 changes: 4 additions & 0 deletions nodes/widgets/locales/en-US/ui_gauge.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@
"gauge34": "3/4 Gauge",
"gaugeBattery": "Battery Level",
"gaugeTank": "Tank Level",
"gaugeThermometer": "Thermometer",
"needle": "Needle",
"rounded": "Rounded",
"tooltip": "Tooltip",
"left": "Left",
"right": "Right",
"limits": "Limits",
"range": "Range",
"min": "min.",
Expand Down
37 changes: 35 additions & 2 deletions nodes/widgets/ui_gauge.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
}
const unique = new Set(this.segments.map(function (o) { return o.from }))
if (this.segments.length === unique.size) { $('#valWarning').hide() } else { $('#valWarning').show() }
return minCovered && !extras && this.segments.length === unique.size
return this.gtype !== 'gauge-thermometer' ? minCovered && !extras && this.segments.length === unique.size : false
}

const DEFAULTS = {
Expand Down Expand Up @@ -87,6 +87,11 @@
color: '#EA5353',
from: 7
}]
},
'gauge-thermometer': {
min: 0,
max: 10,
segments: []
}
}
DEFAULTS['gauge-34'] = DEFAULTS['gauge-tile']
Expand Down Expand Up @@ -137,6 +142,7 @@
height: { value: 3 },
gtype: { value: 'gauge-half' },
gstyle: { value: 'needle' },
thermoTooltipPosition: { value: 'right' },
title: { value: 'gauge' },
units: { value: 'units' },
icon: { value: '' },
Expand Down Expand Up @@ -188,6 +194,12 @@
})
}

if (this.thermoTooltipPosition === 'left') {
$('#node-input-thermoTooltipPosition').val('left')
} else {
$('#node-input-thermoTooltipPosition').val('right')
}

function generateSegment (i, segment) {
const container = $('<li/>', { style: 'background: var(--red-ui-secondary-background, #fff); margin:0; padding:8px 0px 0px; border-bottom: 1px solid var(--red-ui-form-input-border-color, #ccc);' })
const row = $('<div/>').appendTo(container)
Expand Down Expand Up @@ -227,6 +239,20 @@
$('#node-input-container-label-extras').show()
}

if (type === 'gauge-thermometer') {
$('#node-input-container-thermoTooltipPosition').show()
$('.node-input-segments-container-row').hide()
$('#node-input-add-segment').hide()
$('#node-input-get-defaults').css({ 'margin-top': '4px', 'margin-left': '103px' })
$('#ui-gauge-icon').hide()
$('#node-input-container-sizes').hide()
} else {
$('#node-input-container-thermoTooltipPosition').hide()
$('.node-input-segments-container-row').show()
$('#node-input-add-segment').show()
$('#node-input-get-defaults').css({ 'margin-top': '4px', 'margin-left': 0 })
}

if (isInitialised && isChanging && isDefault(oldType)) {
const defaults = DEFAULTS[currentType]
setup(defaults?.segments, defaults?.min, defaults?.max)
Expand Down Expand Up @@ -325,7 +351,7 @@
<option value="gauge-34" data-i18n="ui-gauge.label.gauge34"></option>
<option value="gauge-half" data-i18n="ui-gauge.label.gaugeHalf"></option>
<option value="gauge-battery" data-i18n="ui-gauge.label.gaugeBattery"></option>
<option value="gauge-tank" data-i18n="ui-gauge.label.gaugeTank"></option>
<option value="gauge-thermometer" data-i18n="ui-gauge.label.gaugeThermometer"></option>
<!-- <segment value="donut">Donut</segment>
<segment value="compass">Compass</segment>
<segment value="wave">Level</segment> -->
Expand All @@ -339,6 +365,13 @@
<!-- <option value=rounded">Rounded</option> -->
</select>
</div>
<div id="node-input-container-thermoTooltipPosition" class="form-row">
<label for="node-input-thermoTooltipPosition"><i class="fa fa-thermometer-three-quarters"></i> <span data-i18n="ui-gauge.label.tooltip"></span></label>
<select id="node-input-thermoTooltipPosition" style="width:150px">
<option value="left" data-i18n="ui-gauge.label.left"></option>
<option value="right" data-i18n="ui-gauge.label.right"></option>
</select>
</div>
<div class="form-row">
<h3><span data-i18n="ui-gauge.label.limits"></span></h3>
</div>
Expand Down
4 changes: 4 additions & 0 deletions nodes/widgets/ui_gauge.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ module.exports = function (RED) {
// which group are we rendering this widget
const group = RED.nodes.getNode(config.group)

if (!config.thermoTooltipPosition || typeof config.thermoTooltipPosition === 'undefined') {
config.thermoTooltipPosition = 'right'
}

const evts = {
beforeSend: async function (msg) {
const updates = msg.ui_update
Expand Down
6 changes: 4 additions & 2 deletions ui/src/widgets/ui-gauge/UIGauge.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<component :is="`ui-${gtype}`" v-if="['gauge-tile', 'gauge-battery', 'gauge-tank'].includes(gtype)" :id="id" :props="dynamicProps" :value="value" />
<component :is="`ui-${gtype}`" v-if="['gauge-tile', 'gauge-battery', 'gauge-tank', 'gauge-thermometer'].includes(gtype)" :id="id" :props="dynamicProps" :value="value" />
<ui-gauge-dial v-else :id="id" :key="updateGaugeDial" :props="dynamicProps" :value="value" />
</template>

Expand All @@ -9,6 +9,7 @@ import { mapState } from 'vuex' // eslint-disable-line import/order
import UIGaugeBattery from './types/UIGaugeBattery.vue'
import UIGaugeDial from './types/UIGaugeDial.vue'
import UIGaugeTank from './types/UIGaugeTank.vue'
import UIGaugeThermometer from './types/UIGaugeThermometer.vue'
import UIGaugeTile from './types/UIGaugeTile.vue'
export default {
Expand All @@ -17,7 +18,8 @@ export default {
'ui-gauge-battery': UIGaugeBattery,
'ui-gauge-tank': UIGaugeTank,
'ui-gauge-dial': UIGaugeDial,
'ui-gauge-tile': UIGaugeTile
'ui-gauge-tile': UIGaugeTile,
'ui-gauge-thermometer': UIGaugeThermometer
},
inject: ['$socket', '$dataTracker'],
props: {
Expand Down
248 changes: 248 additions & 0 deletions ui/src/widgets/ui-gauge/types/UIGaugeThermometer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
<!-- eslint-disable vue/html-self-closing -->
<template>
<div id="nrdb-ui-gauge-thermometer-wrapper" ref="container">
<div id="thermometer-container" ref="thermometerContainer">
<div id="thermometer" ref="thermometer">
<div id="temperature" :style="{ height: `${temperatureHeight}%` }" :data-value="temperatureValue"></div>
<div id="graduations"></div>
</div>
<div id="mercury-bulb"></div>
</div>
<label v-if="props.label" ref="title" class="nrdb-ui-gauge-title">{{ props.label }}</label>
</div>
</template>

<script>
import { mapState } from 'vuex' // eslint-disable-line import/order
export default {
name: 'DBUIGaugeThermometer',
props: {
id: { type: String, required: true },
props: { type: Object, default: () => ({}) },
state: { type: Object, default: () => ({}) }
},
data () {
return {
resizeObserver: null
}
},
computed: {
...mapState('data', ['messages']),
value: function () {
return this.messages[this.id]?.payload
},
min () {
return this.props.min
},
max () {
return this.props.max
},
prefix () {
return this.props.prefix
},
suffix () {
return this.props.suffix
},
units () {
return this.props.units
},
clampedValue () {
return Math.min(Math.max(this.value || this.min, this.min), this.max)
},
temperatureHeight () {
return (this.clampedValue - this.min) / (this.max - this.min) * 100
},
temperatureValue () {
return `${this.prefix} ${this.clampedValue}${this.units} ${this.suffix}`
}
},
mounted () {
this.initResizeObserver()
},
unmounted () {
if (this.resizeObserver) {
this.resizeObserver.disconnect()
this.resizeObserver = null
}
},
methods: {
initResizeObserver () {
this.resizeObserver = new ResizeObserver(entries => {
for (const entry of entries) {
if (entry.target === this.$refs.container || entry.target === this.$el.parentElement) {
this.resize()
}
}
})
this.resizeObserver.observe(this.$refs.container)
this.resizeObserver.observe(this.$el.parentElement) // Observe the parent element
},
resize () {
const container = this.$el.parentElement
const thermometer = this.$refs.thermometerContainer
if (container && thermometer) {
const containerHeight = container.clientHeight
const containerWidth = container.clientWidth
const originalHeight = 180 // Original height of thermometer
const originalWidth = 20 // Original width of thermometer
// Calculate scale factors for width and height
const scaleX = containerWidth / originalWidth
const scaleY = containerHeight / originalHeight
// Use the smaller scale to maintain aspect ratio
const scale = Math.min(scaleX, scaleY)
// Apply the scale transform
thermometer.style.transform = `scale(${scale})`
thermometer.style.transformOrigin = 'top center'
}
}
}
}
</script>
<style lang="scss" scoped>
#nrdb-ui-gauge-thermometer-wrapper {
margin: auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
position: relative;
width: 100%;
height: 100%;
}
// VARIABLES (Have fun) ―――――――――――――――――――――――――
$TM-mainTint: #3d3d44;
$TM-backgroundColor: darken($TM-mainTint, 2%);
$TM-borderSize: 4px;
$TM-borderColor: darken($TM-mainTint, 8%);
$TM-width: 20px;
$TM-height: 115px;
$TM-bulbSize: $TM-width * 1.6;
$TM-radius: 20px;
$TM-graduationsStyle: 2px solid rgba(0, 0, 0, 0.5);
$TM-bulbColor: #3dcadf;
$TM-mercuryColor : linear-gradient(#f17a65, $TM-bulbColor) no-repeat bottom;
// Tooltip
$TM-tooltipColor: rgba(0, 0, 0, 0.7);
$TM-tooltipSize: 1em;
$TM-tooltipRadius: 5px;
$TM-tooltipTopShift: 5px;
$TM-tooltipVerticalPadding: 5px;
$TM-tooltipHorizontalPadding: $TM-tooltipVerticalPadding * 2;
$TM-tooltipLeftShift: 100%;
$TM-tooltipArrowWidth: 1.5; // Higher numbers produce smaller width
$TM-tooltipArrowHeight: 2.2; // Higher numbers produce smaller height
@mixin border() { border: $TM-borderSize solid $TM-borderColor; }
// THERMOMETER ―――――――――――――――――――――――――
#thermometer-container {
position: relative;
}
#thermometer {
position: relative;
width: $TM-width;
height: $TM-height;
border-left: $TM-borderSize solid $TM-borderColor;
border-top: $TM-borderSize solid $TM-borderColor;
border-right: $TM-borderSize solid $TM-borderColor;
border-bottom: $TM-borderSize solid $TM-bulbColor;
border-radius: $TM-radius $TM-radius 0 0;
z-index: 1;
background-color: #686868;
#graduations {
height: 59%;
top: 20%;
width: 50%;
&, &:before {
position: absolute;
border-top: $TM-graduationsStyle;
border-bottom: $TM-graduationsStyle;
}
&:before {
content: "";
height: 34%;
width: 100%;
top: 32%;
}
}
#temperature {
bottom: 0;
background: $TM-mercuryColor;
width: 100%;
border-radius: $TM-radius $TM-radius 0 0;
background-size: 100% $TM-height;
transition: all 0.2s ease-in-out;
&, &:before, &:after {
position: absolute;
}
// Temperature value - Tooltip
&:before {
content: attr(data-value);
background: $TM-tooltipColor;
color: white;
z-index: 2;
padding: $TM-tooltipVerticalPadding $TM-tooltipHorizontalPadding;
border-radius: $TM-tooltipRadius;
font-size: $TM-tooltipSize;
line-height: 1;
transform: translateY(50%);
left: calc(100% + 13px);
top: calc(-1em - 11px);
white-space: nowrap;
}
// Tooltip arrow
&:after {
content: "";
border-top: $TM-tooltipSize / $TM-tooltipArrowHeight solid transparent;
border-bottom: $TM-tooltipSize / $TM-tooltipArrowHeight solid transparent;
border-right: $TM-tooltipSize / $TM-tooltipArrowWidth solid $TM-tooltipColor;
left: calc(100% + 4px);
top: calc(-#{$TM-tooltipSize} / #{$TM-tooltipArrowHeight} - 1px);
}
}
}
#mercury-bulb {
position: absolute;
content: "";
border-radius: 50%;
transform: translateX(-50%);
width: $TM-bulbSize;
height: $TM-bulbSize;
background-color: $TM-bulbColor;
top: calc(100% - 9px);
@include border;
z-index: -3;
left: 50%;
}
.nrdb-ui-gauge-title {
position: absolute;
bottom: 0;
display: block;
text-align: center;
font-weight: bold;
font-size: 1rem;
padding-bottom: 4px;
font-size: min(1rem,max(2cqmin,0.8rem));
}
</style>

0 comments on commit 24ebdba

Please sign in to comment.