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
JavaScript
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;
;