Custom spinners that use step for increment, but enforce multipleOf
This commit is contained in:
parent
e66fe5a35a
commit
ec9fbd6245
1 changed files with 97 additions and 22 deletions
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="input-and-buttons-container">
|
<div class="input-and-buttons-container">
|
||||||
<select
|
<select
|
||||||
v-model="internalValue"
|
v-model="internalValue"
|
||||||
class="uk-form-small numeric-setting-line-input dropdown"
|
class="uk-form-small property-input dropdown"
|
||||||
@change="sendValue"
|
@change="sendValue"
|
||||||
>
|
>
|
||||||
<option v-for="(value, display) in options" :key="value" :value="value">
|
<option v-for="(value, display) in options" :key="value" :value="value">
|
||||||
|
|
@ -18,26 +18,32 @@
|
||||||
<label v-if="!useDropdown && dataType == 'number'" class="uk-form-label"
|
<label v-if="!useDropdown && dataType == 'number'" class="uk-form-label"
|
||||||
>{{ label }}
|
>{{ label }}
|
||||||
<div class="input-and-buttons-container">
|
<div class="input-and-buttons-container">
|
||||||
<input
|
<div class="uk-inline number-wrapper">
|
||||||
ref="numericalInput"
|
<input
|
||||||
v-model="internalValue"
|
ref="numericalInput"
|
||||||
class="uk-form-small numeric-setting-line-input"
|
v-model="internalValue"
|
||||||
:class="{ edited: isEdited, flash: animateUpdate }"
|
class="uk-form-small property-input numeric-property"
|
||||||
:disabled="isDisabled"
|
:class="{ edited: isEdited, flash: animateUpdate }"
|
||||||
type="number"
|
:disabled="isDisabled"
|
||||||
:min="minimum"
|
type="number"
|
||||||
:max="maximum"
|
:min="minimum"
|
||||||
:step="spinner_step"
|
:max="maximum"
|
||||||
@input="grabFocus"
|
@wheel="handleSpiner"
|
||||||
@focusout="focusOut"
|
@input="grabFocus"
|
||||||
@keydown="keyDown"
|
@focusout="focusOut"
|
||||||
@animationend="animationEnd"
|
@keydown="keyDown"
|
||||||
/>
|
@animationend="animationEnd"
|
||||||
|
/>
|
||||||
|
<div class="spinner-buttons">
|
||||||
|
<button type="button" @click="increment(1)" @mousedown="spinnerClick">▲</button>
|
||||||
|
<button type="button" @click="increment(-1)" @mousedown="spinnerClick">▼</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<sync-property-button @click="requestUpdate" />
|
<sync-property-button @click="requestUpdate" />
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<div v-if="!useDropdown && dataType == 'boolean'" class="input-and-buttons-container">
|
<div v-if="!useDropdown && dataType == 'boolean'" class="input-and-buttons-container">
|
||||||
<label class="uk-form-label numeric-setting-line-input">
|
<label class="uk-form-label property-input">
|
||||||
<input
|
<input
|
||||||
ref="checkbox"
|
ref="checkbox"
|
||||||
v-model="internalValue"
|
v-model="internalValue"
|
||||||
|
|
@ -57,7 +63,7 @@
|
||||||
v-for="i in valueLength"
|
v-for="i in valueLength"
|
||||||
:key="i"
|
:key="i"
|
||||||
v-model="internalValue[i - 1]"
|
v-model="internalValue[i - 1]"
|
||||||
class="uk-form-small numeric-setting-line-input"
|
class="uk-form-small property-input"
|
||||||
:class="{ edited: isEdited, flash: animateUpdate }"
|
:class="{ edited: isEdited, flash: animateUpdate }"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
type="number"
|
type="number"
|
||||||
|
|
@ -76,7 +82,7 @@
|
||||||
<div class="input-and-buttons-container">
|
<div class="input-and-buttons-container">
|
||||||
<input
|
<input
|
||||||
v-model="internalValue[key]"
|
v-model="internalValue[key]"
|
||||||
class="uk-form-small numeric-setting-line-input"
|
class="uk-form-small property-input"
|
||||||
:class="{ edited: isEdited, flash: animateUpdate }"
|
:class="{ edited: isEdited, flash: animateUpdate }"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
type="number"
|
type="number"
|
||||||
|
|
@ -94,7 +100,7 @@
|
||||||
<div class="input-and-buttons-container">
|
<div class="input-and-buttons-container">
|
||||||
<input
|
<input
|
||||||
v-model="internalValue"
|
v-model="internalValue"
|
||||||
class="uk-form-small numeric-setting-line-input"
|
class="uk-form-small property-input"
|
||||||
:class="{ edited: isEdited, flash: animateUpdate }"
|
:class="{ edited: isEdited, flash: animateUpdate }"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -110,7 +116,7 @@
|
||||||
<div class="input-and-buttons-container">
|
<div class="input-and-buttons-container">
|
||||||
<input
|
<input
|
||||||
v-model="internalValue"
|
v-model="internalValue"
|
||||||
class="uk-form-small numeric-setting-line-input"
|
class="uk-form-small property-input"
|
||||||
type="text"
|
type="text"
|
||||||
disabled="true"
|
disabled="true"
|
||||||
/>
|
/>
|
||||||
|
|
@ -321,6 +327,37 @@ export default {
|
||||||
this.sendValue();
|
this.sendValue();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
increment(sign) {
|
||||||
|
const step = this.spinner_step || 1;
|
||||||
|
const multipleOf = this.dataSchema.multipleOf;
|
||||||
|
let newValue = Number(this.internalValue) || 0;
|
||||||
|
newValue += sign * step;
|
||||||
|
// Apply minimum if defined
|
||||||
|
if (this.minimum !== undefined && this.minimum !== null) {
|
||||||
|
newValue = Math.max(newValue, this.minimum);
|
||||||
|
}
|
||||||
|
// Apply maximum if defined
|
||||||
|
if (this.maximum !== undefined && this.maximum !== null) {
|
||||||
|
newValue = Math.min(newValue, this.maximum);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If mulipleOf is validated on server then enforce it.
|
||||||
|
if (multipleOf) {
|
||||||
|
newValue = Math.round(newValue / multipleOf) * multipleOf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rounding to about 5 sig figs to stop weird javascript rounding.
|
||||||
|
const magnitude = Math.floor(Math.log10(Math.abs(newValue)));
|
||||||
|
const factor = 10 ** -(Math.floor(magnitude) - 4);
|
||||||
|
this.internalValue = Math.round(newValue * factor) / factor;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Grab focus for the numerical input when a spinner is clicked.
|
||||||
|
*/
|
||||||
|
spinnerClick(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.$refs.numericalInput.focus();
|
||||||
|
},
|
||||||
/** Grab the browser focus.
|
/** Grab the browser focus.
|
||||||
*
|
*
|
||||||
* This is needed for numerical elements to be in focus when a spinner
|
* This is needed for numerical elements to be in focus when a spinner
|
||||||
|
|
@ -378,12 +415,50 @@ export default {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.numeric-setting-line-input {
|
.property-input {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
width: 6em;
|
width: 6em;
|
||||||
}
|
}
|
||||||
|
/*Hide standard spinners as it isn't possible to customise the browser ones.*/
|
||||||
|
.numeric-property {
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
.numeric-property::-webkit-outer-spin-button,
|
||||||
|
.numeric-property::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.number-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-content: stretch;
|
||||||
|
align-items: center;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner-buttons {
|
||||||
|
position: absolute;
|
||||||
|
right: 4px;
|
||||||
|
top: 1px;
|
||||||
|
bottom: 5px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner-buttons button {
|
||||||
|
height: 45%;
|
||||||
|
padding: 0 6px;
|
||||||
|
line-height: 1;
|
||||||
|
font-size: 10px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
.dropdown {
|
.dropdown {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue