You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ClearableInput.js 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // @flow
  2. import { makeStyles } from '@material-ui/core';
  3. import React, { useCallback, useEffect, useState } from 'react';
  4. import { Icon, IconCloseSolid } from '../../../base/icons';
  5. type Props = {
  6. /**
  7. * String for html autocomplete attribute.
  8. */
  9. autoComplete?: string,
  10. /**
  11. * If the input should be focused on display.
  12. */
  13. autoFocus?: boolean,
  14. /**
  15. * Class name to be appended to the default class list.
  16. */
  17. className?: string,
  18. /**
  19. * Input id.
  20. */
  21. id?: string,
  22. /**
  23. * Callback for the onChange event of the field.
  24. */
  25. onChange: Function,
  26. /**
  27. * Callback to be used when the user hits Enter in the field.
  28. */
  29. onSubmit?: Function,
  30. /**
  31. * Placeholder text for the field.
  32. */
  33. placeholder: string,
  34. /**
  35. * The field type (e.g. text, password...etc).
  36. */
  37. type?: string,
  38. /**
  39. * TestId of the button. Can be used to locate element when testing UI.
  40. */
  41. testId?: string,
  42. /**
  43. * Externally provided value.
  44. */
  45. value?: string
  46. };
  47. const useStyles = makeStyles(theme => {
  48. return {
  49. clearableInput: {
  50. display: 'flex',
  51. alignItems: 'center',
  52. justifyContent: 'flex-start',
  53. height: '20px',
  54. border: `1px solid ${theme.palette.border02}`,
  55. backgroundColor: theme.palette.uiBackground,
  56. position: 'relative',
  57. borderRadius: '6px',
  58. padding: '10px 16px',
  59. '&.focused': {
  60. border: `3px solid ${theme.palette.field01Focus}`
  61. }
  62. },
  63. clearButton: {
  64. backgroundColor: 'transparent',
  65. border: 0,
  66. position: 'absolute',
  67. right: '10px',
  68. top: '11px',
  69. padding: 0,
  70. '& svg': {
  71. fill: theme.palette.icon02
  72. }
  73. },
  74. input: {
  75. backgroundColor: 'transparent',
  76. border: 0,
  77. width: '100%',
  78. height: '100%',
  79. borderRadius: '6px',
  80. fontSize: '14px',
  81. lineHeight: '20px',
  82. textAlign: 'center',
  83. caretColor: theme.palette.text01,
  84. color: theme.palette.text01,
  85. '&::placeholder': {
  86. color: theme.palette.text03
  87. }
  88. }
  89. };
  90. });
  91. /**
  92. * Implements a pre-styled clearable input field.
  93. *
  94. * @param {Props} props - The props of the component.
  95. * @returns {ReactElement}
  96. */
  97. function ClearableInput({
  98. autoFocus = false,
  99. autoComplete,
  100. className = '',
  101. id,
  102. onChange,
  103. onSubmit,
  104. placeholder,
  105. testId,
  106. type = 'text',
  107. value
  108. }: Props) {
  109. const classes = useStyles();
  110. const [ val, setVal ] = useState(value || '');
  111. const [ focused, setFocused ] = useState(false);
  112. const inputRef = React.createRef();
  113. useEffect(() => {
  114. if (value && value !== val) {
  115. setVal(value);
  116. }
  117. }, [ value ]);
  118. /**
  119. * Callback for the onBlur event of the field.
  120. *
  121. * @returns {void}
  122. */
  123. const _onBlur = useCallback(() => {
  124. setFocused(false);
  125. });
  126. /**
  127. * Callback for the onChange event of the field.
  128. *
  129. * @param {Object} evt - The static event.
  130. * @returns {void}
  131. */
  132. const _onChange = useCallback(evt => {
  133. const newValue = evt.target.value;
  134. setVal(newValue);
  135. onChange && onChange(newValue);
  136. }, [ onChange ]);
  137. /**
  138. * Callback for the onFocus event of the field.
  139. *
  140. * @returns {void}
  141. */
  142. const _onFocus = useCallback(() => {
  143. setFocused(true);
  144. });
  145. /**
  146. * Joins the conference on 'Enter'.
  147. *
  148. * @param {Event} event - Key down event object.
  149. * @returns {void}
  150. */
  151. const _onKeyDown = useCallback(event => {
  152. onSubmit && event.key === 'Enter' && onSubmit();
  153. }, [ onSubmit ]);
  154. /**
  155. * Clears the input.
  156. *
  157. * @returns {void}
  158. */
  159. const _clearInput = useCallback(() => {
  160. if (inputRef.current) {
  161. inputRef.current.focus();
  162. }
  163. setVal('');
  164. onChange && onChange('');
  165. }, [ onChange ]);
  166. return (
  167. <div className = { `${classes.clearableInput} ${focused ? 'focused' : ''} ${className || ''}` }>
  168. <input
  169. autoComplete = { autoComplete }
  170. autoFocus = { autoFocus }
  171. className = { classes.input }
  172. data-testid = { testId ? testId : undefined }
  173. id = { id }
  174. onBlur = { _onBlur }
  175. onChange = { _onChange }
  176. onFocus = { _onFocus }
  177. onKeyDown = { _onKeyDown }
  178. placeholder = { placeholder }
  179. ref = { inputRef }
  180. type = { type }
  181. value = { val } />
  182. {val !== '' && (
  183. <button
  184. className = { classes.clearButton }
  185. onClick = { _clearInput }>
  186. <Icon
  187. size = { 20 }
  188. src = { IconCloseSolid } />
  189. </button>
  190. )}
  191. </div>
  192. );
  193. }
  194. export default ClearableInput;