Rework oh-knob and oh-slider, fixes #1003 (#1012)

Fix oh-slider issue

Signed-off-by: Hubert Nusser <hubsif@gmx.de>
pull/1052/head
hubsif 2021-05-16 20:25:36 +02:00 committed by GitHub
parent f590b28331
commit d586b77594
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 145 additions and 80 deletions

View File

@ -116,6 +116,16 @@ Display a knob in a card to visualize and control a quantifiable item
Size the control using percentages instead of pixels
</PropDescription>
</PropBlock>
<PropBlock type="INTEGER" name="updateInterval" label="Update Interval">
<PropDescription>
Time to wait between subsequent commands in ms (default 500)
</PropDescription>
</PropBlock>
<PropBlock type="INTEGER" name="delayStateDisplay" label="Delay State Display">
<PropDescription>
Time to wait before switching from displaying user input to displaying item state in ms (default 2000)
</PropDescription>
</PropBlock>
</PropGroup>
</div>

View File

@ -125,6 +125,16 @@ A cell expanding to a knob control
Size the control using percentages instead of pixels
</PropDescription>
</PropBlock>
<PropBlock type="INTEGER" name="updateInterval" label="Update Interval">
<PropDescription>
Time to wait between subsequent commands in ms (default 500)
</PropDescription>
</PropBlock>
<PropBlock type="INTEGER" name="delayStateDisplay" label="Delay State Display">
<PropDescription>
Time to wait before switching from displaying user input to displaying item state in ms (default 2000)
</PropDescription>
</PropBlock>
</PropGroup>
</div>

View File

@ -83,6 +83,16 @@ Knob control, allow to change a number value on a circular track
Size the control using percentages instead of pixels
</PropDescription>
</PropBlock>
<PropBlock type="INTEGER" name="updateInterval" label="Update Interval">
<PropDescription>
Time to wait between subsequent commands in ms (default 500)
</PropDescription>
</PropBlock>
<PropBlock type="INTEGER" name="delayStateDisplay" label="Delay State Display">
<PropDescription>
Time to wait before switching from displaying user input to displaying item state in ms (default 2000)
</PropDescription>
</PropBlock>
<PropBlock type="TEXT" name="variable" label="Variable">
<PropDescription>
Name of the variable to set on input change

View File

@ -110,6 +110,16 @@ Display a slider in a card to control an item
Text to append to the label while dragging the cursor
</PropDescription>
</PropBlock>
<PropBlock type="INTEGER" name="updateInterval" label="Update Interval">
<PropDescription>
Time to wait between subsequent commands in ms (default 500)
</PropDescription>
</PropBlock>
<PropBlock type="INTEGER" name="delayStateDisplay" label="Delay State Display">
<PropDescription>
Time to wait before switching from displaying user input to displaying item state in ms (default 2000)
</PropDescription>
</PropBlock>
</PropGroup>
</div>

View File

@ -120,6 +120,16 @@ A cell expanding to a big vertical slider
Text to append to the label while dragging the cursor
</PropDescription>
</PropBlock>
<PropBlock type="INTEGER" name="updateInterval" label="Update Interval">
<PropDescription>
Time to wait between subsequent commands in ms (default 500)
</PropDescription>
</PropBlock>
<PropBlock type="INTEGER" name="delayStateDisplay" label="Delay State Display">
<PropDescription>
Time to wait before switching from displaying user input to displaying item state in ms (default 2000)
</PropDescription>
</PropBlock>
</PropGroup>
</div>

View File

@ -115,6 +115,16 @@ Display a slider control in a list
Text to append to the label while dragging the cursor
</PropDescription>
</PropBlock>
<PropBlock type="INTEGER" name="updateInterval" label="Update Interval">
<PropDescription>
Time to wait between subsequent commands in ms (default 500)
</PropDescription>
</PropBlock>
<PropBlock type="INTEGER" name="delayStateDisplay" label="Delay State Display">
<PropDescription>
Time to wait before switching from displaying user input to displaying item state in ms (default 2000)
</PropDescription>
</PropBlock>
</PropGroup>
</div>

View File

@ -78,6 +78,16 @@ Slider control, allows to pick a number value on a scale
Text to append to the label while dragging the cursor
</PropDescription>
</PropBlock>
<PropBlock type="INTEGER" name="updateInterval" label="Update Interval">
<PropDescription>
Time to wait between subsequent commands in ms (default 500)
</PropDescription>
</PropBlock>
<PropBlock type="INTEGER" name="delayStateDisplay" label="Delay State Display">
<PropDescription>
Time to wait before switching from displaying user input to displaying item state in ms (default 2000)
</PropDescription>
</PropBlock>
<PropBlock type="TEXT" name="variable" label="Variable">
<PropDescription>
Name of the variable to set on input change

View File

@ -11,5 +11,7 @@ export default () => [
pt('secondaryColor', 'Secondary Color', 'Color of the rest of the control (HTML value, default #dcdfe6)'),
pt('textColor', 'Text Color', 'Color of the value text (HTML value, default #000000)'),
pt('strokeWidth', 'Stroke Width', 'Thickness of the arcs, default 17'),
pb('responsive', 'Responsive', 'Size the control using percentages instead of pixels')
pb('responsive', 'Responsive', 'Size the control using percentages instead of pixels'),
pn('updateInterval', 'Update Interval', 'Time to wait between subsequent commands in ms (default 500)').a(),
pn('delayStateDisplay', 'Delay State Display', 'Time to wait before switching from displaying user input to displaying item state in ms (default 2000)').a()
]

View File

@ -10,5 +10,7 @@ export default () => [
pb('scale', 'Display Scale', 'Display a scale on the slider'),
pn('scaleSteps', 'Scale steps', 'Number of (major) scale markers'),
pn('scaleSubSteps', 'Scale sub-steps', 'Number of scale minor markers between each major marker'),
pt('unit', 'Unit', 'Text to append to the label while dragging the cursor')
pt('unit', 'Unit', 'Text to append to the label while dragging the cursor'),
pn('updateInterval', 'Update Interval', 'Time to wait between subsequent commands in ms (default 500)').a(),
pn('delayStateDisplay', 'Delay State Display', 'Time to wait before switching from displaying user input to displaying item state in ms (default 2000)').a()
]

View File

@ -1,69 +1,20 @@
<template>
<knob-control v-bind="config" :text-color="config.textColor || ($f7.data.themeOptions.dark === 'dark') ? '#ffffff' : undefined" :value="value" @input="onChange" />
<knob-control v-bind="config" :text-color="config.textColor || ($f7.data.themeOptions.dark === 'dark') ? '#ffffff' : undefined" :value="value"
@input="sendCommandDebounced($event)" @click.native="sendCommandDebounced(value, true)" @touchend.native="sendCommandDebounced(value, true)" />
</template>
<script>
import mixin from '../widget-mixin'
import slideMixin from './slide-mixin'
import { OhKnobDefinition } from '@/assets/definitions/widgets/system'
import KnobControl from 'vue-knob-control'
export default {
mixins: [mixin],
mixins: [mixin, slideMixin],
components: {
KnobControl
},
widget: OhKnobDefinition,
data () {
return {
pendingCommand: null,
delayCommand: false,
delayUpdate: null
}
},
mounted () {
delete this.config.value
},
computed: {
value () {
if (this.config.variable) return this.context.vars[this.config.variable]
if (this.delayUpdate && this.pendingCommand) return this.pendingCommand // to keep the control reactive when operating
const value = this.context.store[this.config.item].state
// use as a brightness control for HSB values
if (value.split && value.split(',').length === 3) return parseFloat(value.split(',')[2])
return parseFloat(value)
}
},
methods: {
onChange (value) {
if (value === this.value) return
if (this.config.variable) {
this.$set(this.context.vars, this.config.variable, value)
return
}
this.pendingCommand = value
if (!this.delayCommand) {
this.delayCommand = true
this.setUpdateDelayTimout()
this.$store.dispatch('sendCommand', { itemName: this.config.item, cmd: value.toString() })
setTimeout(() => {
this.delayCommand = false
if (this.delayUpdate) clearTimeout(this.delayUpdate)
if (this.pendingCommand) {
this.setUpdateDelayTimout()
this.$store.dispatch('sendCommand', { itemName: this.config.item, cmd: this.pendingCommand.toString() })
}
}, 200)
}
},
setUpdateDelayTimout () {
if (this.delayUpdate) clearTimeout(this.delayUpdate)
this.delayUpdate = setTimeout(() => {
console.debug('End of update delay')
this.delayUpdate = null
this.pendingCommand = null
}, 2000)
}
}
widget: OhKnobDefinition
}
</script>

View File

@ -1,5 +1,6 @@
<template>
<f7-range ref="rangeslider" class="oh-slider" v-bind="config" :value="value" @range:changed="onChange" :format-label="formatLabel" :format-scale-label="formatScaleLabel" />
<f7-range ref="rangeslider" class="oh-slider" v-bind="config" :value="value" :format-label="formatLabel" :format-scale-label="formatScaleLabel"
@range:change="sendCommandDebounced($event)" @click.native="sendCommandDebounced(value, true)" @touchend.native="sendCommandDebounced(value, true)" />
</template>
<style lang="stylus">
@ -10,30 +11,22 @@
<script>
import mixin from '../widget-mixin'
import slideMixin from './slide-mixin'
import { OhSliderDefinition } from '@/assets/definitions/widgets/system'
export default {
mixins: [mixin],
mixins: [mixin, slideMixin],
widget: OhSliderDefinition,
mounted () {
delete this.config.value
// f7-range inside of masonry can get rendered faulty, as the masonry changes its breakpoint layout after being rendered
// re-calculate the range slider after masonry is updated
setTimeout(() => {
this.$refs.rangeslider.f7Range.calcSize()
this.$refs.rangeslider.f7Range.layout()
if (this.$refs.rangeslider) {
this.$refs.rangeslider.f7Range.calcSize()
this.$refs.rangeslider.f7Range.layout()
}
}, 0)
},
computed: {
value () {
if (this.config.variable) return this.context.vars[this.config.variable]
const value = this.context.store[this.config.item].state
// use as a brightness control for HSB values
if (value.split && value.split(',').length === 3) return parseFloat(value.split(',')[2])
return parseFloat(value)
}
},
methods: {
formatLabel (value) {
return this.toStepFixed(value) + (this.config.unit || '')
@ -45,15 +38,6 @@ export default {
// uses the number of decimals in the step config to round the provided number
const nbDecimals = this.config.step ? Number(this.config.step).toString().replace(',', '.').split('.')[1] : 0
return parseFloat(Number(value).toFixed(nbDecimals))
},
onChange (value) {
const newValue = this.toStepFixed(value)
if (newValue === this.toStepFixed(this.value)) return
if (this.config.variable) {
this.$set(this.context.vars, this.config.variable, value)
} else if (this.config.item) {
this.$store.dispatch('sendCommand', { itemName: this.config.item, cmd: newValue.toString() })
}
}
}
}

View File

@ -0,0 +1,56 @@
export default {
data () {
return {
pendingCommand: null
}
},
mounted () {
delete this.config.value
this.updateInterval = this.config.updateInterval ? this.config.updateInterval : 200
this.delayStateDisplay = this.config.delayStateDisplay ? this.config.delayStateDisplay : 2000
},
computed: {
value () {
if (this.config.variable) return this.context.vars[this.config.variable]
if (this.pendingCommand) return this.pendingCommand // to keep the control reactive when operating
const value = this.context.store[this.config.item].state
// use as a brightness control for HSB values
if (value.split && value.split(',').length === 3) return parseFloat(value.split(',')[2])
return parseFloat(value)
}
},
methods: {
sendCommandDebounced (value, stop = false) {
if ((value === this.value && !stop) || value === this.lastValueSent) return
if (this.config.variable) {
this.$set(this.context.vars, this.config.variable, value)
return
}
if (!this.config.item) return
this.pendingCommand = value
let diff = this.lastDateSent ? Date.now() - this.lastDateSent : this.updateInterval
let delay = diff < this.updateInterval ? this.updateInterval - diff : stop ? 0 : this.updateInterval
if (this.sendCommandTimer && stop) {
clearTimeout(this.sendCommandTimer)
this.sendCommandTimer = null
}
if (!this.sendCommandTimer) {
if (this.displayLockTimer) clearTimeout(this.displayLockTimer)
this.sendCommandTimer = setTimeout(() => {
this.$store.dispatch('sendCommand', { itemName: this.config.item, cmd: this.pendingCommand.toString() })
this.lastValueSent = this.pendingCommand
this.lastDateSent = Date.now()
this.sendCommandTimer = null
// keep displaying `pendingCommand` as value for `delayStateDisplay` time to give sse state some time to update
this.displayLockTimer = setTimeout(() => { this.pendingCommand = null }, this.delayStateDisplay)
}, delay)
}
}
}
}