2
0

CustomInput.jsx 4.6 KB

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