input.tsx 2.6 KB

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