CustomInput.jsx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  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. isInput: true,
  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. isInput: true,
  33. regex: regex.emailSimple,
  34. errors: {
  35. format: t("general:invalidEmailFormat"),
  36. },
  37. },
  38. password: {
  39. inputType: "password",
  40. minLength: 6,
  41. maxLength: 200,
  42. isInput: true,
  43. regex: regex.password,
  44. errors: {
  45. format: t("general:invalidPasswordFormat", { characters: "$@$!%*?&" }),
  46. },
  47. },
  48. uniqueCode: {
  49. inputType: "text",
  50. minLength: 8,
  51. maxLength: 8,
  52. isInput: true,
  53. regex: regex.azAZ09,
  54. errors: {
  55. length: t("general:invalidCodeLength", { length: 8 }),
  56. format: t("general:invalidCodeFormat"),
  57. },
  58. },
  59. stationName: {
  60. inputType: "text",
  61. minLength: 2,
  62. maxLength: 16,
  63. isInput: true,
  64. regex: regex.az09_,
  65. errors: {
  66. //format: t("general:invalidUsernameFormat", { characters: `a-z, A-Z, 0-9${ t("general:and") } _` }),
  67. format: t("general:invalidStationNameFormat", { characters: `a-z, 0-9${ t("general:and") } _` }),
  68. },
  69. },
  70. stationDisplayName: {
  71. inputType: "text",
  72. minLength: 2,
  73. maxLength: 32,
  74. isInput: true,
  75. regex: regex.azAZ09_,
  76. errors: {
  77. //format: t("general:invalidUsernameFormat", { characters: `a-z, A-Z, 0-9${ t("general:and") } _` }),
  78. format: t("general:invalidStationDisplayNameFormat", { characters: `a-z, A-Z, 0-9${ t("general:and") } _` }),
  79. },
  80. },
  81. stationDescription: {
  82. inputType: "text",
  83. minLength: 2,
  84. maxLength: 200,
  85. isTextarea: true,
  86. errors: {},
  87. },
  88. playlistDescription: {
  89. inputType: "text",
  90. minLength: 1,
  91. maxLength: 16,
  92. isInput: true,
  93. regex: regex.ascii,
  94. errors: {
  95. //format: t("general:invalidUsernameFormat", { characters: `a-z, A-Z, 0-9${ t("general:and") } _` }),
  96. format: "Only ascii is allowed",
  97. },
  98. },
  99. stationPrivacy: {
  100. isRadio: true,
  101. options: [
  102. {
  103. text: "Public - Lorem ipsum lorem ipsum lorem ipsum",
  104. value: "public",
  105. },
  106. {
  107. text: "Unlisted - Lorem ipsum lorem ipsum lorem ipsum",
  108. value: "unlisted",
  109. },
  110. {
  111. text: "Private - Lorem ipsum lorem ipsum lorem ipsum",
  112. value: "private",
  113. },
  114. ],
  115. },
  116. };
  117. export default class CustomInput extends Component {
  118. static propTypes = {
  119. type: PropTypes.string,
  120. original: PropTypes.string,
  121. name: PropTypes.string,
  122. label: PropTypes.string,
  123. //showLabel: PropTypes.boolean,
  124. placeholder: PropTypes.string,
  125. onRef: PropTypes.func,
  126. };
  127. static defaultProps = {
  128. type: "",
  129. original: "",
  130. name: "",
  131. label: "",
  132. //showLabel: true,
  133. placeholder: "",
  134. valid: false,
  135. onRef: () => {},
  136. };
  137. static initialize = (context) => {
  138. context.input = {}; // eslint-disable-line no-param-reassign
  139. };
  140. static hasInvalidInput = (input, properties) => {
  141. let invalid = false;
  142. if (properties) {
  143. properties.forEach((property) => {
  144. if (!input[property].isValid()) invalid = true;
  145. });
  146. } else {
  147. Object.keys(input).forEach((key) => {
  148. if (!input[key].isValid()) invalid = true;
  149. });
  150. }
  151. return invalid;
  152. };
  153. static isTheSame = (input, properties) => {
  154. let invalid = false;
  155. const value = input[properties[0]].getValue();
  156. properties.forEach((key) => {
  157. if (input[key].getValue() !== value) invalid = true;
  158. });
  159. return invalid;
  160. };
  161. constructor(props) {
  162. super(props);
  163. this.state = {
  164. inputType: dictionary[props.type].inputType,
  165. isTextarea: (typeof dictionary[props.type].isTextarea === "boolean") ? dictionary[props.type].isTextarea : false,
  166. isInput: (typeof dictionary[props.type].isInput === "boolean") ? dictionary[props.type].isInput : false,
  167. isRadio: (typeof dictionary[props.type].isRadio === "boolean") ? dictionary[props.type].isRadio : false,
  168. options: (typeof dictionary[props.type].options === "object") ? dictionary[props.type].options : null,
  169. value: "",
  170. original: this.props.original,
  171. errors: [],
  172. pristine: true,
  173. disabled: false,
  174. valid: false,
  175. };
  176. // More values/functions needs like isEmpty, isRequired
  177. }
  178. componentDidMount() {
  179. this.props.onRef(this);
  180. }
  181. componentWillUnmount() {
  182. this.props.onRef(null);
  183. }
  184. onBlur = () => {
  185. this.validate();
  186. };
  187. onFocus = () => {
  188. this.setState({
  189. pristine: false,
  190. });
  191. };
  192. onChange = (event) => {
  193. this.setState({
  194. value: event.target.value,
  195. });
  196. };
  197. setValue = (value, original = false) => {
  198. const state = {
  199. value,
  200. };
  201. if (original) state.original = value;
  202. this.setState(state, () => {
  203. if (this.state.isRadio) {
  204. this.validate();
  205. }
  206. });
  207. };
  208. getValue = () => {
  209. return this.state.value;
  210. };
  211. listErrors = () => {
  212. let errors = this.state.errors;
  213. let key = 0;
  214. if (errors.length > 0) {
  215. errors = errors.map((error) => {
  216. key++;
  217. return (<li key={ key }>{ error }</li>);
  218. });
  219. return (
  220. <ul className="validation-errors">
  221. { errors }
  222. </ul>
  223. );
  224. } return "";
  225. };
  226. isValid = () => {
  227. return this.state.valid;
  228. };
  229. isOriginal = () => {
  230. return this.state.original === this.state.value;
  231. };
  232. isPristine = () => {
  233. return this.state.pristine;
  234. };
  235. validate = (cb = () => {}) => {
  236. if (!this.state.isRadio) {
  237. const errors = [];
  238. const info = dictionary[this.props.type];
  239. const value = this.state.value;
  240. if (!isLength(value, info.minLength, info.maxLength)) errors.push((info.errors.length) ? info.errors.length : t("general:valueMustBeBetween", {
  241. min: info.minLength,
  242. max: info.maxLength
  243. }));
  244. if (info.regex && !info.regex.test(value)) errors.push(info.errors.format);
  245. this.setState({
  246. errors,
  247. valid: errors.length === 0,
  248. }, cb);
  249. } else {
  250. this.setState({
  251. valid: this.state.value !== null,
  252. }, cb);
  253. }
  254. };
  255. componentWillMount() {
  256. if (this.props.original) {
  257. this.setValue(this.props.original, true);
  258. }
  259. }
  260. componentDidUpdate(prevProps, prevState) {
  261. if (this.props.original !== prevProps.original) {
  262. this.setValue(this.props.original, true);
  263. }
  264. }
  265. render() {
  266. if (this.state.isTextarea || this.state.isInput) {
  267. const ElementType = (this.state.isInput) ? "input" : "textarea";
  268. return (
  269. <label htmlFor={this.props.name}>
  270. {(this.props.showLabel) ? <span>{this.props.label}</span> : null}
  271. <ElementType
  272. placeholder={this.props.placeholder}
  273. type={this.state.inputType}
  274. name={this.props.name}
  275. value={this.state.value}
  276. className={(this.state.errors.length > 0) ? "has-validation-errors" : ""}
  277. onBlur={this.onBlur}
  278. onFocus={this.onFocus}
  279. onChange={this.onChange}
  280. ref={(input) => this.inputElement = input}
  281. />
  282. {this.listErrors()}
  283. </label>
  284. );
  285. } else {
  286. let optionsArr = this.state.options.map((option) => {
  287. let checked = option.value === this.state.value;
  288. return (
  289. <label key={ option.value }>
  290. <input type="radio" name={ this.props.type } value={ option.value } checked={ checked } onChange={ this.onChange }/>
  291. <span>{ option.text }</span>
  292. </label>
  293. );
  294. });
  295. return (
  296. <label htmlFor={this.props.name}>
  297. {(this.props.showLabel) ? <span>{this.props.label}</span> : null}
  298. <div>
  299. { optionsArr }
  300. {/*<ElementType
  301. placeholder={this.props.placeholder}
  302. type={this.state.inputType}
  303. name={this.props.name}
  304. value={this.state.value}
  305. className={(this.state.errors.length > 0) ? "has-validation-errors" : ""}
  306. onBlur={this.onBlur}
  307. onFocus={this.onFocus}
  308. onChange={this.onChange}
  309. ref={(input) => this.inputElement = input}
  310. />*/}
  311. </div>
  312. {this.listErrors()}
  313. </label>
  314. );
  315. }
  316. }
  317. }