Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

Input.tsx 7.0KB

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