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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. import React, { useCallback } from 'react';
  2. import TextareaAutosize from 'react-textarea-autosize';
  3. import { makeStyles } from 'tss-react/mui';
  4. import { isMobileBrowser } from '../../../environment/utils';
  5. import Icon from '../../../icons/components/Icon';
  6. import { IconCloseCircle } from '../../../icons/svg';
  7. import { withPixelLineHeight } from '../../../styles/functions.web';
  8. import { IInputProps } from '../types';
  9. interface IProps extends IInputProps {
  10. accessibilityLabel?: string;
  11. autoFocus?: boolean;
  12. bottomLabel?: string;
  13. className?: string;
  14. iconClick?: () => void;
  15. id?: string;
  16. maxLength?: number;
  17. maxRows?: number;
  18. minRows?: number;
  19. name?: string;
  20. onKeyPress?: (e: React.KeyboardEvent) => void;
  21. readOnly?: boolean;
  22. textarea?: boolean;
  23. type?: 'text' | 'email' | 'number' | 'password';
  24. }
  25. const useStyles = makeStyles()(theme => {
  26. return {
  27. inputContainer: {
  28. display: 'flex',
  29. flexDirection: 'column'
  30. },
  31. label: {
  32. color: theme.palette.text01,
  33. ...withPixelLineHeight(theme.typography.bodyShortRegular),
  34. marginBottom: theme.spacing(2),
  35. '&.is-mobile': {
  36. ...withPixelLineHeight(theme.typography.bodyShortRegularLarge)
  37. }
  38. },
  39. fieldContainer: {
  40. position: 'relative',
  41. display: 'flex'
  42. },
  43. input: {
  44. backgroundColor: theme.palette.ui03,
  45. background: 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),
  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, IProps>(({
  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. readOnly = false,
  130. textarea = false,
  131. type = 'text',
  132. value
  133. }: IProps, ref) => {
  134. const { classes: styles, cx } = useStyles();
  135. const isMobile = isMobileBrowser();
  136. const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) =>
  137. onChange?.(e.target.value), []);
  138. const clearInput = useCallback(() => onChange?.(''), []);
  139. return (
  140. <div className = { cx(styles.inputContainer, className) }>
  141. {label && <span className = { cx(styles.label, isMobile && 'is-mobile') }>{label}</span>}
  142. <div className = { styles.fieldContainer }>
  143. {icon && <Icon
  144. { ...(iconClick ? { tabIndex: 0 } : {}) }
  145. className = { cx(styles.icon, iconClick && styles.iconClickable) }
  146. onClick = { iconClick }
  147. size = { 20 }
  148. src = { icon } />}
  149. {textarea ? (
  150. <TextareaAutosize
  151. aria-label = { accessibilityLabel }
  152. autoFocus = { autoFocus }
  153. className = { cx(styles.input, isMobile && 'is-mobile',
  154. error && 'error', clearable && styles.clearableInput, icon && 'icon-input') }
  155. disabled = { disabled }
  156. { ...(id ? { id } : {}) }
  157. maxRows = { maxRows }
  158. minRows = { minRows }
  159. name = { name }
  160. onChange = { handleChange }
  161. onKeyPress = { onKeyPress }
  162. placeholder = { placeholder }
  163. readOnly = { readOnly }
  164. ref = { ref }
  165. value = { value } />
  166. ) : (
  167. <input
  168. aria-label = { accessibilityLabel }
  169. autoFocus = { autoFocus }
  170. className = { cx(styles.input, isMobile && 'is-mobile',
  171. error && 'error', clearable && styles.clearableInput, icon && 'icon-input') }
  172. disabled = { disabled }
  173. { ...(id ? { id } : {}) }
  174. maxLength = { maxLength }
  175. name = { name }
  176. onChange = { handleChange }
  177. onKeyPress = { onKeyPress }
  178. placeholder = { placeholder }
  179. readOnly = { readOnly }
  180. ref = { ref }
  181. type = { type }
  182. value = { value } />
  183. )}
  184. {clearable && !disabled && value !== '' && <button className = { styles.clearButton }>
  185. <Icon
  186. onClick = { clearInput }
  187. size = { 20 }
  188. src = { IconCloseCircle } />
  189. </button>}
  190. </div>
  191. {bottomLabel && (
  192. <span className = { cx(styles.bottomLabel, isMobile && 'is-mobile', error && 'error') }>
  193. {bottomLabel}
  194. </span>
  195. )}
  196. </div>
  197. );
  198. });
  199. export default Input;