react-imask
Version:
React input mask
311 lines (302 loc) • 11.9 kB
JavaScript
'use strict';
var IMask = require('imask');
var React = require('react');
var PropTypes = require('prop-types');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var IMask__default = /*#__PURE__*/_interopDefault(IMask);
var React__default = /*#__PURE__*/_interopDefault(React);
var PropTypes__default = /*#__PURE__*/_interopDefault(PropTypes);
const MASK_PROPS = {
// common
mask: PropTypes__default.default.oneOfType([PropTypes__default.default.array, PropTypes__default.default.func, PropTypes__default.default.string, PropTypes__default.default.instanceOf(RegExp), PropTypes__default.default.oneOf([Date, Number, IMask__default.default.Masked]), PropTypes__default.default.instanceOf(IMask__default.default.Masked)]),
value: PropTypes__default.default.any,
unmask: PropTypes__default.default.oneOfType([PropTypes__default.default.bool, PropTypes__default.default.oneOf(['typed'])]),
prepare: PropTypes__default.default.func,
prepareChar: PropTypes__default.default.func,
validate: PropTypes__default.default.func,
commit: PropTypes__default.default.func,
overwrite: PropTypes__default.default.oneOfType([PropTypes__default.default.bool, PropTypes__default.default.oneOf(['shift'])]),
eager: PropTypes__default.default.oneOfType([PropTypes__default.default.bool, PropTypes__default.default.oneOf(['append', 'remove'])]),
skipInvalid: PropTypes__default.default.bool,
// events
onAccept: PropTypes__default.default.func,
onComplete: PropTypes__default.default.func,
// pattern
placeholderChar: PropTypes__default.default.string,
displayChar: PropTypes__default.default.string,
lazy: PropTypes__default.default.bool,
definitions: PropTypes__default.default.object,
blocks: PropTypes__default.default.object,
// enum
enum: PropTypes__default.default.arrayOf(PropTypes__default.default.string),
// range
maxLength: PropTypes__default.default.number,
from: PropTypes__default.default.number,
to: PropTypes__default.default.number,
// date
pattern: PropTypes__default.default.string,
format: PropTypes__default.default.func,
parse: PropTypes__default.default.func,
autofix: PropTypes__default.default.oneOfType([PropTypes__default.default.bool, PropTypes__default.default.oneOf(['pad'])]),
// number
radix: PropTypes__default.default.string,
thousandsSeparator: PropTypes__default.default.string,
mapToRadix: PropTypes__default.default.arrayOf(PropTypes__default.default.string),
scale: PropTypes__default.default.number,
normalizeZeros: PropTypes__default.default.bool,
padFractionalZeros: PropTypes__default.default.bool,
min: PropTypes__default.default.oneOfType([PropTypes__default.default.number, PropTypes__default.default.instanceOf(Date)]),
max: PropTypes__default.default.oneOfType([PropTypes__default.default.number, PropTypes__default.default.instanceOf(Date)]),
// dynamic
dispatch: PropTypes__default.default.func,
// ref
inputRef: PropTypes__default.default.oneOfType([PropTypes__default.default.func, PropTypes__default.default.shape({
current: PropTypes__default.default.object
})])
};
const MASK_PROPS_NAMES = Object.keys(MASK_PROPS).filter(p => p !== 'value');
const NON_MASK_OPTIONS_NAMES = ['value', 'unmask', 'onAccept', 'onComplete', 'inputRef'];
const MASK_OPTIONS_NAMES = MASK_PROPS_NAMES.filter(pName => NON_MASK_OPTIONS_NAMES.indexOf(pName) < 0);
function IMaskMixin(ComposedComponent) {
var _Class;
const MaskedComponent = (_Class = class MaskedComponent extends React__default.default.Component {
constructor(props) {
super(props);
this._inputRef = this._inputRef.bind(this);
}
componentDidMount() {
if (!this.props.mask) return;
this.initMask();
}
componentDidUpdate() {
const props = this.props;
const maskOptions = this._extractMaskOptionsFromProps(props);
if (maskOptions.mask) {
if (this.maskRef) {
this.maskRef.updateOptions(maskOptions); // TODO fix
if ('value' in props && props.value !== undefined) this.maskValue = props.value;
} else {
this.initMask(maskOptions);
}
} else {
this.destroyMask();
if ('value' in props && props.value !== undefined) {
var _this$element;
if ((_this$element = this.element) != null && _this$element.isContentEditable && this.element.tagName !== 'INPUT' && this.element.tagName !== 'TEXTAREA') this.element.textContent = props.value;else this.element.value = props.value;
}
}
}
componentWillUnmount() {
this.destroyMask();
}
_inputRef(el) {
this.element = el;
if (this.props.inputRef) {
if (Object.prototype.hasOwnProperty.call(this.props.inputRef, 'current')) this.props.inputRef.current = el;else this.props.inputRef(el);
}
}
initMask(maskOptions) {
if (maskOptions === void 0) {
maskOptions = this._extractMaskOptionsFromProps(this.props);
}
this.maskRef = IMask__default.default(this.element, maskOptions).on('accept', this._onAccept.bind(this)).on('complete', this._onComplete.bind(this));
if ('value' in this.props && this.props.value !== undefined) this.maskValue = this.props.value;
}
destroyMask() {
if (this.maskRef) {
this.maskRef.destroy();
delete this.maskRef;
}
}
_extractMaskOptionsFromProps(props) {
const {
...cloneProps
} = props;
// keep only mask options
Object.keys(cloneProps).filter(prop => MASK_OPTIONS_NAMES.indexOf(prop) < 0).forEach(nonMaskProp => {
delete cloneProps[nonMaskProp];
});
return cloneProps;
}
_extractNonMaskProps(props) {
const {
...cloneProps
} = props;
MASK_PROPS_NAMES.forEach(maskProp => {
if (maskProp !== 'maxLength') delete cloneProps[maskProp];
});
if (!('defaultValue' in cloneProps)) cloneProps.defaultValue = props.mask ? '' : cloneProps.value;
delete cloneProps.value;
return cloneProps;
}
get maskValue() {
if (!this.maskRef) return '';
if (this.props.unmask === 'typed') return this.maskRef.typedValue;
if (this.props.unmask) return this.maskRef.unmaskedValue;
return this.maskRef.value;
}
set maskValue(value) {
if (!this.maskRef) return;
value = value == null && this.props.unmask !== 'typed' ? '' : value;
if (this.props.unmask === 'typed') this.maskRef.typedValue = value;else if (this.props.unmask) this.maskRef.unmaskedValue = value;else this.maskRef.value = value;
}
_onAccept(e) {
if (this.props.onAccept && this.maskRef) this.props.onAccept(this.maskValue, this.maskRef, e);
}
_onComplete(e) {
if (this.props.onComplete && this.maskRef) this.props.onComplete(this.maskValue, this.maskRef, e);
}
render() {
return React__default.default.createElement(ComposedComponent, {
...this._extractNonMaskProps(this.props),
inputRef: this._inputRef
});
}
}, _Class.displayName = void 0, _Class.propTypes = void 0, _Class);
const nestedComponentName = ComposedComponent.displayName || ComposedComponent.name || 'Component';
MaskedComponent.displayName = "IMask(" + nestedComponentName + ")";
MaskedComponent.propTypes = MASK_PROPS;
return React__default.default.forwardRef((props, ref) => React__default.default.createElement(MaskedComponent, {
...props,
ref
}));
}
const IMaskInputClass = IMaskMixin(_ref => {
let {
inputRef,
...props
} = _ref;
return React__default.default.createElement('input', {
...props,
ref: inputRef
});
});
const IMaskInputFn = (props, ref) => React__default.default.createElement(IMaskInputClass, {
...props,
ref
}) // TODO fix no idea
;
const IMaskInput = React__default.default.forwardRef(IMaskInputFn);
function useIMask(opts, _temp) {
let {
onAccept,
onComplete,
ref = React.useRef(null),
defaultValue,
defaultUnmaskedValue,
defaultTypedValue
} = _temp === void 0 ? {} : _temp;
const maskRef = React.useRef(null);
const [lastAcceptState, setLastAcceptState] = React.useState({});
const [value, setValue] = React.useState('');
const [unmaskedValue, setUnmaskedValue] = React.useState('');
const [typedValue, setTypedValue] = React.useState();
const _destroyMask = React.useCallback(() => {
var _maskRef$current;
(_maskRef$current = maskRef.current) == null || _maskRef$current.destroy();
maskRef.current = null;
}, []);
const storeLastAcceptedValues = React.useCallback(() => {
const m = maskRef.current;
if (!m) return;
setLastAcceptState({
value: m.value,
unmaskedValue: m.unmaskedValue,
typedValue: m.typedValue
});
setTypedValue(m.typedValue);
setUnmaskedValue(m.unmaskedValue);
setValue(m.value);
}, []);
const _onAccept = React.useCallback(event => {
const m = maskRef.current;
if (!m) return;
storeLastAcceptedValues();
onAccept == null || onAccept(m.value, m, event);
}, [onAccept]);
const _onComplete = React.useCallback(event => maskRef.current && (onComplete == null ? void 0 : onComplete(maskRef.current.value, maskRef.current, event)), [onComplete]);
React.useEffect(() => {
const {
value: lastAcceptValue,
...state
} = lastAcceptState;
const mask = maskRef.current;
if (!mask || value === undefined) return;
if (lastAcceptValue !== value) {
mask.value = value;
if (mask.value !== value) _onAccept();
}
setLastAcceptState(state);
}, [value]);
React.useEffect(() => {
const {
unmaskedValue: lastAcceptUnmaskedValue,
...state
} = lastAcceptState;
const mask = maskRef.current;
if (!mask || unmaskedValue === undefined) return;
if (lastAcceptUnmaskedValue !== unmaskedValue) {
mask.unmaskedValue = unmaskedValue;
if (mask.unmaskedValue !== unmaskedValue) _onAccept();
}
setLastAcceptState(state);
}, [unmaskedValue]);
React.useEffect(() => {
const {
typedValue: lastAcceptTypedValue,
...state
} = lastAcceptState;
const mask = maskRef.current;
if (!mask || typedValue === undefined) return;
if (lastAcceptTypedValue !== typedValue) {
mask.typedValue = typedValue;
if (!mask.masked.typedValueEquals(typedValue)) _onAccept();
}
setLastAcceptState(state);
}, [typedValue]);
React.useEffect(() => {
const el = ref.current;
if (!el || !(opts != null && opts.mask)) return _destroyMask();
const mask = maskRef.current;
if (!mask) {
if (el && opts != null && opts.mask) {
maskRef.current = IMask__default.default(el, opts);
storeLastAcceptedValues();
if (defaultValue !== undefined) setValue(defaultValue);
if (defaultUnmaskedValue !== undefined) setUnmaskedValue(defaultUnmaskedValue);
if (defaultTypedValue !== undefined) setTypedValue(defaultTypedValue);
}
} else {
mask == null || mask.updateOptions(opts); // TODO fix no idea
}
}, [opts, _destroyMask, _onAccept]);
React.useEffect(() => {
if (!maskRef.current) return;
const mask = maskRef.current;
mask.on('accept', _onAccept);
mask.on('complete', _onComplete);
return () => {
mask.off('accept', _onAccept);
mask.off('complete', _onComplete);
};
}, [_onAccept, _onComplete]);
React.useEffect(() => _destroyMask, [_destroyMask]);
return {
ref,
maskRef,
value,
setValue,
unmaskedValue,
setUnmaskedValue,
typedValue,
setTypedValue
};
}
Object.defineProperty(exports, "IMask", {
enumerable: true,
get: function () { return IMask__default.default; }
});
exports.IMaskInput = IMaskInput;
exports.IMaskMixin = IMaskMixin;
exports.useIMask = useIMask;
//# sourceMappingURL=react-imask.cjs.map