dashboard/components/form/UnitInput.vue

207 lines
4.5 KiB
Vue

<script>
import { parseSi, formatSi, UNITS, FRACTIONAL } from '@/utils/units';
import LabeledInput from '@/components/form/LabeledInput';
import { _EDIT } from '@/config/query-params';
export default {
components: { LabeledInput },
props: {
// Convert output to string
// Output will also be a string regardless of this prop if outputModifier = true
outputAs: {
type: String,
default: 'number',
},
/* Append exponential modifier in output, eg "123Mi"
If this is false while inputExponent is true, the output val will be converted to base units
eg user is views in terms of MiB but integer values corresponding to B are actually emitted
*/
outputModifier: {
type: Boolean,
default: false
},
/* Set modifier on base unit - positive vals map to UNITS array, negative to FRACTIONAL
String input values with si notation will be converted to this measurement unit,
eg "1Gi" will become "1024Mi" if this is set to 2
UNITS = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
FRACTIONAL = ['', 'm', 'u', 'n', 'p', 'f', 'a', 'z', 'y'];
*/
inputExponent: {
type: Number,
default: 0,
},
/* Combines with inputExponent to make displayed unit.
Use 'suffix' if the input's units are strictly for display
*/
baseUnit: {
type: String,
default: 'B',
},
// If set to 1024, binary modifier will be used eg MiB instead of MB
increment: {
type: Number,
default: 1000,
},
/* Ignore baseUnit and inputExponent in favor of a display-only suffix
display/emit integers without SI conversion
*/
suffix: {
type: String,
default: null,
},
// LabeledInput props
mode: {
type: String,
default: _EDIT
},
value: {
type: [Number, String],
default: null
},
label: {
type: String,
default: null
},
labelKey: {
type: String,
default: null
},
tooltip: {
type: [String, Object],
default: null
},
tooltipKey: {
type: String,
default: null
},
required: {
type: Boolean,
default: false,
},
min: {
type: [Number, String],
default: 0
},
placeholder: {
type: [String, Number],
default: ''
},
},
computed: {
addon() {
if (!this.suffix) {
return false;
}
return this.unit + this.suffix;
},
unit() {
let out;
if ( this.inputExponent >= 0 ) {
out = UNITS[this.inputExponent];
} else {
out = FRACTIONAL[-1 * this.inputExponent];
}
if (this.increment === 1024 && out) {
out += 'i';
}
return out;
},
// Parse string with unit modifier to base unit eg "1m" -> 0.001
parsedValue() {
return typeof this.value === 'string' ? parseSi(this.value) : this.value;
},
// Convert integer value
displayValue() {
let displayValue = '';
if ( this.parsedValue || this.parsedValue === 0) {
displayValue = formatSi(this.parsedValue, {
increment: this.increment,
addSuffix: false,
maxExponent: this.inputExponent,
minExponent: this.inputExponent,
});
}
return displayValue ;
},
displayUnit() {
if (this.suffix) {
return this.suffix;
}
return this.unit + this.baseUnit;
}
},
methods: {
update(inputValue) {
let out = inputValue === '' ? null : inputValue;
if (this.outputModifier) {
out = out === null ? null : `${ inputValue }${ this.unit }`;
} else if ( this.outputAs === 'string' ) {
out = out === null ? '' : `${ inputValue }`;
} else if (out) {
out = this.unit ? parseSi(`out${ this.unit }`) : parseInt(out);
}
this.$emit('input', out);
},
}
};
</script>
<template>
<LabeledInput
:value="displayValue"
v-bind="$attrs"
type="number"
:min="min"
:mode="mode"
:label="label"
:label-key="labelKey"
:tooltip="tooltip"
:tooltip-key="tooltipKey"
:required="required"
:placeholder="placeholder"
@input="update($event)"
>
<template #suffix>
<div v-if="displayUnit" class="addon" :class="{'with-tooltip': tooltip || tooltipKey}">
{{ displayUnit }}
</div>
</template>
</LabeledInput>
</template>
<style lang="scss" scoped>
.addon.with-tooltip {
position: relative;
right: 30px;
}
</style>