input.tsx 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  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 InputProps
  7. extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
  8. monospace?: boolean;
  9. nativeSize?: React.InputHTMLAttributes<HTMLInputElement>['size'];
  10. size?: FormSize;
  11. type?: React.HTMLInputTypeAttribute;
  12. }
  13. export const inputStyles = (p: InputProps & {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. /**
  47. * Basic input component.
  48. *
  49. * Use the `size` prop ('md', 'sm', 'xs') to control the input's height &
  50. * padding. To use the native size attribute (which controls the number of
  51. * characters the input should fit), use the `nativeSize` prop instead.
  52. *
  53. * To add leading/trailing items (e.g. a search icon on the left side), use
  54. * InputControl (components/inputControl) instead.
  55. */
  56. const Input = styled(
  57. forwardRef<HTMLInputElement, InputProps>(
  58. (
  59. {
  60. // Do not forward `required` to avoid default browser behavior
  61. required: _required,
  62. // Do not forward `size` since it's used for custom styling, not as the
  63. // native `size` attribute (for that, use `nativeSize` instead)
  64. size: _size,
  65. // Use `nativeSize` as the native `size` attribute
  66. nativeSize,
  67. ...props
  68. },
  69. ref
  70. ) => <input {...props} ref={ref} size={nativeSize} />
  71. ),
  72. {shouldForwardProp: prop => typeof prop === 'string' && isPropValid(prop)}
  73. )`
  74. ${inputStyles}
  75. `;
  76. export default Input;