CustomInput.jsx 5.6 KB

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