input.tsx 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import {forwardRef} from 'react';
  2. import isPropValid from '@emotion/is-prop-valid';
  3. import {css} from '@emotion/react';
  4. import styled from '@emotion/styled';
  5. import {FormSize, Theme} from 'sentry/utils/theme';
  6. export interface InputStylesProps {
  7. monospace?: boolean;
  8. nativeSize?: React.InputHTMLAttributes<HTMLInputElement>['size'];
  9. readOnly?: React.InputHTMLAttributes<HTMLInputElement>['readOnly'];
  10. size?: FormSize;
  11. type?: React.HTMLInputTypeAttribute;
  12. }
  13. export const inputStyles = (p: InputStylesProps & {theme: Theme}) => css`
  14. display: block;
  15. width: 100%;
  16. color: ${p.theme.formText};
  17. background: ${p.theme.background};
  18. border: 1px solid ${p.theme.border};
  19. border-radius: ${p.theme.borderRadius};
  20. box-shadow: inset ${p.theme.dropShadowLight};
  21. resize: vertical;
  22. transition: border 0.1s, box-shadow 0.1s;
  23. ${p.monospace ? `font-family: ${p.theme.text.familyMono};` : ''}
  24. ${p.readOnly ? 'cursor: default;' : ''}
  25. ${p.theme.form[p.size ?? 'md']}
  26. ${p.theme.formPadding[p.size ?? 'md']}
  27. &::placeholder {
  28. color: ${p.theme.formPlaceholder};
  29. opacity: 1;
  30. }
  31. &[disabled] {
  32. background: ${p.theme.backgroundSecondary};
  33. color: ${p.theme.disabled};
  34. cursor: not-allowed;
  35. &::placeholder {
  36. color: ${p.theme.disabled};
  37. }
  38. }
  39. &:focus,
  40. &.focus-visible {
  41. outline: none;
  42. border-color: ${p.theme.focusBorder};
  43. box-shadow: ${p.theme.focusBorder} 0 0 0 1px;
  44. }
  45. `;
  46. export interface InputProps
  47. extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'readOnly'>,
  48. InputStylesProps {}
  49. /**
  50. * Basic input component.
  51. *
  52. * Use the `size` prop ('md', 'sm', 'xs') to control the input's height &
  53. * padding. To use the native size attribute (which controls the number of
  54. * characters the input should fit), use the `nativeSize` prop instead.
  55. *
  56. * To add leading/trailing items (e.g. a search icon on the left side), use
  57. * InputControl (components/inputControl) instead.
  58. */
  59. const Input = styled(
  60. forwardRef<HTMLInputElement, InputProps>(
  61. (
  62. {
  63. // Do not forward `required` to avoid default browser behavior
  64. required: _required,
  65. // Do not forward `size` since it's used for custom styling, not as the
  66. // native `size` attribute (for that, use `nativeSize` instead)
  67. size: _size,
  68. // Use `nativeSize` as the native `size` attribute
  69. nativeSize,
  70. ...props
  71. },
  72. ref
  73. ) => <input {...props} ref={ref} size={nativeSize} />
  74. ),
  75. {shouldForwardProp: prop => typeof prop === 'string' && isPropValid(prop)}
  76. )`
  77. ${inputStyles}
  78. `;
  79. export default Input;