pill.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import {memo} from 'react';
  2. import styled from '@emotion/styled';
  3. import space from 'sentry/styles/space';
  4. import {Theme} from 'sentry/utils/theme';
  5. type PillType = 'positive' | 'negative' | 'error';
  6. type Props = {
  7. children?: React.ReactNode;
  8. className?: string;
  9. name?: React.ReactNode;
  10. type?: PillType;
  11. value?: number | string | boolean | null;
  12. };
  13. const Pill = memo(({name, value, children, type, className}: Props) => {
  14. const getTypeAndValue = (): Partial<{renderValue: string; valueType: PillType}> => {
  15. if (value === undefined) {
  16. return {};
  17. }
  18. switch (value) {
  19. case 'true':
  20. case true:
  21. return {
  22. valueType: 'positive',
  23. renderValue: 'true',
  24. };
  25. case 'false':
  26. case false:
  27. return {
  28. valueType: 'negative',
  29. renderValue: 'false',
  30. };
  31. case null:
  32. case undefined:
  33. return {
  34. valueType: 'error',
  35. renderValue: 'n/a',
  36. };
  37. default:
  38. return {
  39. valueType: undefined,
  40. renderValue: String(value),
  41. };
  42. }
  43. };
  44. const {valueType, renderValue} = getTypeAndValue();
  45. return (
  46. <StyledPill type={type ?? valueType} className={className}>
  47. <PillName>{name}</PillName>
  48. <PillValue>{children ?? renderValue}</PillValue>
  49. </StyledPill>
  50. );
  51. });
  52. const getPillStyle = ({type, theme}: {theme: Theme; type?: PillType}) => {
  53. switch (type) {
  54. case 'error':
  55. return `
  56. background: ${theme.red100};
  57. border: 1px solid ${theme.red300};
  58. `;
  59. default:
  60. return `
  61. border: 1px solid ${theme.border};
  62. `;
  63. }
  64. };
  65. const getPillValueStyle = ({type, theme}: {theme: Theme; type?: PillType}) => {
  66. switch (type) {
  67. case 'positive':
  68. return `
  69. background: ${theme.green100};
  70. border: 1px solid ${theme.green300};
  71. border-left-color: ${theme.green300};
  72. font-family: ${theme.text.familyMono};
  73. margin: -1px;
  74. `;
  75. case 'error':
  76. return `
  77. border-left-color: ${theme.red300};
  78. background: ${theme.red100};
  79. border: 1px solid ${theme.red300};
  80. margin: -1px;
  81. `;
  82. case 'negative':
  83. return `
  84. border-left-color: ${theme.red300};
  85. background: ${theme.red100};
  86. border: 1px solid ${theme.red300};
  87. font-family: ${theme.text.familyMono};
  88. margin: -1px;
  89. `;
  90. default:
  91. return `
  92. background: ${theme.backgroundSecondary};
  93. font-family: ${theme.text.familyMono};
  94. `;
  95. }
  96. };
  97. const PillName = styled('span')`
  98. padding: ${space(0.5)} ${space(1)};
  99. min-width: 0;
  100. white-space: nowrap;
  101. display: flex;
  102. align-items: center;
  103. `;
  104. const PillValue = styled(PillName)`
  105. border-left: 1px solid ${p => p.theme.border};
  106. border-radius: ${p =>
  107. `0 ${p.theme.button.borderRadius} ${p.theme.button.borderRadius} 0`};
  108. max-width: 100%;
  109. display: flex;
  110. align-items: center;
  111. > a {
  112. max-width: 100%;
  113. text-overflow: ellipsis;
  114. overflow: hidden;
  115. white-space: nowrap;
  116. display: inline-block;
  117. vertical-align: text-bottom;
  118. }
  119. .pill-icon,
  120. .external-icon {
  121. display: inline;
  122. margin: 0 0 0 ${space(1)};
  123. color: ${p => p.theme.gray300};
  124. &:hover {
  125. color: ${p => p.theme.textColor};
  126. }
  127. }
  128. `;
  129. const StyledPill = styled('li')<{type?: PillType}>`
  130. white-space: nowrap;
  131. margin: 0 ${space(1)} ${space(1)} 0;
  132. display: flex;
  133. border-radius: ${p => p.theme.button.borderRadius};
  134. box-shadow: ${p => p.theme.dropShadowLightest};
  135. line-height: 1.2;
  136. max-width: 100%;
  137. :last-child {
  138. margin-right: 0;
  139. }
  140. ${getPillStyle};
  141. ${PillValue} {
  142. ${getPillValueStyle};
  143. }
  144. `;
  145. export default Pill;