CustomInput.jsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  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. stationName: {
  56. inputType: "text",
  57. minLength: 2,
  58. maxLength: 16,
  59. regex: regex.az09_,
  60. errors: {
  61. //format: t("general:invalidUsernameFormat", { characters: `a-z, A-Z, 0-9${ t("general:and") } _` }),
  62. format: t("general:invalidStationNameFormat", { characters: `a-z, 0-9${ t("general:and") } _` }),
  63. },
  64. },
  65. stationDisplayName: {
  66. inputType: "text",
  67. minLength: 2,
  68. maxLength: 32,
  69. regex: regex.azAZ09_,
  70. errors: {
  71. //format: t("general:invalidUsernameFormat", { characters: `a-z, A-Z, 0-9${ t("general:and") } _` }),
  72. format: t("general:invalidStationDisplayNameFormat", { characters: `a-z, A-Z, 0-9${ t("general:and") } _` }),
  73. },
  74. },
  75. stationDescription: {
  76. inputType: "text",
  77. minLength: 2,
  78. maxLength: 200,
  79. isTextarea: true,
  80. errors: {},
  81. },
  82. };
  83. export default class CustomInput extends Component {
  84. static propTypes = {
  85. type: PropTypes.string,
  86. name: PropTypes.string,
  87. label: PropTypes.string,
  88. //showLabel: PropTypes.boolean,
  89. placeholder: PropTypes.string,
  90. onRef: PropTypes.func,
  91. };
  92. static defaultProps = {
  93. type: "",
  94. name: "",
  95. label: "",
  96. //showLabel: true,
  97. placeholder: "",
  98. valid: false,
  99. onRef: () => {},
  100. };
  101. static initialize = (context) => {
  102. context.input = {}; // eslint-disable-line no-param-reassign
  103. };
  104. static hasInvalidInput = (input, properties) => {
  105. let invalid = false;
  106. if (properties) {
  107. properties.forEach((property) => {
  108. if (!input[property].isValid()) invalid = true;
  109. });
  110. } else {
  111. Object.keys(input).forEach((key) => {
  112. if (!input[key].isValid()) invalid = true;
  113. });
  114. }
  115. return invalid;
  116. };
  117. static isTheSame = (input, properties) => {
  118. let invalid = false;
  119. const value = input[properties[0]].getValue();
  120. properties.forEach((key) => {
  121. if (input[key].getValue() !== value) invalid = true;
  122. });
  123. return invalid;
  124. };
  125. constructor(props) {
  126. super(props);
  127. this.state = {
  128. inputType: dictionary[props.type].inputType,
  129. isTextarea: (typeof dictionary[props.type].isTextarea === "boolean") ? dictionary[props.type].isTextarea : false,
  130. value: "",
  131. original: "",
  132. errors: [],
  133. pristine: true,
  134. disabled: false,
  135. valid: false,
  136. };
  137. // More values/functions needs like isEmpty, isRequired
  138. }
  139. componentDidMount() {
  140. this.props.onRef(this);
  141. }
  142. componentWillUnmount() {
  143. this.props.onRef(null);
  144. }
  145. onBlur = () => {
  146. this.validate();
  147. };
  148. onFocus = () => {
  149. this.setState({
  150. pristine: false,
  151. });
  152. };
  153. onChange = (event) => {
  154. this.setState({
  155. value: event.target.value,
  156. });
  157. };
  158. setValue = (value, original = false) => {
  159. const state = {
  160. value,
  161. };
  162. if (original) state.original = value;
  163. this.setState(state);
  164. };
  165. getValue = () => {
  166. return this.state.value;
  167. };
  168. listErrors = () => {
  169. let errors = this.state.errors;
  170. let key = 0;
  171. if (errors.length > 0) {
  172. errors = errors.map((error) => {
  173. key++;
  174. return (<li key={ key }>{ error }</li>);
  175. });
  176. return (
  177. <ul className="validation-errors">
  178. { errors }
  179. </ul>
  180. );
  181. } return "";
  182. };
  183. isValid = () => {
  184. return this.state.valid;
  185. };
  186. isOriginal = () => {
  187. return this.state.original === this.state.value;
  188. };
  189. isPristine = () => {
  190. return this.state.pristine;
  191. };
  192. validate = (cb = () => {}) => {
  193. const errors = [];
  194. const info = dictionary[this.props.type];
  195. const value = this.state.value;
  196. if (!isLength(value, info.minLength, info.maxLength)) errors.push((info.errors.length) ? info.errors.length : t("general:valueMustBeBetween", { min: info.minLength, max: info.maxLength }));
  197. if (info.regex && !info.regex.test(value)) errors.push(info.errors.format);
  198. this.setState({
  199. errors,
  200. valid: errors.length === 0,
  201. }, cb);
  202. };
  203. render() {
  204. const ElementType = (!this.state.isTextarea) ? "input" : "textarea";
  205. return (
  206. <label htmlFor={ this.props.name }>
  207. {(this.props.showLabel) ? <span>{ this.props.label }</span> : null}
  208. <ElementType
  209. placeholder={ this.props.placeholder }
  210. type={ this.state.inputType }
  211. name={ this.props.name }
  212. value={ this.state.value }
  213. className={ (this.state.errors.length > 0) ? "has-validation-errors" : "" }
  214. onBlur={ this.onBlur }
  215. onFocus={ this.onFocus }
  216. onChange={ this.onChange }
  217. ref={ (input) => this.inputElement = input }
  218. />
  219. { this.listErrors() }
  220. </label>
  221. );
  222. }
  223. }