UNPKG

v-mask

Version:

Tiny input mask library for Vue.js based on text-mask-core (~5kb) exposed as directive. No dependencies

163 lines (138 loc) 4.75 kB
/* eslint-disable no-param-reassign */ // eslint-disable-next-line import/no-extraneous-dependencies import conformToMask from 'text-mask-core/src/conformToMask'; import { stringMaskToRegExpMask, arrayMaskToRegExpMask } from './maskToRegExpMask'; import { trigger, queryInputElementInside, isFunction, isString, isRegexp, } from './utils'; import createOptions from './createOptions'; import { defaultMaskReplacers } from './constants'; const options = createOptions(); /** * Convenience wrapper for `trigger(el, 'input')`, which raises * an event for Vue to detect a value change. * * @param {HTMLInputElement} el */ function triggerInputUpdate(el) { trigger(el, 'input'); } /** * Event handler * @param {HTMLInputElement} el * @param {Boolean} force */ function updateValue(el, force = false) { const { value } = el; const { previousValue, mask } = options.get(el); const isValueChanged = value !== previousValue; const isLengthIncreased = value.length > previousValue.length; const isUpdateNeeded = value && isValueChanged && isLengthIncreased; if ((force || isUpdateNeeded) && mask) { const { conformedValue } = conformToMask(value, mask, { guide: false }); el.value = conformedValue; triggerInputUpdate(el); } options.partiallyUpdate(el, { previousValue: value }); } /** * Fires on handler update * @param {HTMLInputElement} el * @param {String|Array.<String|RegExp>|Function|null} inputMask */ function updateMask(el, inputMask, maskReplacers) { let mask; if (Array.isArray(inputMask)) { mask = arrayMaskToRegExpMask(inputMask, maskReplacers); } else if (isFunction(inputMask)) { mask = inputMask; } else if (isString(inputMask) && inputMask.length > 0) { mask = stringMaskToRegExpMask(inputMask, maskReplacers); } else { mask = inputMask; } options.partiallyUpdate(el, { mask }); } /** * Merge custom mask replacers with default mask replacers * @param {Object<string, RegExp>} maskReplacers * @param {Object<string, RegExp>} baseMaskReplacers * @return {Object} The extended mask replacers */ function extendMaskReplacers(maskReplacers, baseMaskReplacers = defaultMaskReplacers) { if (maskReplacers === null || Array.isArray(maskReplacers) || typeof maskReplacers !== 'object') { return baseMaskReplacers; } return Object.keys(maskReplacers).reduce((extendedMaskReplacers, key) => { const value = maskReplacers[key]; if (value !== null && !(value instanceof RegExp)) { return extendedMaskReplacers; } return { ...extendedMaskReplacers, [key]: value }; }, baseMaskReplacers); } /** * Convert a mask into a string for comparison * @param {String|Array.<String|RegExp>} mask */ function maskToString(mask) { const maskArray = Array.isArray(mask) ? mask : [mask]; const filteredMaskArray = maskArray.filter((part) => isString(part) || isRegexp(part)); return filteredMaskArray.toString(); } /** * Create the Vue directive * @param {Object} directiveOptions * @param {Object.<string, RegExp>} directiveOptions.placeholders * @return {Object} The Vue directive */ export function createDirective(directiveOptions = {}) { const instanceMaskReplacers = extendMaskReplacers( directiveOptions && directiveOptions.placeholders, ); /** * Vue directive definition */ return { /** * Called only once, when the directive is first bound to the element. * This is where you can do one-time setup work. * * @param {(HTMLInputElement|HTMLElement)} el * @param {?String} value */ bind(el, { value }) { el = queryInputElementInside(el); updateMask(el, value, instanceMaskReplacers); updateValue(el); }, /** * Called after the containing component has updated, * but possibly before its children have updated. * The directive’s value may or may not have changed, * but you can skip unnecessary updates by comparing the * binding’s current and old values. * * @param {(HTMLInputElement|HTMLElement)} el * @param {?String} value * @param {?String} oldValue */ componentUpdated(el, { value, oldValue }) { el = queryInputElementInside(el); const isMaskChanged = isFunction(value) || maskToString(oldValue) !== maskToString(value); if (isMaskChanged) { updateMask(el, value, instanceMaskReplacers); } updateValue(el, isMaskChanged); }, unbind(el) { el = queryInputElementInside(el); options.remove(el); }, }; } /** * Default directive definition */ export default createDirective();