CustomInput.jsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import React, { Component } from "react";
  2. import PropTypes from "prop-types";
  3. const i18next = require("i18next");
  4. const t = i18next.t;
  5. const isLength = (string, min, max) => {
  6. return !(typeof string !== "string" || string.length < min || string.length > max);
  7. };
  8. const regex = {
  9. azAZ09_: /^[A-Za-z0-9_]+$/,
  10. azAZ09: /^[A-Za-z0-9]+$/,
  11. az09_: /^[a-z0-9_]+$/,
  12. az09: /^[a-z0-9]+$/,
  13. emailSimple: /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-z0-9]+\.[a-z0-9]+(\.[a-z0-9]+)?$/,
  14. password: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]/,
  15. ascii: /^[\x00-\x7F]+$/,
  16. };
  17. const dictionary = {
  18. username: {
  19. inputType: "text",
  20. minLength: 2,
  21. maxLength: 32,
  22. regex: regex.azAZ09_,
  23. errors: {
  24. format: t("general:invalidUsernameFormat", { characters: `a-z, A-Z, 0-9${ t("general:and") } _` }),
  25. },
  26. },
  27. email: {
  28. inputType: "email",
  29. minLength: 3,
  30. maxLength: 254,
  31. regex: regex.emailSimple,
  32. errors: {
  33. format: t("general:invalidEmailFormat"),
  34. },
  35. },
  36. password: {
  37. inputType: "password",
  38. minLength: 6,
  39. maxLength: 200,
  40. regex: regex.password,
  41. errors: {
  42. format: t("general:invalidPasswordFormat", { characters: "$@$!%*?&" }),
  43. },
  44. },
  45. uniqueCode: {
  46. inputType: "text",
  47. minLength: 8,
  48. maxLength: 8,
  49. regex: regex.azAZ09,
  50. errors: {
  51. length: t("general:invalidCodeLength", { length: 8 }),
  52. format: t("general:invalidCodeFormat"),
  53. },
  54. },
  55. };
  56. export default class CustomInput extends Component {
  57. static propTypes = {
  58. type: PropTypes.string,
  59. name: PropTypes.string,
  60. label: PropTypes.string,
  61. placeholder: PropTypes.string,
  62. onRef: PropTypes.func,
  63. };
  64. static defaultProps = {
  65. type: "",
  66. name: "",
  67. label: "",
  68. placeholder: "",
  69. valid: false,
  70. onRef: () => {},
  71. };
  72. static initialize = (context) => {
  73. context.input = {}; // eslint-disable-line no-param-reassign
  74. };
  75. static hasInvalidInput = (input, properties) => {
  76. let invalid = false;
  77. if (properties) {
  78. properties.forEach((property) => {
  79. if (!input[property].isValid()) invalid = true;
  80. });
  81. } else {
  82. Object.keys(input).forEach((key) => {
  83. if (!input[key].isValid()) invalid = true;
  84. });
  85. }
  86. return invalid;
  87. };
  88. static isTheSame = (input, properties) => {
  89. let invalid = false;
  90. const value = input[properties[0]].getValue();
  91. properties.forEach((key) => {
  92. if (input[key].getValue() !== value) invalid = true;
  93. });
  94. return invalid;
  95. };
  96. constructor(props) {
  97. super(props);
  98. this.state = {
  99. inputType: dictionary[props.type].inputType,
  100. value: "",
  101. original: "",
  102. errors: [],
  103. pristine: true,
  104. disabled: false,
  105. valid: false,
  106. };
  107. // More values/functions needs like isEmpty, isRequired
  108. }
  109. componentDidMount() {
  110. this.props.onRef(this);
  111. }
  112. componentWillUnmount() {
  113. this.props.onRef(null);
  114. }
  115. onBlur = () => {
  116. this.validate();
  117. };
  118. onFocus = () => {
  119. this.setState({
  120. pristine: false,
  121. });
  122. };
  123. onChange = (event) => {
  124. this.setState({
  125. value: event.target.value,
  126. });
  127. };
  128. setValue = (value, original = false) => {
  129. const state = {
  130. value,
  131. };
  132. if (original) state.original = value;
  133. this.setState(state);
  134. };
  135. getValue = () => {
  136. return this.state.value;
  137. };
  138. listErrors = () => {
  139. let errors = this.state.errors;
  140. let key = 0;
  141. if (errors.length > 0) {
  142. errors = errors.map((error) => {
  143. key++;
  144. return (<li key={ key }>{ error }</li>);
  145. });
  146. return (
  147. <ul className="validation-errors">
  148. { errors }
  149. </ul>
  150. );
  151. } return "";
  152. };
  153. isValid = () => {
  154. return this.state.valid;
  155. };
  156. isOriginal = () => {
  157. return this.state.original === this.state.value;
  158. };
  159. isPristine = () => {
  160. return this.state.pristine;
  161. };
  162. validate = (cb = () => {}) => {
  163. const errors = [];
  164. const info = dictionary[this.props.type];
  165. const value = this.state.value;
  166. if (!isLength(value, info.minLength, info.maxLength)) errors.push((info.errors.length) ? info.errors.length : t("general:valueMustBeBetween", { min: info.minLength, max: info.maxLength }));
  167. if (!info.regex.test(value)) errors.push(info.errors.format);
  168. this.setState({
  169. errors,
  170. valid: errors.length === 0,
  171. }, cb);
  172. };
  173. render() {
  174. return (
  175. <label htmlFor={ this.props.name }>
  176. <span>{ this.props.label }</span>
  177. <input
  178. placeholder={ this.props.placeholder }
  179. type={ this.state.inputType }
  180. name={ this.props.name }
  181. value={ this.state.value }
  182. className={ (this.state.errors.length > 0) ? "has-validation-errors" : "" }
  183. onBlur={ this.onBlur }
  184. onFocus={ this.onFocus }
  185. onChange={ this.onChange }
  186. ref={ (input) => this.inputElement = input }
  187. />
  188. { this.listErrors() }
  189. </label>
  190. );
  191. }
  192. }