Last active
February 22, 2023 10:16
-
-
Save jonasraoni/9dea65e270495158393f54e36ee6b78d to your computer and use it in GitHub Desktop.
Numeric directive for Vue (v-decimal and v-integer, with an "unsigned" modifier). Useful to improve existing components which you can't modify.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//+ Jonas Raoni Soares Silva | |
//@ http://raoni.org | |
export default class NumericDirective { | |
constructor(input, binding) { | |
Object.assign(this, { | |
input, | |
binding | |
}); | |
input.addEventListener('keydown', this); | |
input.addEventListener('change', this); | |
} | |
static install(Vue) { | |
Vue.directive('decimal', this.directive); | |
Vue.directive('integer', this.directive); | |
} | |
static directive = { | |
bind(el, binding) { | |
el = el instanceof HTMLInputElement ? el : el.querySelector("input"); | |
if (el) { | |
return new NumericDirective(el, binding); | |
} | |
} | |
} | |
handleEvent(event) { | |
this[event.type](event); | |
} | |
keydown(event) { | |
const { target, key, keyCode, ctrlKey, metaKey } = event; | |
if (!( | |
// Is numeric | |
(key >= '0' && key <= '9') || | |
// Is special symbol allowed (. and -) | |
( | |
((key === '.' && this.binding.name === 'decimal') || (key === '-' && !this.binding.modifiers.unsigned)) && | |
!~target.value.indexOf(key) | |
) || | |
// Is system key | |
[ | |
'Delete', 'Backspace', 'Tab', 'Esc', 'Escape', 'Enter', | |
'Home', 'End', 'PageUp', 'PageDown', 'Del', 'Delete', | |
'Left', 'ArrowLeft', 'Right', 'ArrowRight', 'Insert', | |
'Up', 'ArrowUp', 'Down', 'ArrowDown' | |
].includes(key) || | |
// Is ctrl + a, c, x, v | |
((ctrlKey || metaKey) && [65, 67, 86, 88].includes(keyCode)) | |
)) { | |
event.preventDefault(); | |
} | |
} | |
change({ target }) { | |
const isDecimal = this.binding.name === 'decimal'; | |
let value = target.value; | |
if (!value) { | |
return; | |
} | |
// Is it a negative number and is it allowed? | |
const isNegative = /^\s*-/.test(value) && !this.binding.modifiers.unsigned; | |
// Remove invalid digits (if it's a decimal, then allows "," and "." to stay) | |
value = value.replace(isDecimal ? /[^\d,.]/g : /\D/g, ''); | |
if (isDecimal) { | |
// Naive adjustment for decimal values, breaks the number by ",." and considers the last group as the decimal part | |
const pieces = value.split(/[,.]/); | |
// Removes useless zeroes on the right | |
const decimal = pieces.pop().replace(/0+$/, ''); | |
if (pieces.length) { | |
value = `${pieces.join('') || (decimal ? '0' : '')}${decimal ? `.${decimal}` : ''}`; | |
} | |
} | |
// Removes useless zeroes on the left | |
value = value.replace(/^(?:0(?!\b))+/, ''); | |
if (value && isNegative) { | |
value = `-${value}`; | |
} | |
// Raise a fake event to signal others that we've updated the field | |
if (target.value !== value) { | |
target.value = value; | |
const event = document.createEvent('UIEvent'); | |
event.initEvent('input', true, false, window, 0); | |
target.dispatchEvent(event); | |
} | |
} | |
} |
Hi @jonasraoni, thanks for the good example! 🙏
By adding the following line at the beginning of the bind hook, you could enable the directive conditionally:
bind (el, binding) {
if (binding.hasOwnProperty('value') && ! binding.value) {
return;
}
el = el instanceof HTMLInputElement ? el : el.querySelector("input");
if (el) {
return new NumericDirective(el, binding);
}
}
That way you could do both v-integer
or v-integer="false"
.
Thank you for your case!!!
Thank you brother this helpfull I will try understand .
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for confirming and accepting the suggestion, @jonasraoni! 🙇♂️ Perhaps you can add the link to your improved version here, as a comment, so people who find this can also check the enhanced version as well. 🚀