-
-
Save jonasraoni/9dea65e270495158393f54e36ee6b78d to your computer and use it in GitHub Desktop.
//+ 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); | |
} | |
} | |
} |
Hey, @jonasraoni!
Thanks for the good work! π
May I suggest an improvement to make it work smoothly also on Macs?
Line 30, replace with:
const { target, key, keyCode, ctrlKey, metaKey } = event;
And replace line 44 with:
((ctrlKey || metaKey) && [65, 67, 86, 88].includes(keyCode))
That should be enough for enabling CTRL+A, C , X , V
on Macs (which uses the CMD
instead of CTRL
for these actions). π€
Thanks @rogeriotaques! I'm unable to test on Apple devices, but it works on IE11 π©
- I think that filtering special keys smells bad haha (the issue you found confirms that), so I've created an improved version, using another technique, I just left this one here to avoid throwing code in trash π€
- Enforcing formatting is also something that I don't like... But I didn't try to solve this issue yet, as it wasn't needed for my use case.
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. π
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 .
Thank you ! @jonasraoni I will check it.