Skip to content

Instantly share code, notes, and snippets.

@FranciscoG
Last active May 28, 2020 21:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FranciscoG/97035afb29b53f61d15662ff41710048 to your computer and use it in GitHub Desktop.
Save FranciscoG/97035afb29b53f61d15662ff41710048 to your computer and use it in GitHub Desktop.
Vue custom directive that only allows certain keys to be typed into an element that accepts keyboard input
/**
* `v-kesAllowed` directive
*
* This works by blocking keys during onKeyDown by using a whitelist regex and
* testing `event.key`
*/
/**
* these keys should be allowed in all cases because they handle traversing and a11y
*/
const baseAllowedRegex = new RegExp('Backspace|ArrowLeft|ArrowRight|ArrowUp|ArrowDown|Tab|Delete')
/**
* allow only numbers
*/
const numbersRegex = new RegExp(`[0-9]`)
/**
* Similar to numbers but also allows commas minus the currency symbol
*/
const currencyRegex = new RegExp(`[0-9,]`)
/**
* Regex to be used for any part of a person's full name with full support for
* international language character
*
* u flag for 'unicode'
* https://www.regular-expressions.info/unicode.html
*
* \p{L} - any kind of letter from any language. case insensitive
*
* \p{Zs} - any unicode space character that takes up visible space
*
* \p{Pd} - any kind of hyphen or dash.
*
* periods - for things like: Jr. Sr.
*
* apostrophe (aka single quote) - for names like O'Malley
*
* This should cover _most_ names in the US, Europe, and probably the world
*
*/
let namesRegex
try {
namesRegex = new RegExp(`[\\p{L}\\p{Zs}\\p{Pd}\\.\\\\']`, 'u')
} catch (e) {
// Unicode Property Escapes is very new in JS so not all browsers support it. This is a fallback
// that is specifically made for the western world.
// It supports:
// Latin-1 Supplement, Latin Extended-A, Latin Extended-B, Latin Extended Additional
// If you need full worldwide language support you can use this to generate a regex:
// https://mothereff.in/regexpu#input=var+regex+%3D+/%5Cp%7BL%7D/u%3B&unicodePropertyEscape=1
namesRegex = new RegExp(`[a-z\u00C0-\u024F\u1E00-\u1EFF\\s-']`, 'i')
}
/**
* Just letters from any language, like in Middle initials
*/
let lettersRegex
try {
lettersRegex = new RegExp('\\p{L}', 'u')
} catch (e) {
lettersRegex = new RegExp(`[a-z\u00C0-\u024F\u1E00-\u1EFF]`, 'i')
}
function onKeyDown (re) {
return function handler (e) {
if (e.metaKey) {
// allow shortcuts like Ctrl/Command + A
return true
}
if (re.test(e.key) || baseAllowedRegex.test(e.key)) {
return true
}
e.preventDefault()
return false
}
}
export const AllowedTypes = {
CURRENCY: 'currency',
NAMES: 'names',
NUMBERS: 'numbers',
LETTERS: 'letters'
}
/**
* Directive that whitelists keys allowed to be typed inside an input and
* prevents anything else from being typed
*
* <Component v-keysAllowed="AllowedTypes.CURRENCY" />
*
* TODO: instead of accepting a string, use modifiers
* <Component v-keysAllowed.currency />
* OR
* <Component v-keysAllowed:currency />
*/
export default {
bind (el, binding, vnode) {
switch (binding.value) {
case AllowedTypes.CURRENCY:
self.handler = onKeyDown(currencyRegex)
break
case AllowedTypes.NAMES:
self.handler = onKeyDown(namesRegex)
break
case AllowedTypes.NUMBERS:
self.handler = onKeyDown(numbersRegex)
break
case AllowedTypes.LETTERS:
self.handler = onKeyDown(lettersRegex)
break
default:
self.handler = null
}
if (self.handler) {
el.addEventListener('keydown', self.handler)
}
},
unbind (el, binding, vnode) {
if (self.handler) {
el.removeEventListener('keydown', self.handler)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment