react-easy-input
Version:
A react input component that has simple validation and masking
278 lines (241 loc) • 8.33 kB
JavaScript
// Generated by CoffeeScript 2.3.1
var Input, Invalid, React, converterModule, converters, get, invalidModule, isInvalid, resetCursor, set;
React = require("react");
converterModule = require("./converters");
invalidModule = require('./Invalid');
get = invalidModule.get;
set = invalidModule.set;
// TODO/Extra
// add some props
// required, invalidClass, validateImmediately(show red on empty required fields onload), validateOnblur (show red only after/not during field editing)
// add more converters
// numeric, integer, decimal, percent
// EVENTUALLY
// add masking, mask=[/[^@]+/,"@",/\d/,"."]
// re-exports
converters = converterModule.converters;
module.exports.converters = converters;
Invalid = invalidModule.Invalid;
module.exports.Invalid = Invalid;
isInvalid = invalidModule.isInvalid;
module.exports.isInvalid = isInvalid;
// helper function
resetCursor = function(thisFromInput) {
var inputSelectionStart, moveCursor;
if (get(thisFromInput, ["refs", "input", "selectionStart"], "")) {
inputSelectionStart = thisFromInput.refs.input.selectionStart;
}
moveCursor = function() {
var cursorPos;
cursorPos = get(thisFromInput, ["refs", "input", "selectionStart"], "");
if (cursorPos) {
if (inputSelectionStart) {
thisFromInput.refs.input.selectionStart = inputSelectionStart;
return thisFromInput.refs.input.selectionEnd = inputSelectionStart;
}
}
};
return setTimeout(moveCursor, 0);
};
Input = class Input extends React.Component {
constructor(props) {
super(props);
this.hasCompletedTheInitialFilter = false;
}
render() {
var classAdd, className, converter, displayInvalid, each, expectedProps, i, incomingFilter, initialValue, invalidStyle, len, linkTo, newProps, outgoingFilter, props, ref, value, valueIsInvalid;
props = this.props;
// extract values from props
expectedProps = [];
expectedProps.push("invalidStyle");
if (props.invalidStyle) {
invalidStyle = props.invalidStyle;
} else {
invalidStyle = null;
}
expectedProps.push("linkTo");
if (props.linkTo) {
linkTo = props.linkTo;
} else {
linkTo = null;
}
expectedProps.push("className");
if (props.className) {
className = props.className;
} else {
className = "easy-input";
}
expectedProps.push("classAdd");
if (props.classAdd) {
classAdd = props.classAdd;
} else {
classAdd = "";
}
expectedProps.push("incomingFilter");
if (props.incomingFilter) {
incomingFilter = props.incomingFilter;
} else {
incomingFilter = null;
}
expectedProps.push("outgoingFilter");
if (props.outgoingFilter) {
outgoingFilter = props.outgoingFilter;
} else {
outgoingFilter = null;
}
expectedProps.push("value");
if (props.value) {
value = props.value;
} else {
value = null;
}
// create a mutable version of props
newProps = {};
ref = Object.keys(props);
for (i = 0, len = ref.length; i < len; i++) {
each = ref[i];
if (!expectedProps.includes(each)) {
newProps[each] = props[each];
}
}
// retrieve converters
if (converters[newProps.type]) {
converter = converters[newProps.type];
} else {
converter = {};
}
if (!outgoingFilter) {
outgoingFilter = converter.outgoingFilter;
}
if (!incomingFilter) {
incomingFilter = converter.incomingFilter;
}
// FIXME, wrap the incomingFilter to make sure it always receives non-Invalid() values
// calculate value
if (newProps.this && linkTo) {
// retrieve the actual value from the component's state
newProps.value = get(newProps.this.state, linkTo, "");
}
// Check the initial value
if (incomingFilter && !this.hasCompletedTheInitialFilter) {
initialValue = newProps.value;
newProps.value = incomingFilter(newProps.value);
this.hasCompletedTheInitialFilter = true;
if (initialValue !== newProps.value) {
// if the initial value is different, call the onchange
if (newProps.onChange) {
newProps.onChange({
target: {
value: newProps.value
}
});
}
}
}
// preserve the validity/un-validityness
valueIsInvalid = isInvalid(newProps.value); //FIXME, there could be a better solution than this
// always run outgoing filters
if (outgoingFilter) {
newProps.value = outgoingFilter(newProps.value);
}
// always convert null values to "" (otherwise react will complain)
if (newProps.value === null || newProps.value === void 0) {
newProps.value = "";
}
if (valueIsInvalid) {
newProps.value = new Invalid(newProps.value);
}
// Compute onChange
if (!newProps.onChange) {
// first reset the cursor, then handle the change
newProps.onChange = (e) => {
var copyOfState, newValue;
newValue = e.target.value;
resetCursor(this);
// if there is a converter function, then run the function before it returns to state
// for example convert "True" into the boolean: true,
// or convert the string "Jan 12 2017" to dateTime(1,12,2017)
if (incomingFilter) {
newValue = incomingFilter(newValue);
}
// create a copy of state instead of mutating the original
copyOfState = Object.assign(newProps.this.state);
invalidModule.set(copyOfState, linkTo, newValue);
return props.this.setState(copyOfState);
};
} else {
newProps.onChange = (e) => {
var newValue;
e.persist(); // react will destroy e if we don't tell it to persist
newValue = e.target.value;
resetCursor(this);
// if there is a converter function, then run the function before it returns to state
// for example convert "True" into the boolean: true,
// or convert the string "Jan 12 2017" to dateTime(1,12,2017)
if (incomingFilter) {
newValue = incomingFilter(newValue);
}
return props.onChange({
target: {
value: newValue
}
});
};
}
// Calculate styling/css class
// add additional classes
newProps.className = className + " " + classAdd;
displayInvalid = false;
// if 'invalid' prop was set to something (true/false)
if (typeof newProps.invalid === 'bool') {
// and if 'invalid' is true
if (newProps.invalid === true) {
// then display it
displayInvalid = true;
}
// if 'invalid' is false, dont add error class
// if 'invalid' prop was not set, but the state value is indeed invalid, then displayInvalid
} else if (isInvalid(newProps.value)) {
// then display it
displayInvalid = true;
}
if (displayInvalid === true) {
// add the error css class
className = "easy-input-error " + className;
// check if there is an invalid style
if (invalidStyle) {
// if there is one then attach it
newProps.style = invalidStyle;
}
}
// set a reference for fixing the cursor jump
newProps.ref = "input";
// return the react input component
return React.createElement('input', newProps, null);
}
};
module.exports.Input = Input;
// validate onblur instructions (implement in next few versions)
// onChange={
// (e)=>{
// var val = e.target.value
// e.persist()
// setTimeout(() => {
// e.target.onblur = () => {
// console.log(`val is:`,val)
// var matches = val.match(/^.+@.+\..+$/)
// console.log(`val.match(/\\d+/) is:`,val.match(/\d+/))
// if (!matches) {
// console.log(`doesnt match`)
// e.target.setCustomValidity("Needs to be a number")
// e.target.classList.add('input-err')
// // this.refs.form.reportValidity()
// } else {
// console.log(`matches`)
// }
// }
// }, 0);
// this.setState({Nums:val})
// }
// }
// />