pill.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import {memo} from 'react';
  2. import {Theme} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import {space} from 'sentry/styles/space';
  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 => `0 ${p.theme.borderRadius} ${p.theme.borderRadius} 0`};
  107. max-width: 100%;
  108. display: flex;
  109. align-items: center;
  110. > a {
  111. max-width: 100%;
  112. text-overflow: ellipsis;
  113. overflow: hidden;
  114. white-space: nowrap;
  115. display: inline-block;
  116. vertical-align: text-bottom;
  117. }
  118. .pill-icon,
  119. .external-icon {
  120. display: inline;
  121. margin: 0 0 0 ${space(1)};
  122. color: ${p => p.theme.gray300};
  123. &:hover {
  124. color: ${p => p.theme.textColor};
  125. }
  126. }
  127. `;
  128. const StyledPill = styled('li')<{type?: PillType}>`
  129. white-space: nowrap;
  130. margin: 0 ${space(1)} ${space(1)} 0;
  131. display: flex;
  132. border-radius: ${p => p.theme.borderRadius};
  133. box-shadow: ${p => p.theme.dropShadowLight};
  134. line-height: 1.2;
  135. max-width: 100%;
  136. :last-child {
  137. margin-right: 0;
  138. }
  139. ${getPillStyle};
  140. ${PillValue} {
  141. ${getPillValueStyle};
  142. }
  143. `;
  144. export default Pill;