Added debouncing for ui update functions

This commit is contained in:
Stefan Zermatten
2019-02-06 17:26:56 +02:00
parent de4509ab6a
commit bf2e9439cf
4 changed files with 118 additions and 62 deletions

View File

@@ -1,16 +1,20 @@
<template lang="html">
<div>
<attribute-edit
v-for="attribute in attributes"
v-for="(attribute, index) in attributes"
:key="attribute._id"
:attribute="attribute"
@change="log"
@change="e => {writeChange(e, index); log(e)}"
/>
<div class="ma-4" v-for="(attribute, index) in attributes">
{{attribute}}
</div>
</div>
</template>
<script>
import AttributeEdit from '/imports/ui/components/AttributeEdit.vue';
import debounceUpdate from '/imports/ui/utility/debounceUpdate.js';
export default {
components: {
AttributeEdit,
@@ -34,15 +38,23 @@
},
],
}},
methods: {
log: console.log,
change(index, e){
created () {
// Doing this here instead of in methods ensures every instance has its
// own debounced function
this.writeChange = debounceUpdate((e, index) => {
// Do work storing the change, this is where we'd write to the database
for (let i in e){
if (typeof e[i] === 'string'){
e[i] = e[i].trim();
}
this.attributes[index][i] = e[i];
}
},
});
},
}
methods: {
log: console.log,
},
};
</script>
<style lang="css" scoped>

View File

@@ -3,25 +3,26 @@
<v-text-field
label="Name"
:value="attribute.name"
@input="name => $emit('change', {name})"
/>
<v-text-field
label="Variable name"
:value="attribute.variableName"
@change="variableName => $emit('change', {variableName})"
@input="variableName => $emit('change', {variableName})"
hint="Use this name in formulae to reference this attribute"
/>
<v-text-field
label="Base Value"
type="number"
:value="attribute.baseValue"
@change="baseValue => $emit('change', {baseValue})"
@input="baseValue => $emit('change', {baseValue})"
hint="This is the value of the attribute before effects are applied"
/>
<v-text-field
label="Damage"
type="number"
:value="-attribute.adjustment"
@change="damage => $emit('change', {adjustment: -damage})"
@input="damage => $emit('change', {adjustment: -damage || null})"
/>
<v-select
label="Type"
@@ -29,12 +30,12 @@
:items="attributeTypes"
:value="attribute.type"
:menu-props="{auto: true, lazy: true}"
@change="type => $emit('change', {type})"
@input="type => $emit('change', {type})"
/>
<v-switch
label="Allow decimal values"
:value="attribute.decimal"
@change="decimal => $emit('change', {decimal})"
@change="e => $emit('change', {decimal: e})"
/>
<v-select
label="Reset"
@@ -44,65 +45,65 @@
:items="resetOptions"
:value="attribute.reset"
:menu-props="{auto: true, lazy: true}"
@change="reset => $emit('change', {reset})"
@input="reset => $emit('change', {reset})"
/>
<v-text-field
label="Reset Multiplier"
type="number"
:value="attribute.resetMultiplier"
@change="resetMultiplier => $emit('change', {resetMultiplier})"
@input="resetMultiplier => $emit('change', {resetMultiplier})"
hint="Some attributes, like hit dice, only reset by half their total on a long rest"
/>
</div>
</template>
<script>
export default {
props: {
attribute: {
type: Object,
default: {},
},
},
data(){ return{
attributeTypes: [
{
text: 'Ability score',
value: 'ability',
}, {
text: 'Stat',
value: 'stat',
}, {
text: 'Modifier',
value: 'modifier',
}, {
text: 'Hit dice',
value: 'hitDice',
}, {
text: 'Health bar',
value: 'healthBar',
}, {
text: 'Resource',
value: 'resource',
}, {
text: 'Spell slot',
value: 'spellSlot',
}, {
text: 'Utility',
value: 'utility',
export default {
props: {
attribute: {
type: Object,
default: {},
},
],
resetOptions: [
{
text: 'Short rest',
value: 'shortRest',
}, {
text: 'Long rest',
value: 'longRest',
}
]
}},
}
},
data(){ return{
attributeTypes: [
{
text: 'Ability score',
value: 'ability',
}, {
text: 'Stat',
value: 'stat',
}, {
text: 'Modifier',
value: 'modifier',
}, {
text: 'Hit dice',
value: 'hitDice',
}, {
text: 'Health bar',
value: 'healthBar',
}, {
text: 'Resource',
value: 'resource',
}, {
text: 'Spell slot',
value: 'spellSlot',
}, {
text: 'Utility',
value: 'utility',
},
],
resetOptions: [
{
text: 'Short rest',
value: 'shortRest',
}, {
text: 'Long rest',
value: 'longRest',
}
]
}},
};
</script>
<style lang="css" scoped>

View File

@@ -9,7 +9,7 @@
:menu-props="{transition: 'slide-y-transition', lazy: true}"
:items="operations"
:value="this.effect.operation"
@change="operation => $emit('change', {operation})"
@input="operation => $emit('change', {operation})"
>
<v-icon
class="black--text icon"
@@ -35,7 +35,7 @@
:value="needsValue ? (effect.calculation) : ' '"
:disabled="!needsValue"
:hint="!isFinite(effect.calculation) && effect.result ? effect.result + '' : '' "
@change="calculation => $emit('change', {calculation})"
@input="calculation => $emit('change', {calculation})"
/>
</v-flex>
@@ -49,7 +49,7 @@
:menu-props="{transition: 'slide-y-transition', lazy: true}"
:value="effect.stat"
:items="stats"
@change="stat => $emit('change', {stat})"
@input="stat => $emit('change', {stat})"
/>
</v-flex>

View File

@@ -0,0 +1,43 @@
/*
* Creates a function that takes an object which represents the updates to be
* made as key: value pairs. The function collects these updates, (overwriting
* duplicates), and runs the given callback with the collated updates after the
* `time` has passed without the function being called.
*/
export default function debounceUpdate(callback, time = 300){
let collatedUpdate = {};
let interval;
return (update, ...rest) => {
// Every time we're called, collect the update
for (key in update){
collatedUpdate[key] = update[key];
}
// Reset the clock and set it again to run the callback
clearTimeout(interval);
interval = setTimeout(() => {
// Clock timed out, apply all the collated updates
callback(collatedUpdate, ...rest);
// Reset the collation
collatedUpdate = {};
}, time);
};
};
// Example use
//
// let f = update => {
// console.log({wroteUpdate: update});
// };
// let df = debounceUpdate(f, 300);
// df({name: 'john'});
// df({name: 'jack'});
// df({age: 24, country: 'Ethiopia'});
// df({transport: 'bus'});
//
// --> { wroteUpdate: {
// name: 'jack',
// age: 24,
// country: 'Ethiopia',
// transport: 'bus'
// }}