selectOption.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import {components as selectComponents} from 'react-select';
  2. import styled from '@emotion/styled';
  3. import {IconCheckmark} from 'sentry/icons';
  4. import space from 'sentry/styles/space';
  5. import {defined} from 'sentry/utils';
  6. type Props = React.ComponentProps<typeof selectComponents.Option>;
  7. function SelectOption(props: Props) {
  8. const {label, data, selectProps, isMulti, isSelected, isFocused} = props;
  9. const {showDividers, verticallyCenterCheckWrap} = selectProps;
  10. const {
  11. details,
  12. leadingItems,
  13. trailingItems,
  14. leadingItemsSpanFullHeight,
  15. trailingItemsSpanFullHeight,
  16. } = data;
  17. return (
  18. <selectComponents.Option {...props} className="select-option">
  19. <InnerWrap isFocused={isFocused}>
  20. <Indent isMulti={isMulti} centerCheckWrap={verticallyCenterCheckWrap}>
  21. <CheckWrap isMulti={isMulti} isSelected={isSelected}>
  22. {isSelected && (
  23. <IconCheckmark
  24. size={isMulti ? 'xs' : 'sm'}
  25. color={isMulti ? 'white' : undefined}
  26. />
  27. )}
  28. </CheckWrap>
  29. {leadingItems && (
  30. <LeadingItems spanFullHeight={leadingItemsSpanFullHeight}>
  31. {leadingItems}
  32. </LeadingItems>
  33. )}
  34. </Indent>
  35. <ContentWrap
  36. isFocused={isFocused}
  37. showDividers={showDividers}
  38. addRightMargin={!defined(trailingItems)}
  39. >
  40. <div>
  41. <Label as={typeof label === 'string' ? 'p' : 'div'}>{label}</Label>
  42. {details && <Details>{details}</Details>}
  43. </div>
  44. {trailingItems && (
  45. <TrailingItems spanFullHeight={trailingItemsSpanFullHeight}>
  46. {trailingItems}
  47. </TrailingItems>
  48. )}
  49. </ContentWrap>
  50. </InnerWrap>
  51. </selectComponents.Option>
  52. );
  53. }
  54. export default SelectOption;
  55. const InnerWrap = styled('div')<{isFocused: boolean}>`
  56. display: flex;
  57. padding: 0 ${space(1)};
  58. border-radius: ${p => p.theme.borderRadius};
  59. box-sizing: border-box;
  60. ${p => p.isFocused && `background: ${p.theme.hover};`}
  61. `;
  62. const Indent = styled('div')<{centerCheckWrap?: boolean; isMulti?: boolean}>`
  63. display: flex;
  64. justify-content: center;
  65. gap: ${space(1)};
  66. padding: ${space(1)};
  67. padding-left: ${space(0.5)};
  68. ${p => p.isMulti && !p.centerCheckWrap && `margin-top: 0.2em;`}
  69. ${p => p.centerCheckWrap && 'align-items: center;'}
  70. `;
  71. const CheckWrap = styled('div')<{isMulti: boolean; isSelected: boolean}>`
  72. display: flex;
  73. justify-content: center;
  74. align-items: center;
  75. ${p =>
  76. p.isMulti
  77. ? `
  78. width: 1em;
  79. height: 1em;
  80. padding: 1px;
  81. border: solid 1px ${p.theme.border};
  82. background: ${p.theme.backgroundElevated};
  83. border-radius: 2px;
  84. box-shadow: inset ${p.theme.dropShadowLight};
  85. ${
  86. p.isSelected &&
  87. `
  88. background: ${p.theme.purple300};
  89. border-color: ${p.theme.purple300};
  90. `
  91. }
  92. `
  93. : `
  94. width: 1em;
  95. height: 1.4em;
  96. padding-bottom: 1px;
  97. `}
  98. `;
  99. const ContentWrap = styled('div')<{
  100. addRightMargin: boolean;
  101. isFocused: boolean;
  102. showDividers?: boolean;
  103. }>`
  104. position: relative;
  105. width: 100%;
  106. display: flex;
  107. gap: ${space(1)};
  108. justify-content: space-between;
  109. padding: ${space(1)} 0;
  110. ${p => p.addRightMargin && `margin-right: ${space(1)};`}
  111. ${p =>
  112. p.showDividers &&
  113. !p.isFocused &&
  114. `
  115. z-index: -1;
  116. box-shadow: 0 1px 0 0 ${p.theme.innerBorder};
  117. .select-option:last-of-type & {
  118. box-shadow: none;
  119. }
  120. `}
  121. `;
  122. const LeadingItems = styled('div')<{spanFullHeight?: boolean}>`
  123. display: flex;
  124. align-items: center;
  125. height: ${p => (p.spanFullHeight ? '100%' : '1.4em')};
  126. gap: ${space(1)};
  127. `;
  128. const Label = styled('p')`
  129. margin-bottom: 0;
  130. line-height: 1.4;
  131. white-space: nowrap;
  132. `;
  133. const Details = styled('p')`
  134. font-size: 14px;
  135. line-height: 1.2;
  136. color: ${p => p.theme.subText};
  137. margin-bottom: 0;
  138. `;
  139. const TrailingItems = styled('div')<{spanFullHeight?: boolean}>`
  140. display: flex;
  141. align-items: center;
  142. height: ${p => (p.spanFullHeight ? '100%' : '1.4em')};
  143. gap: ${space(1)};
  144. margin-right: ${space(0.5)};
  145. `;