Socket
Socket
Sign inDemoInstall

rifm

Package Overview
Dependencies
5
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.7.0 to 0.8.0

dist/rifm.esm.production.js

234

dist/rifm.cjs.js

@@ -5,152 +5,142 @@ 'use strict';

function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var _inheritsLoose = _interopDefault(require('@babel/runtime/helpers/inheritsLoose'));
var React = require('react');
var Rifm =
/*#__PURE__*/
function (_React$Component) {
_inheritsLoose(Rifm, _React$Component);
var Rifm = function Rifm(props) {
var _React$useReducer = React.useReducer(function (c) {
return c + 1;
}, 0),
refresh = _React$useReducer[1];
function Rifm(props) {
var _this;
var valueRef = React.useRef(null); // state of delete button see comments below about inputType support
_this = _React$Component.call(this, props) || this;
_this._state = null;
_this._del = false;
var isDeleleteButtonDownRef = React.useRef(false);
_this._handleChange = function (evt) {
if (process.env.NODE_ENV !== 'production') {
if (evt.target.type === 'number') {
console.error('Rifm does not support input type=number, use type=tel instead.');
return;
}
} // FUTURE: use evt.nativeEvent.inputType for del event, see comments at onkeydown
var onChange = function onChange(evt) {
if (process.env.NODE_ENV !== 'production') {
if (evt.target.type === 'number') {
console.error('Rifm does not support input type=number, use type=tel instead.');
return;
}
}
var value = props.value;
var eventValue = evt.target.value;
valueRef.current = [eventValue, // eventValue
evt.target, // input
eventValue.length > value.length, // isSizeIncreaseOperation
isDeleleteButtonDownRef.current, // isDeleleteButtonDown
value === props.format(eventValue)]; // The main trick is to update underlying input with non formatted value (= eventValue)
// that allows us to calculate right cursor position after formatting (see getCursorPosition)
// then we format new value and call props.onChange with masked/formatted value
// and finally we are able to set cursor position into right place
var stateValue = _this.state.value;
var value = evt.target.value;
var input = evt.target;
var op = value.length > stateValue.length;
var del = _this._del;
refresh();
}; // React prints warn on server in non production mode about useLayoutEffect usage
// in both cases it's noop
var noOp = stateValue === _this.props.format(value);
_this.setState({
value: value,
local: true
}, function () {
var selectionStart = input.selectionStart;
var refuse = _this.props.refuse || /[^\d]+/g;
var before = value.substr(0, selectionStart).replace(refuse, '');
_this._state = {
input: input,
before: before,
op: op,
di: del && noOp,
del: del
};
if (process.env.NODE_ENV === 'production' || typeof window !== 'undefined') {
React.useLayoutEffect(function () {
if (valueRef.current == null) return;
var _valueRef$current = valueRef.current,
eventValue = _valueRef$current[0],
input = _valueRef$current[1],
isSizeIncreaseOperation = _valueRef$current[2],
isDeleleteButtonDown = _valueRef$current[3],
// No operation means that value itself hasn't been changed, BTW cursor, selection etc can be changed
isNoOperation = _valueRef$current[4];
valueRef.current = null; // this usually occurs on deleting special symbols like ' here 123'123.00
// in case of isDeleleteButtonDown cursor should move differently vs backspace
if (_this.props.replace && _this.props.replace(stateValue) && op && !noOp) {
var start = -1;
var deleteWasNoOp = isDeleleteButtonDown && isNoOperation; // Create string from only accepted symbols
for (var i = 0; i !== before.length; ++i) {
start = Math.max(start, value.toLowerCase().indexOf(before[i].toLowerCase(), start + 1));
}
var clean = function clean(str) {
return (str.match(props.accept || /\d/g) || []).join('');
};
var c = value.substr(start + 1).replace(refuse, '')[0];
start = value.indexOf(c, start + 1);
value = "" + value.substr(0, start) + value.substr(start + 1);
}
var valueBeforeSelectionStart = clean(eventValue.substr(0, input.selectionStart)).toLowerCase(); // trying to find cursor position in formatted value having knowledge about valueBeforeSelectionStart
// This works because we assume that format doesn't change the order of accepted symbols.
// Imagine we have formatter which adds ' symbol between numbers, and by default we refuse all non numeric symbols
// for example we had input = 1'2|'4 (| means cursor position) then user entered '3' symbol
// inputValue = 1'23'|4 so valueBeforeSelectionStart = 123 and formatted value = 1'2'3'4
// calling getCursorPosition("1'2'3'4") will give us position after 3, 1'2'3|'4
// so for formatting just this function to determine cursor position after formatting is enough
// with masking we need to do some additional checks see `replace` below
var fv = _this.props.format(value);
var getCursorPosition = function getCursorPosition(val) {
var start = 0;
if (stateValue === fv) {
_this.setState({
value: value
});
} else {
_this.props.onChange(fv);
for (var i = 0; i !== valueBeforeSelectionStart.length; ++i) {
start = Math.max(start, val.toLowerCase().indexOf(valueBeforeSelectionStart[i], start) + 1);
}
});
};
_this._hKD = function (evt) {
if (evt.code === 'Delete') {
_this._del = true;
return start;
}; // Masking part, for masks if size of mask is above some value (props.replace checks that)
// we need to replace symbols instead of do nothing as like in format
if (props.replace && props.replace(props.value) && isSizeIncreaseOperation && !isNoOperation) {
var start = getCursorPosition(eventValue);
var c = clean(eventValue.substr(start))[0];
start = eventValue.indexOf(c, start);
eventValue = "" + eventValue.substr(0, start) + eventValue.substr(start + 1);
}
};
_this._hKU = function (evt) {
if (evt.code === 'Delete') {
_this._del = false;
var formattedValue = props.format(eventValue);
if (props.value === formattedValue) {
// if nothing changed for formatted value, just refresh so props.value will be used at render
refresh();
} else {
props.onChange(formattedValue);
}
};
_this.state = {
value: props.value,
local: true
};
return _this;
}
return function () {
var start = getCursorPosition(formattedValue); // Visually improves working with masked values,
// like cursor jumping over refused symbols
// as an example date mask: was "5|1-24-3" then user pressed "6"
// it becomes "56-|12-43" with this code, and "56|-12-43" without
Rifm.getDerivedStateFromProps = function getDerivedStateFromProps(props, state) {
return {
value: state.local ? state.value : props.value,
local: false
};
};
if (props.replace && (isSizeIncreaseOperation || isDeleleteButtonDown && !deleteWasNoOp)) {
while (formattedValue[start] && clean(formattedValue[start]) === '') {
start += 1;
}
}
var _proto = Rifm.prototype;
_proto.render = function render() {
var _handleChange = this._handleChange,
value = this.state.value,
children = this.props.children;
return children({
value: value,
onChange: _handleChange
input.selectionStart = input.selectionEnd = start + (deleteWasNoOp ? 1 : 0);
};
});
} // delete when https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/inputType will be supported by all major browsers
;
}
_proto.componentWillUnmount = function componentWillUnmount() {
document.removeEventListener('keydown', this._hKD);
document.removeEventListener('keyup', this._hKU);
} // delete when https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/inputType will be supported by all major browsers
;
React.useEffect(function () {
// until https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/inputType will be supported
// by all major browsers (now supported by: +chrome, +safari, ?edge, !firefox)
// there is no way I found to distinguish in onChange
// backspace or delete was called in some situations
// firefox track https://bugzilla.mozilla.org/show_bug.cgi?id=1447239
var handleKeyDown = function handleKeyDown(evt) {
if (evt.code === 'Delete') {
isDeleleteButtonDownRef.current = true;
}
};
_proto.componentDidMount = function componentDidMount() {
document.addEventListener('keydown', this._hKD);
document.addEventListener('keyup', this._hKU);
};
_proto.componentDidUpdate = function componentDidUpdate() {
var _state = this._state;
if (_state) {
var value = this.state.value;
var start = -1;
for (var i = 0; i !== _state.before.length; ++i) {
start = Math.max(start, value.toLowerCase().indexOf(_state.before[i].toLowerCase(), start + 1));
} // format usually looks better without this
if (this.props.replace && (_state.op || _state.del && !_state.di)) {
while (value[start + 1] && (this.props.refuse || /[^\d]+/).test(value[start + 1])) {
start += 1;
}
var handleKeyUp = function handleKeyUp(evt) {
if (evt.code === 'Delete') {
isDeleleteButtonDownRef.current = false;
}
};
_state.input.selectionStart = _state.input.selectionEnd = start + 1 + (_state.di ? 1 : 0);
}
document.addEventListener('keydown', handleKeyDown);
document.addEventListener('keyup', handleKeyUp);
return function () {
document.removeEventListener('keydown', handleKeyDown);
document.removeEventListener('keyup', handleKeyUp);
};
}, []);
return props.children({
value: valueRef.current != null ? valueRef.current[0] : props.value,
onChange: onChange
});
};
this._state = null;
};
return Rifm;
}(React.Component);
exports.Rifm = Rifm;

@@ -1,149 +0,141 @@

import _inheritsLoose from '@babel/runtime/helpers/esm/inheritsLoose';
import { Component } from 'react';
import { useReducer, useRef, useLayoutEffect, useEffect } from 'react';
var Rifm =
/*#__PURE__*/
function (_React$Component) {
_inheritsLoose(Rifm, _React$Component);
var Rifm = function Rifm(props) {
var _React$useReducer = useReducer(function (c) {
return c + 1;
}, 0),
refresh = _React$useReducer[1];
function Rifm(props) {
var _this;
var valueRef = useRef(null); // state of delete button see comments below about inputType support
_this = _React$Component.call(this, props) || this;
_this._state = null;
_this._del = false;
var isDeleleteButtonDownRef = useRef(false);
_this._handleChange = function (evt) {
if (process.env.NODE_ENV !== 'production') {
if (evt.target.type === 'number') {
console.error('Rifm does not support input type=number, use type=tel instead.');
return;
}
} // FUTURE: use evt.nativeEvent.inputType for del event, see comments at onkeydown
var onChange = function onChange(evt) {
if (process.env.NODE_ENV !== 'production') {
if (evt.target.type === 'number') {
console.error('Rifm does not support input type=number, use type=tel instead.');
return;
}
}
var value = props.value;
var eventValue = evt.target.value;
valueRef.current = [eventValue, // eventValue
evt.target, // input
eventValue.length > value.length, // isSizeIncreaseOperation
isDeleleteButtonDownRef.current, // isDeleleteButtonDown
value === props.format(eventValue)]; // The main trick is to update underlying input with non formatted value (= eventValue)
// that allows us to calculate right cursor position after formatting (see getCursorPosition)
// then we format new value and call props.onChange with masked/formatted value
// and finally we are able to set cursor position into right place
var stateValue = _this.state.value;
var value = evt.target.value;
var input = evt.target;
var op = value.length > stateValue.length;
var del = _this._del;
refresh();
}; // React prints warn on server in non production mode about useLayoutEffect usage
// in both cases it's noop
var noOp = stateValue === _this.props.format(value);
_this.setState({
value: value,
local: true
}, function () {
var selectionStart = input.selectionStart;
var refuse = _this.props.refuse || /[^\d]+/g;
var before = value.substr(0, selectionStart).replace(refuse, '');
_this._state = {
input: input,
before: before,
op: op,
di: del && noOp,
del: del
};
if (process.env.NODE_ENV === 'production' || typeof window !== 'undefined') {
useLayoutEffect(function () {
if (valueRef.current == null) return;
var _valueRef$current = valueRef.current,
eventValue = _valueRef$current[0],
input = _valueRef$current[1],
isSizeIncreaseOperation = _valueRef$current[2],
isDeleleteButtonDown = _valueRef$current[3],
// No operation means that value itself hasn't been changed, BTW cursor, selection etc can be changed
isNoOperation = _valueRef$current[4];
valueRef.current = null; // this usually occurs on deleting special symbols like ' here 123'123.00
// in case of isDeleleteButtonDown cursor should move differently vs backspace
if (_this.props.replace && _this.props.replace(stateValue) && op && !noOp) {
var start = -1;
var deleteWasNoOp = isDeleleteButtonDown && isNoOperation; // Create string from only accepted symbols
for (var i = 0; i !== before.length; ++i) {
start = Math.max(start, value.toLowerCase().indexOf(before[i].toLowerCase(), start + 1));
}
var clean = function clean(str) {
return (str.match(props.accept || /\d/g) || []).join('');
};
var c = value.substr(start + 1).replace(refuse, '')[0];
start = value.indexOf(c, start + 1);
value = "" + value.substr(0, start) + value.substr(start + 1);
}
var valueBeforeSelectionStart = clean(eventValue.substr(0, input.selectionStart)).toLowerCase(); // trying to find cursor position in formatted value having knowledge about valueBeforeSelectionStart
// This works because we assume that format doesn't change the order of accepted symbols.
// Imagine we have formatter which adds ' symbol between numbers, and by default we refuse all non numeric symbols
// for example we had input = 1'2|'4 (| means cursor position) then user entered '3' symbol
// inputValue = 1'23'|4 so valueBeforeSelectionStart = 123 and formatted value = 1'2'3'4
// calling getCursorPosition("1'2'3'4") will give us position after 3, 1'2'3|'4
// so for formatting just this function to determine cursor position after formatting is enough
// with masking we need to do some additional checks see `replace` below
var fv = _this.props.format(value);
var getCursorPosition = function getCursorPosition(val) {
var start = 0;
if (stateValue === fv) {
_this.setState({
value: value
});
} else {
_this.props.onChange(fv);
for (var i = 0; i !== valueBeforeSelectionStart.length; ++i) {
start = Math.max(start, val.toLowerCase().indexOf(valueBeforeSelectionStart[i], start) + 1);
}
});
};
_this._hKD = function (evt) {
if (evt.code === 'Delete') {
_this._del = true;
return start;
}; // Masking part, for masks if size of mask is above some value (props.replace checks that)
// we need to replace symbols instead of do nothing as like in format
if (props.replace && props.replace(props.value) && isSizeIncreaseOperation && !isNoOperation) {
var start = getCursorPosition(eventValue);
var c = clean(eventValue.substr(start))[0];
start = eventValue.indexOf(c, start);
eventValue = "" + eventValue.substr(0, start) + eventValue.substr(start + 1);
}
};
_this._hKU = function (evt) {
if (evt.code === 'Delete') {
_this._del = false;
var formattedValue = props.format(eventValue);
if (props.value === formattedValue) {
// if nothing changed for formatted value, just refresh so props.value will be used at render
refresh();
} else {
props.onChange(formattedValue);
}
};
_this.state = {
value: props.value,
local: true
};
return _this;
}
return function () {
var start = getCursorPosition(formattedValue); // Visually improves working with masked values,
// like cursor jumping over refused symbols
// as an example date mask: was "5|1-24-3" then user pressed "6"
// it becomes "56-|12-43" with this code, and "56|-12-43" without
Rifm.getDerivedStateFromProps = function getDerivedStateFromProps(props, state) {
return {
value: state.local ? state.value : props.value,
local: false
};
};
if (props.replace && (isSizeIncreaseOperation || isDeleleteButtonDown && !deleteWasNoOp)) {
while (formattedValue[start] && clean(formattedValue[start]) === '') {
start += 1;
}
}
var _proto = Rifm.prototype;
_proto.render = function render() {
var _handleChange = this._handleChange,
value = this.state.value,
children = this.props.children;
return children({
value: value,
onChange: _handleChange
input.selectionStart = input.selectionEnd = start + (deleteWasNoOp ? 1 : 0);
};
});
} // delete when https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/inputType will be supported by all major browsers
;
}
_proto.componentWillUnmount = function componentWillUnmount() {
document.removeEventListener('keydown', this._hKD);
document.removeEventListener('keyup', this._hKU);
} // delete when https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/inputType will be supported by all major browsers
;
useEffect(function () {
// until https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/inputType will be supported
// by all major browsers (now supported by: +chrome, +safari, ?edge, !firefox)
// there is no way I found to distinguish in onChange
// backspace or delete was called in some situations
// firefox track https://bugzilla.mozilla.org/show_bug.cgi?id=1447239
var handleKeyDown = function handleKeyDown(evt) {
if (evt.code === 'Delete') {
isDeleleteButtonDownRef.current = true;
}
};
_proto.componentDidMount = function componentDidMount() {
document.addEventListener('keydown', this._hKD);
document.addEventListener('keyup', this._hKU);
};
_proto.componentDidUpdate = function componentDidUpdate() {
var _state = this._state;
if (_state) {
var value = this.state.value;
var start = -1;
for (var i = 0; i !== _state.before.length; ++i) {
start = Math.max(start, value.toLowerCase().indexOf(_state.before[i].toLowerCase(), start + 1));
} // format usually looks better without this
if (this.props.replace && (_state.op || _state.del && !_state.di)) {
while (value[start + 1] && (this.props.refuse || /[^\d]+/).test(value[start + 1])) {
start += 1;
}
var handleKeyUp = function handleKeyUp(evt) {
if (evt.code === 'Delete') {
isDeleleteButtonDownRef.current = false;
}
};
_state.input.selectionStart = _state.input.selectionEnd = start + 1 + (_state.di ? 1 : 0);
}
document.addEventListener('keydown', handleKeyDown);
document.addEventListener('keyup', handleKeyUp);
return function () {
document.removeEventListener('keydown', handleKeyDown);
document.removeEventListener('keyup', handleKeyUp);
};
}, []);
return props.children({
value: valueRef.current != null ? valueRef.current[0] : props.value,
onChange: onChange
});
};
this._state = null;
};
return Rifm;
}(Component);
export { Rifm };

@@ -1,1 +0,1 @@

!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e=e||self).Rifm={},e.React)}(this,function(e,t){"use strict";var o=function(e){var t,o;function n(t){var o;return(o=e.call(this,t)||this)._state=null,o._del=!1,o._handleChange=function(e){var t=o.state.value,n=e.target.value,r=e.target,a=n.length>t.length,s=o._del,i=t===o.props.format(n);o.setState({value:n,local:!0},function(){var e=r.selectionStart,u=o.props.refuse||/[^\d]+/g,l=n.substr(0,e).replace(u,"");if(o._state={input:r,before:l,op:a,di:s&&i,del:s},o.props.replace&&o.props.replace(t)&&a&&!i){for(var p=-1,c=0;c!==l.length;++c)p=Math.max(p,n.toLowerCase().indexOf(l[c].toLowerCase(),p+1));var d=n.substr(p+1).replace(u,"")[0];p=n.indexOf(d,p+1),n=""+n.substr(0,p)+n.substr(p+1)}var f=o.props.format(n);t===f?o.setState({value:n}):o.props.onChange(f)})},o._hKD=function(e){"Delete"===e.code&&(o._del=!0)},o._hKU=function(e){"Delete"===e.code&&(o._del=!1)},o.state={value:t.value,local:!0},o}o=e,(t=n).prototype=Object.create(o.prototype),t.prototype.constructor=t,t.__proto__=o,n.getDerivedStateFromProps=function(e,t){return{value:t.local?t.value:e.value,local:!1}};var r=n.prototype;return r.render=function(){var e=this._handleChange,t=this.state.value;return(0,this.props.children)({value:t,onChange:e})},r.componentWillUnmount=function(){document.removeEventListener("keydown",this._hKD),document.removeEventListener("keyup",this._hKU)},r.componentDidMount=function(){document.addEventListener("keydown",this._hKD),document.addEventListener("keyup",this._hKU)},r.componentDidUpdate=function(){var e=this._state;if(e){for(var t=this.state.value,o=-1,n=0;n!==e.before.length;++n)o=Math.max(o,t.toLowerCase().indexOf(e.before[n].toLowerCase(),o+1));if(this.props.replace&&(e.op||e.del&&!e.di))for(;t[o+1]&&(this.props.refuse||/[^\d]+/).test(t[o+1]);)o+=1;e.input.selectionStart=e.input.selectionEnd=o+1+(e.di?1:0)}this._state=null},n}(t.Component);e.Rifm=o,Object.defineProperty(e,"__esModule",{value:!0})});
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e=e||self).Rifm={},e.React)}(this,function(e,t){"use strict";e.Rifm=function(e){var n=t.useReducer(function(e){return e+1},0)[1],r=t.useRef(null),u=t.useRef(!1);return t.useLayoutEffect(function(){if(null!=r.current){var t=r.current,u=t[0],o=t[1],c=t[2],a=t[3],f=t[4];r.current=null;var i=a&&f,l=function(t){return(t.match(e.accept||/\d/g)||[]).join("")},s=l(u.substr(0,o.selectionStart)).toLowerCase(),d=function(e){for(var t=0,n=0;n!==s.length;++n)t=Math.max(t,e.toLowerCase().indexOf(s[n],t)+1);return t};if(e.replace&&e.replace(e.value)&&c&&!f){var v=d(u),m=l(u.substr(v))[0];v=u.indexOf(m,v),u=""+u.substr(0,v)+u.substr(v+1)}var p=e.format(u);return e.value===p?n():e.onChange(p),function(){var t=d(p);if(e.replace&&(c||a&&!i))for(;p[t]&&""===l(p[t]);)t+=1;o.selectionStart=o.selectionEnd=t+(i?1:0)}}}),t.useEffect(function(){var e=function(e){"Delete"===e.code&&(u.current=!0)},t=function(e){"Delete"===e.code&&(u.current=!1)};return document.addEventListener("keydown",e),document.addEventListener("keyup",t),function(){document.removeEventListener("keydown",e),document.removeEventListener("keyup",t)}},[]),e.children({value:null!=r.current?r.current[0]:e.value,onChange:function(t){var o=e.value,c=t.target.value;r.current=[c,t.target,c.length>o.length,u.current,o===e.format(c)],n()}})},Object.defineProperty(e,"__esModule",{value:!0})});

@@ -7,153 +7,140 @@ (function (global, factory) {

function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
subClass.__proto__ = superClass;
}
var Rifm = function Rifm(props) {
var _React$useReducer = React.useReducer(function (c) {
return c + 1;
}, 0),
refresh = _React$useReducer[1];
var Rifm =
/*#__PURE__*/
function (_React$Component) {
_inheritsLoose(Rifm, _React$Component);
var valueRef = React.useRef(null); // state of delete button see comments below about inputType support
function Rifm(props) {
var _this;
var isDeleleteButtonDownRef = React.useRef(false);
_this = _React$Component.call(this, props) || this;
_this._state = null;
_this._del = false;
var onChange = function onChange(evt) {
{
if (evt.target.type === 'number') {
console.error('Rifm does not support input type=number, use type=tel instead.');
return;
}
}
_this._handleChange = function (evt) {
{
if (evt.target.type === 'number') {
console.error('Rifm does not support input type=number, use type=tel instead.');
return;
}
} // FUTURE: use evt.nativeEvent.inputType for del event, see comments at onkeydown
var value = props.value;
var eventValue = evt.target.value;
valueRef.current = [eventValue, // eventValue
evt.target, // input
eventValue.length > value.length, // isSizeIncreaseOperation
isDeleleteButtonDownRef.current, // isDeleleteButtonDown
value === props.format(eventValue)]; // The main trick is to update underlying input with non formatted value (= eventValue)
// that allows us to calculate right cursor position after formatting (see getCursorPosition)
// then we format new value and call props.onChange with masked/formatted value
// and finally we are able to set cursor position into right place
refresh();
}; // React prints warn on server in non production mode about useLayoutEffect usage
// in both cases it's noop
var stateValue = _this.state.value;
var value = evt.target.value;
var input = evt.target;
var op = value.length > stateValue.length;
var del = _this._del;
var noOp = stateValue === _this.props.format(value);
if (typeof window !== 'undefined') {
React.useLayoutEffect(function () {
if (valueRef.current == null) return;
var _valueRef$current = valueRef.current,
eventValue = _valueRef$current[0],
input = _valueRef$current[1],
isSizeIncreaseOperation = _valueRef$current[2],
isDeleleteButtonDown = _valueRef$current[3],
// No operation means that value itself hasn't been changed, BTW cursor, selection etc can be changed
isNoOperation = _valueRef$current[4];
valueRef.current = null; // this usually occurs on deleting special symbols like ' here 123'123.00
// in case of isDeleleteButtonDown cursor should move differently vs backspace
_this.setState({
value: value,
local: true
}, function () {
var selectionStart = input.selectionStart;
var refuse = _this.props.refuse || /[^\d]+/g;
var before = value.substr(0, selectionStart).replace(refuse, '');
_this._state = {
input: input,
before: before,
op: op,
di: del && noOp,
del: del
};
var deleteWasNoOp = isDeleleteButtonDown && isNoOperation; // Create string from only accepted symbols
if (_this.props.replace && _this.props.replace(stateValue) && op && !noOp) {
var start = -1;
var clean = function clean(str) {
return (str.match(props.accept || /\d/g) || []).join('');
};
for (var i = 0; i !== before.length; ++i) {
start = Math.max(start, value.toLowerCase().indexOf(before[i].toLowerCase(), start + 1));
}
var valueBeforeSelectionStart = clean(eventValue.substr(0, input.selectionStart)).toLowerCase(); // trying to find cursor position in formatted value having knowledge about valueBeforeSelectionStart
// This works because we assume that format doesn't change the order of accepted symbols.
// Imagine we have formatter which adds ' symbol between numbers, and by default we refuse all non numeric symbols
// for example we had input = 1'2|'4 (| means cursor position) then user entered '3' symbol
// inputValue = 1'23'|4 so valueBeforeSelectionStart = 123 and formatted value = 1'2'3'4
// calling getCursorPosition("1'2'3'4") will give us position after 3, 1'2'3|'4
// so for formatting just this function to determine cursor position after formatting is enough
// with masking we need to do some additional checks see `replace` below
var c = value.substr(start + 1).replace(refuse, '')[0];
start = value.indexOf(c, start + 1);
value = "" + value.substr(0, start) + value.substr(start + 1);
var getCursorPosition = function getCursorPosition(val) {
var start = 0;
for (var i = 0; i !== valueBeforeSelectionStart.length; ++i) {
start = Math.max(start, val.toLowerCase().indexOf(valueBeforeSelectionStart[i], start) + 1);
}
var fv = _this.props.format(value);
return start;
}; // Masking part, for masks if size of mask is above some value (props.replace checks that)
// we need to replace symbols instead of do nothing as like in format
if (stateValue === fv) {
_this.setState({
value: value
});
} else {
_this.props.onChange(fv);
}
});
};
_this._hKD = function (evt) {
if (evt.code === 'Delete') {
_this._del = true;
if (props.replace && props.replace(props.value) && isSizeIncreaseOperation && !isNoOperation) {
var start = getCursorPosition(eventValue);
var c = clean(eventValue.substr(start))[0];
start = eventValue.indexOf(c, start);
eventValue = "" + eventValue.substr(0, start) + eventValue.substr(start + 1);
}
};
_this._hKU = function (evt) {
if (evt.code === 'Delete') {
_this._del = false;
var formattedValue = props.format(eventValue);
if (props.value === formattedValue) {
// if nothing changed for formatted value, just refresh so props.value will be used at render
refresh();
} else {
props.onChange(formattedValue);
}
};
_this.state = {
value: props.value,
local: true
};
return _this;
}
return function () {
var start = getCursorPosition(formattedValue); // Visually improves working with masked values,
// like cursor jumping over refused symbols
// as an example date mask: was "5|1-24-3" then user pressed "6"
// it becomes "56-|12-43" with this code, and "56|-12-43" without
Rifm.getDerivedStateFromProps = function getDerivedStateFromProps(props, state) {
return {
value: state.local ? state.value : props.value,
local: false
};
};
if (props.replace && (isSizeIncreaseOperation || isDeleleteButtonDown && !deleteWasNoOp)) {
while (formattedValue[start] && clean(formattedValue[start]) === '') {
start += 1;
}
}
var _proto = Rifm.prototype;
_proto.render = function render() {
var _handleChange = this._handleChange,
value = this.state.value,
children = this.props.children;
return children({
value: value,
onChange: _handleChange
input.selectionStart = input.selectionEnd = start + (deleteWasNoOp ? 1 : 0);
};
});
} // delete when https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/inputType will be supported by all major browsers
;
}
_proto.componentWillUnmount = function componentWillUnmount() {
document.removeEventListener('keydown', this._hKD);
document.removeEventListener('keyup', this._hKU);
} // delete when https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/inputType will be supported by all major browsers
;
React.useEffect(function () {
// until https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/inputType will be supported
// by all major browsers (now supported by: +chrome, +safari, ?edge, !firefox)
// there is no way I found to distinguish in onChange
// backspace or delete was called in some situations
// firefox track https://bugzilla.mozilla.org/show_bug.cgi?id=1447239
var handleKeyDown = function handleKeyDown(evt) {
if (evt.code === 'Delete') {
isDeleleteButtonDownRef.current = true;
}
};
_proto.componentDidMount = function componentDidMount() {
document.addEventListener('keydown', this._hKD);
document.addEventListener('keyup', this._hKU);
};
_proto.componentDidUpdate = function componentDidUpdate() {
var _state = this._state;
if (_state) {
var value = this.state.value;
var start = -1;
for (var i = 0; i !== _state.before.length; ++i) {
start = Math.max(start, value.toLowerCase().indexOf(_state.before[i].toLowerCase(), start + 1));
} // format usually looks better without this
if (this.props.replace && (_state.op || _state.del && !_state.di)) {
while (value[start + 1] && (this.props.refuse || /[^\d]+/).test(value[start + 1])) {
start += 1;
}
var handleKeyUp = function handleKeyUp(evt) {
if (evt.code === 'Delete') {
isDeleleteButtonDownRef.current = false;
}
};
_state.input.selectionStart = _state.input.selectionEnd = start + 1 + (_state.di ? 1 : 0);
}
document.addEventListener('keydown', handleKeyDown);
document.addEventListener('keyup', handleKeyUp);
return function () {
document.removeEventListener('keydown', handleKeyDown);
document.removeEventListener('keyup', handleKeyUp);
};
}, []);
return props.children({
value: valueRef.current != null ? valueRef.current[0] : props.value,
onChange: onChange
});
};
this._state = null;
};
return Rifm;
}(React.Component);
exports.Rifm = Rifm;

@@ -160,0 +147,0 @@

{
"name": "rifm",
"version": "0.7.0",
"version": "0.8.0",
"description": "Tiny react input formatter and mask",

@@ -17,11 +17,24 @@ "author": "istarkov",

"build": "rimraf dist && npm run build:code && npm run build:flow",
"dev": "NODE_ICU_DATA=`yarn -s run node-full-icu-path` next dev",
"jest": "NODE_ICU_DATA=`yarn -s run node-full-icu-path` jest",
"test": "eslint ./ && flow check && yarn test:ts && yarn jest",
"test:ts": "tslint -p tsconfig.json",
"docz:build": "docz build",
"docz:dev": "docz dev",
"deploy:docz": "yarn docz:build && cp .docz/dist/index.html .docz/dist/404.html && gh-pages -d .docz/dist",
"copy-sandbox-examples": "mkdir -p ./out/codesandboxes && find ./pages -type d -mindepth 1 -maxdepth 1 -exec cp -R \\{\\} ./out/codesandboxes \\;",
"export": "rimraf ./out && next build && NODE_ICU_DATA=`yarn -s run node-full-icu-path` next export && yarn copy-sandbox-examples",
"deploy": "yarn export && gh-pages -d out",
"precommit": "lint-staged",
"prepublishOnly": "yarn test && yarn build"
},
"babel": {
"presets": [
[
"@babel/env",
{
"loose": true
}
],
"@babel/flow",
"@babel/react"
]
},
"lint-staged": {

@@ -35,3 +48,11 @@ "*.{js,md,ts,tsx}": [

"singleQuote": true,
"trailingComma": "es5"
"trailingComma": "es5",
"overrides": [
{
"files": "*.test.js",
"options": {
"printWidth": 120
}
}
]
},

@@ -68,3 +89,4 @@ "eslintConfig": {

}
]
],
"react/prop-types": "off"
}

@@ -74,3 +96,2 @@ },

"@babel/core": "^7.3.3",
"@babel/plugin-proposal-class-properties": "^7.3.3",
"@babel/plugin-transform-runtime": "^7.0.0",

@@ -80,3 +101,4 @@ "@babel/preset-env": "^7.3.1",

"@babel/preset-react": "^7.0.0",
"@material-ui/core": "^1.3.1",
"@material-ui/core": "^4.0.0",
"@material-ui/styles": "^4.0.0",
"alea": "^0.0.9",

@@ -86,4 +108,2 @@ "babel-core": "^7.0.0-bridge.0",

"babel-jest": "^23.2.0",
"docz": "^0.13.7",
"docz-theme-default": "^0.13.7",
"dtslint": "^0.4.2",

@@ -97,3 +117,3 @@ "emotion": "^9.2.4",

"flow-bin": "^0.93.0",
"full-icu": "^1.2.1",
"full-icu": "^1.3.0",
"gh-pages": "^1.2.0",

@@ -104,3 +124,4 @@ "husky": "^0.14.3",

"lint-staged": "^7.2.0",
"prettier": "^1.10.2",
"next": "^8.1.0",
"prettier": "^1.17.1",
"react": "^16.8.2",

@@ -117,3 +138,2 @@ "react-dom": "^16.8.2",

"rollup-plugin-terser": "^4.0.4",
"string-mask": "^0.3.0",
"tslint": "^5.12.0"

@@ -120,0 +140,0 @@ },

@@ -6,3 +6,3 @@ # RIFM - React Input Format & Mask

[Demo](https://istarkov.github.io/rifm/docs-readme)
[Demo](https://istarkov.github.io/rifm)

@@ -14,3 +14,3 @@ ## Highlights

- Tiny (≈ 800b)
- Supports any [input](https://istarkov.github.io/rifm/docs-readme#material-ui).
- Supports any [input](https://istarkov.github.io/rifm#material-ui).
- Can mask input and format

@@ -20,5 +20,4 @@

```javascript
```js
import { Rifm } from 'rifm';
import { Value } from 'react-powerplug';
import TextField from '@material-ui/core/TextField';

@@ -34,21 +33,19 @@ import { css } from 'emotion';

<Value initial={''}>
{text => (
<Rifm
value={text.value}
onChange={text.set}
format={numberFormat}
>
{({ value, onChange }) => (
<TextField
value={value}
label={'Float'}
onChange={onChange}
className={css({input: {textAlign:"right"}})}
type="tel"
/>
)}
</Rifm>
const [value, setValue] = React.useState('')
<Rifm
value={value}
onChange={setValue}
format={numberFormat}
>
{({ value, onChange }) => (
<TextField
value={value}
label={'Float'}
onChange={onChange}
className={css({input: {textAlign:"right"}})}
type="tel"
/>
)}
</Value>
</Rifm>

@@ -58,18 +55,10 @@ ...

[Demo](https://istarkov.github.io/rifm/docs-readme)
[Demo source](https://github.com/istarkov/rifm/blob/master/docs/readme.mdx)
## Install
```shell
yarn add rifm
```sh
yarn add rifm
```
```shell
npm -i rifm
```
## Thanks
[@TrySound](https://github.com/TrySound) for incredible help and support on this

@@ -13,3 +13,3 @@ import * as React from 'react';

replace?: (str: string) => boolean;
refuse?: RegExp;
accept?: RegExp;
children: (args: RifmRenderArgs<E>) => React.ReactNode;

@@ -16,0 +16,0 @@ }

@@ -10,3 +10,3 @@ /* @flow */

replace?: string => boolean,
refuse?: RegExp,
accept?: RegExp,
children: ({

@@ -20,27 +20,10 @@ value: string,

type State = {|
value: string,
local: boolean,
|};
export const Rifm = (props: Props) => {
const [, refresh] = React.useReducer(c => c + 1, 0);
const valueRef = React.useRef(null);
export class Rifm extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
value: props.value,
local: true,
};
}
// state of delete button see comments below about inputType support
const isDeleleteButtonDownRef = React.useRef(false);
_state: ?{|
before: string,
input: HTMLInputElement,
op: boolean,
di: boolean,
del: boolean,
|} = null;
_del: boolean = false;
_handleChange = (
const onChange = (
evt: SyntheticInputEvent<HTMLInputElement | HTMLTextAreaElement>

@@ -56,125 +39,147 @@ ) => {

}
// FUTURE: use evt.nativeEvent.inputType for del event, see comments at onkeydown
const stateValue = this.state.value;
let value = evt.target.value;
const input = evt.target;
const op = value.length > stateValue.length;
const del = this._del;
const noOp = stateValue === this.props.format(value);
this.setState({ value, local: true }, () => {
const { selectionStart } = input;
const refuse = this.props.refuse || /[^\d]+/g;
const value = props.value;
const eventValue = evt.target.value;
const before = value.substr(0, selectionStart).replace(refuse, '');
valueRef.current = [
eventValue, // eventValue
evt.target, // input
eventValue.length > value.length, // isSizeIncreaseOperation
isDeleleteButtonDownRef.current, // isDeleleteButtonDown
value === props.format(eventValue), // isNoOperation
];
this._state = {
// The main trick is to update underlying input with non formatted value (= eventValue)
// that allows us to calculate right cursor position after formatting (see getCursorPosition)
// then we format new value and call props.onChange with masked/formatted value
// and finally we are able to set cursor position into right place
refresh();
};
// React prints warn on server in non production mode about useLayoutEffect usage
// in both cases it's noop
if (process.env.NODE_ENV === 'production' || typeof window !== 'undefined') {
React.useLayoutEffect(() => {
if (valueRef.current == null) return;
let [
eventValue,
input,
before,
op,
di: del && noOp,
del,
};
isSizeIncreaseOperation,
isDeleleteButtonDown,
// No operation means that value itself hasn't been changed, BTW cursor, selection etc can be changed
isNoOperation,
] = valueRef.current;
valueRef.current = null;
if (this.props.replace && this.props.replace(stateValue) && op && !noOp) {
let start = -1;
for (let i = 0; i !== before.length; ++i) {
// this usually occurs on deleting special symbols like ' here 123'123.00
// in case of isDeleleteButtonDown cursor should move differently vs backspace
const deleteWasNoOp = isDeleleteButtonDown && isNoOperation;
// Create string from only accepted symbols
const clean = str => (str.match(props.accept || /\d/g) || []).join('');
const valueBeforeSelectionStart = clean(
eventValue.substr(0, input.selectionStart)
).toLowerCase();
// trying to find cursor position in formatted value having knowledge about valueBeforeSelectionStart
// This works because we assume that format doesn't change the order of accepted symbols.
// Imagine we have formatter which adds ' symbol between numbers, and by default we refuse all non numeric symbols
// for example we had input = 1'2|'4 (| means cursor position) then user entered '3' symbol
// inputValue = 1'23'|4 so valueBeforeSelectionStart = 123 and formatted value = 1'2'3'4
// calling getCursorPosition("1'2'3'4") will give us position after 3, 1'2'3|'4
// so for formatting just this function to determine cursor position after formatting is enough
// with masking we need to do some additional checks see `replace` below
const getCursorPosition = val => {
let start = 0;
for (let i = 0; i !== valueBeforeSelectionStart.length; ++i) {
start = Math.max(
start,
value.toLowerCase().indexOf(before[i].toLowerCase(), start + 1)
val.toLowerCase().indexOf(valueBeforeSelectionStart[i], start) + 1
);
}
return start;
};
const c = value.substr(start + 1).replace(refuse, '')[0];
start = value.indexOf(c, start + 1);
// Masking part, for masks if size of mask is above some value (props.replace checks that)
// we need to replace symbols instead of do nothing as like in format
if (
props.replace &&
props.replace(props.value) &&
isSizeIncreaseOperation &&
!isNoOperation
) {
let start = getCursorPosition(eventValue);
value = `${value.substr(0, start)}${value.substr(start + 1)}`;
const c = clean(eventValue.substr(start))[0];
start = eventValue.indexOf(c, start);
eventValue = `${eventValue.substr(0, start)}${eventValue.substr(
start + 1
)}`;
}
const fv = this.props.format(value);
const formattedValue = props.format(eventValue);
if (stateValue === fv) {
this.setState({ value });
if (props.value === formattedValue) {
// if nothing changed for formatted value, just refresh so props.value will be used at render
refresh();
} else {
this.props.onChange(fv);
props.onChange(formattedValue);
}
});
};
// until https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/inputType will be supported
// by all major browsers (now supported by: +chrome, +safari, ?edge, !firefox)
// there is no way I found to distinguish in onChange
// backspace or delete was called in some situations
// firefox track https://bugzilla.mozilla.org/show_bug.cgi?id=1447239
_hKD = (evt: KeyboardEvent) => {
if (evt.code === 'Delete') {
this._del = true;
}
};
return () => {
let start = getCursorPosition(formattedValue);
_hKU = (evt: KeyboardEvent) => {
if (evt.code === 'Delete') {
this._del = false;
}
};
// Visually improves working with masked values,
// like cursor jumping over refused symbols
// as an example date mask: was "5|1-24-3" then user pressed "6"
// it becomes "56-|12-43" with this code, and "56|-12-43" without
if (
props.replace &&
(isSizeIncreaseOperation || (isDeleleteButtonDown && !deleteWasNoOp))
) {
while (formattedValue[start] && clean(formattedValue[start]) === '') {
start += 1;
}
}
static getDerivedStateFromProps(props: Props, state: State) {
return {
value: state.local ? state.value : props.value,
local: false,
};
input.selectionStart = input.selectionEnd =
start + (deleteWasNoOp ? 1 : 0);
};
});
}
render() {
const {
_handleChange,
state: { value },
props: { children },
} = this;
return children({ value, onChange: _handleChange });
}
// delete when https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/inputType will be supported by all major browsers
componentWillUnmount() {
document.removeEventListener('keydown', this._hKD);
document.removeEventListener('keyup', this._hKU);
}
// delete when https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/inputType will be supported by all major browsers
componentDidMount() {
document.addEventListener('keydown', this._hKD);
document.addEventListener('keyup', this._hKU);
}
componentDidUpdate() {
const { _state } = this;
if (_state) {
const value = this.state.value;
let start = -1;
for (let i = 0; i !== _state.before.length; ++i) {
start = Math.max(
start,
value.toLowerCase().indexOf(_state.before[i].toLowerCase(), start + 1)
);
React.useEffect(() => {
// until https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/inputType will be supported
// by all major browsers (now supported by: +chrome, +safari, ?edge, !firefox)
// there is no way I found to distinguish in onChange
// backspace or delete was called in some situations
// firefox track https://bugzilla.mozilla.org/show_bug.cgi?id=1447239
const handleKeyDown = (evt: KeyboardEvent) => {
if (evt.code === 'Delete') {
isDeleleteButtonDownRef.current = true;
}
};
// format usually looks better without this
if (this.props.replace && (_state.op || (_state.del && !_state.di))) {
while (
value[start + 1] &&
(this.props.refuse || /[^\d]+/).test(value[start + 1])
) {
start += 1;
}
const handleKeyUp = (evt: KeyboardEvent) => {
if (evt.code === 'Delete') {
isDeleleteButtonDownRef.current = false;
}
};
_state.input.selectionStart = _state.input.selectionEnd =
start + 1 + (_state.di ? 1 : 0);
}
document.addEventListener('keydown', handleKeyDown);
document.addEventListener('keyup', handleKeyUp);
this._state = null;
}
}
return () => {
document.removeEventListener('keydown', handleKeyDown);
document.removeEventListener('keyup', handleKeyUp);
};
}, []);
return props.children({
value: valueRef.current != null ? valueRef.current[0] : props.value,
onChange,
});
};
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc