UNPKG

react-maskinput

Version:

React mask input component. Allow to input formatted values with fixed length or apply custom formtatting function, to format values with any length

255 lines (254 loc) 10.1 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); var React = require("react"); var input_core_1 = require("input-core"); var KEYBOARD = { BACKSPACE: 8, DELETE: 46, }; /** * React-MaskInput component * Params: * `mask`: String. Format: * 0 — any number 0-9 * * — any symbol * a — A-Z, a-z * q — "q" letter, 2 — "2" letter etc. * \a — "a" letter * default is undefined * * [function] `reformat`: user function, if you want use custom reformat logic. It's userfull for numeric inputs. * If reformat defined mask'll be ignored. Reformat function must receive object with several fields: * function reformat({data: data, selection: {start, end}, input}) { * // realisation * * return { * [any] value: value that store and calling in input core funcitons (such as reformat). value may have any format, * [String] visibleValue: value that displayed to user in input if showMask is false, * [String] maskedValue: value that displayed to user in input if showMask is true, * [{[integer] start, [integer] end}] selection: {start, end} — new selection range * } * } * * if `reformat` and `mask` is undefined, input allow to enter any values. * * You can define custom mask by passing `maskFormat`. This prop must be an array, * each object in array have several fields: * str: matched char for mask * regexp: validation rule as regexp * type: special * * `maskChar`: Character to cover unfilled editable parts of mask. Default value is ''. * `maskString`: String to cover unfilled editable parts of mask. Default is undefined. If maskString define maskChar ignored. * * showMask: show mask in input. It's possible only if mask have not cyclic. Default value = false * alwaysShowMask: show mask when input inactive * * Callbacks: * onValueChange(event). event is: * maskedValue: masked value, * value: value without nessesary mask * getReference: callback to get input ref * onChange(event) where event is a regular React.SyntheticEvent. Using this event you can get access to HTMLElement directly * All other props'll passed to input directly */ function MaskInput(props) { var _a; var input = React.useMemo(function () { return input_core_1.createInput({ value: props.value || props.defaultValue || '', reformat: props.reformat, maskString: props.maskString, maskChar: props.maskChar || input_core_1.defaults.maskChar, mask: props.mask || undefined, maskFormat: props.maskFormat || input_core_1.defaults.maskFormat, }); }, []); var firstRender = React.useRef(true); var canSetSelection = React.useRef(false); var inputEl = React.useRef(); var _b = React.useState(props.alwaysShowMask || props.showMask), showMask = _b[0], setShowMask = _b[1]; var getSelection = React.useCallback(function () { input.setSelection({ start: inputEl.current.selectionStart, end: inputEl.current.selectionEnd, }); }, [input]); var setSelection = React.useCallback(function () { if (!canSetSelection.current) { return; } var selection = input.getSelection(); inputEl.current.setSelectionRange(selection.start, selection.end); var raf = window.requestAnimationFrame || window.webkitRequestAnimationFrame || // @ts-ignore window.mozRequestAnimationFrame || (function (fn) { return setTimeout(fn, 0); }); raf(function () { return inputEl.current.setSelectionRange(selection.start, selection.end); }); }, [input]); var showValue = React.useCallback(function () { if (showMask && (canSetSelection.current || props.alwaysShowMask)) { inputEl.current.value = input.getState().maskedValue; return; } inputEl.current.value = input.getState().visibleValue; }, [showMask, props.alwaysShowMask, input]); React.useEffect(function () { if (!firstRender.current) { setShowMask(props.alwaysShowMask || props.showMask); } }, [props.alwaysShowMask, props.showMask]); React.useEffect(function () { if (!firstRender.current) { input.setReformat(props.reformat); } }, [props.reformat]); React.useEffect(function () { if (!firstRender.current) { input.setMaskFormat(props.maskFormat); } }, [props.maskFormat]); React.useEffect(function () { if (!firstRender.current) { input.setMask(props.mask); } }, [props.mask]); React.useEffect(function () { if (!firstRender.current) { input.setMask(props.mask); } }, [props.mask]); React.useEffect(function () { if (!firstRender.current) { input.setMaskString(props.maskString); } }, [props.maskString]); React.useEffect(function () { if (!firstRender.current) { input.setMaskChar(props.maskChar); } }, [props.maskChar]); React.useEffect(function () { if (!firstRender.current) { input.setValue(props.value); } }, [props.value]); React.useEffect(function () { firstRender.current = false; showValue(); }, [firstRender.current, input]); React.useEffect(function () { var subscriber = function () { showValue(); setSelection(); }; input.subscribe(subscriber); return function () { input.unsubscribe(subscriber); }; }, [input, showValue, setSelection]); React.useEffect(function () { props.getReference && props.getReference(inputEl.current); }, [props.getReference]); var keyPressPropName = React.useCallback(function () { if (typeof navigator !== 'undefined' && navigator.userAgent.match(/Android/i)) { return 'onBeforeInput'; } return 'onKeyPress'; }, []); var dispatchEvent = function (e) { props.onChange && props.onChange(e); var _a = input.getState(), maskedValue = _a.maskedValue, visibleValue = _a.visibleValue; onValueChange && onValueChange({ maskedValue: maskedValue, value: visibleValue }); }; var onPaste = function (e) { e.preventDefault(); getSelection(); // getData value needed for IE also works in FF & Chrome input.paste(e.clipboardData.getData('Text')); // Timeout needed for IE setTimeout(setSelection, 0); dispatchEvent(e); }; var onChange = function (e) { var currentValue; if (showMask && (canSetSelection.current || props.alwaysShowMask)) { currentValue = input.getState().maskedValue; } else { currentValue = input.getState().visibleValue; } // fix conflict by update value in mask model if (e.target.value !== currentValue) { getSelection(); input.setValue(e.target.value); setTimeout(setSelection, 0); } dispatchEvent(e); }; var onKeyPress = function (e) { if (e.metaKey || e.altKey || e.ctrlKey || e.key === 'Enter') { return; } e.preventDefault(); getSelection(); input.input(e.key || e.data || String.fromCharCode(e.which)); setSelection(); dispatchEvent(e); }; var onKeyDown = function (e) { if (e.which === KEYBOARD.BACKSPACE) { e.preventDefault(); getSelection(); input.removePreviosOrSelected(); setSelection(); dispatchEvent(e); } if (e.which === KEYBOARD.DELETE) { e.preventDefault(); getSelection(); input.removeNextOrSelected(); setSelection(); dispatchEvent(e); } }; var onFocus = function (e) { canSetSelection.current = true; props.onFocus && props.onFocus(e); }; var onBlur = function (e) { canSetSelection.current = false; props.onBlur && props.onBlur(e); }; var ignoreOnChange = props.onChange, /* ignore unspecific props for input */ onValueChange = props.onValueChange, mask = props.mask, getReference = props.getReference, ignoreShowMask = props.showMask, maskChar = props.maskChar, alwaysShowMask = props.alwaysShowMask, maskFormat = props.maskFormat, maskString = props.maskString, reformat = props.reformat, /* ignore values */ value = props.value, defaultValue = props.defaultValue, inputProps = __rest(props, ["onChange", "onValueChange", "mask", "getReference", "showMask", "maskChar", "alwaysShowMask", "maskFormat", "maskString", "reformat", "value", "defaultValue"]); var keyPressEvent = (_a = {}, _a[keyPressPropName()] = onKeyPress, _a); return (React.createElement("input", __assign({}, inputProps, { onChange: onChange, onKeyDown: onKeyDown, onPaste: onPaste, onFocus: onFocus, onBlur: onBlur }, keyPressEvent, { ref: inputEl }))); } exports.default = MaskInput;