Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import { makeStyles } from '@material-ui/core';
  2. import clsx from 'clsx';
  3. import React, { useCallback } from 'react';
  4. import TextareaAutosize from 'react-textarea-autosize';
  5. import { isMobileBrowser } from '../../../environment/utils';
  6. import Icon from '../../../icons/components/Icon';
  7. import { IconCloseCircle } from '../../../icons/svg/index';
  8. import { withPixelLineHeight } from '../../../styles/functions.web';
  9. import { Theme } from '../../../ui/types';
  10. import { InputProps } from '../types';
  11. interface IInputProps extends InputProps {
  12. accessibilityLabel?: string;
  13. autoFocus?: boolean;
  14. bottomLabel?: string;
  15. className?: string;
  16. iconClick?: () => void;
  17. id?: string;
  18. maxLength?: number;
  19. maxRows?: number;
  20. minRows?: number;
  21. name?: string;
  22. onKeyPress?: (e: React.KeyboardEvent) => void;
  23. textarea?: boolean;
  24. type?: 'text' | 'email' | 'number' | 'password';
  25. }
  26. const useStyles = makeStyles((theme: Theme) => {
  27. return {
  28. inputContainer: {
  29. display: 'flex',
  30. flexDirection: 'column'
  31. },
  32. label: {
  33. color: theme.palette.text01,
  34. ...withPixelLineHeight(theme.typography.bodyShortRegular),
  35. marginBottom: `${theme.spacing(2)}px`,
  36. '&.is-mobile': {
  37. ...withPixelLineHeight(theme.typography.bodyShortRegularLarge)
  38. }
  39. },
  40. fieldContainer: {
  41. position: 'relative',
  42. display: 'flex'
  43. },
  44. input: {
  45. backgroundColor: theme.palette.ui03,
  46. color: theme.palette.text01,
  47. ...withPixelLineHeight(theme.typography.bodyShortRegular),
  48. padding: '10px 16px',
  49. borderRadius: theme.shape.borderRadius,
  50. border: 0,
  51. height: '40px',
  52. boxSizing: 'border-box',
  53. width: '100%',
  54. '&::placeholder': {
  55. color: theme.palette.text02
  56. },
  57. '&:focus': {
  58. outline: 0,
  59. boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`
  60. },
  61. '&:disabled': {
  62. color: theme.palette.text03
  63. },
  64. '&.is-mobile': {
  65. height: '48px',
  66. padding: '13px 16px',
  67. ...withPixelLineHeight(theme.typography.bodyShortRegularLarge)
  68. },
  69. '&.icon-input': {
  70. paddingLeft: '46px'
  71. },
  72. '&.error': {
  73. boxShadow: `0px 0px 0px 2px ${theme.palette.textError}`
  74. }
  75. },
  76. icon: {
  77. position: 'absolute',
  78. top: '50%',
  79. transform: 'translateY(-50%)',
  80. left: '16px'
  81. },
  82. iconClickable: {
  83. cursor: 'pointer'
  84. },
  85. clearableInput: {
  86. paddingRight: '46px'
  87. },
  88. clearButton: {
  89. position: 'absolute',
  90. right: '16px',
  91. top: '10px',
  92. cursor: 'pointer',
  93. backgroundColor: theme.palette.action03,
  94. border: 0,
  95. padding: 0
  96. },
  97. bottomLabel: {
  98. marginTop: `${theme.spacing(2)}px`,
  99. ...withPixelLineHeight(theme.typography.labelRegular),
  100. color: theme.palette.text02,
  101. '&.is-mobile': {
  102. ...withPixelLineHeight(theme.typography.bodyShortRegular)
  103. },
  104. '&.error': {
  105. color: theme.palette.textError
  106. }
  107. }
  108. };
  109. });
  110. const Input = React.forwardRef<any, IInputProps>(({
  111. accessibilityLabel,
  112. autoFocus,
  113. bottomLabel,
  114. className,
  115. clearable = false,
  116. disabled,
  117. error,
  118. icon,
  119. iconClick,
  120. id,
  121. label,
  122. maxLength,
  123. maxRows,
  124. minRows,
  125. name,
  126. onChange,
  127. onKeyPress,
  128. placeholder,
  129. textarea = false,
  130. type = 'text',
  131. value
  132. }: IInputProps, ref) => {
  133. const styles = useStyles();
  134. const isMobile = isMobileBrowser();
  135. const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement|HTMLTextAreaElement>) =>
  136. onChange(e.target.value), []);
  137. const clearInput = useCallback(() => onChange(''), []);
  138. return (<div className = { clsx(styles.inputContainer, className) }>
  139. {label && <span className = { clsx(styles.label, isMobile && 'is-mobile') }>{label}</span>}
  140. <div className = { styles.fieldContainer }>
  141. {icon && <Icon
  142. { ...(iconClick ? { tabIndex: 0 } : {}) }
  143. className = { clsx(styles.icon, iconClick && styles.iconClickable) }
  144. onClick = { iconClick }
  145. size = { 20 }
  146. src = { icon } />}
  147. {textarea ? (
  148. <TextareaAutosize
  149. aria-label = { accessibilityLabel }
  150. autoFocus = { autoFocus }
  151. className = { clsx(styles.input, isMobile && 'is-mobile',
  152. error && 'error', clearable && styles.clearableInput, icon && 'icon-input') }
  153. disabled = { disabled }
  154. { ...(id ? { id } : {}) }
  155. maxRows = { maxRows }
  156. minRows = { minRows }
  157. name = { name }
  158. onChange = { handleChange }
  159. onKeyPress = { onKeyPress }
  160. placeholder = { placeholder }
  161. ref = { ref }
  162. value = { value } />
  163. ) : (
  164. <input
  165. aria-label = { accessibilityLabel }
  166. autoFocus = { autoFocus }
  167. className = { clsx(styles.input, isMobile && 'is-mobile',
  168. error && 'error', clearable && styles.clearableInput, icon && 'icon-input') }
  169. disabled = { disabled }
  170. { ...(id ? { id } : {}) }
  171. maxLength = { maxLength }
  172. name = { name }
  173. onChange = { handleChange }
  174. onKeyPress = { onKeyPress }
  175. placeholder = { placeholder }
  176. ref = { ref }
  177. type = { type }
  178. value = { value } />
  179. )}
  180. {clearable && !disabled && value !== '' && <button className = { styles.clearButton }>
  181. <Icon
  182. onClick = { clearInput }
  183. size = { 20 }
  184. src = { IconCloseCircle } />
  185. </button>}
  186. </div>
  187. {bottomLabel && (
  188. <span className = { clsx(styles.bottomLabel, isMobile && 'is-mobile', error && 'error') }>
  189. {bottomLabel}
  190. </span>
  191. )}
  192. </div>);
  193. });
  194. export default Input;