row.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import styled from '@emotion/styled';
  2. import AutoComplete from 'sentry/components/autoComplete';
  3. import space from 'sentry/styles/space';
  4. import {Item} from './types';
  5. type ItemSize = 'zero' | 'small';
  6. type AutoCompleteChildrenArgs<T> = Parameters<AutoComplete<T>['props']['children']>[0];
  7. type Props<T> = Pick<
  8. AutoCompleteChildrenArgs<T>,
  9. 'highlightedIndex' | 'getItemProps' | 'inputValue'
  10. > &
  11. Omit<Parameters<AutoCompleteChildrenArgs<T>['getItemProps']>[0], 'index'> & {
  12. /**
  13. * Size for dropdown items
  14. */
  15. itemSize?: ItemSize;
  16. };
  17. function Row<T extends Item>({
  18. item,
  19. style,
  20. itemSize,
  21. highlightedIndex,
  22. inputValue,
  23. getItemProps,
  24. }: Props<T>) {
  25. const {index} = item;
  26. if (item.groupLabel) {
  27. return (
  28. <LabelWithBorder style={style}>
  29. {item.label && <GroupLabel>{item.label}</GroupLabel>}
  30. </LabelWithBorder>
  31. );
  32. }
  33. return (
  34. <AutoCompleteItem
  35. itemSize={itemSize}
  36. disabled={item.disabled}
  37. isHighlighted={index === highlightedIndex}
  38. {...getItemProps({item, index, style})}
  39. >
  40. {typeof item.label === 'function' ? item.label({inputValue}) : item.label}
  41. </AutoCompleteItem>
  42. );
  43. }
  44. export default Row;
  45. const getItemPaddingForSize = (itemSize?: ItemSize) => {
  46. if (itemSize === 'small') {
  47. return `${space(0.5)} ${space(1)}`;
  48. }
  49. if (itemSize === 'zero') {
  50. return '0';
  51. }
  52. return space(1);
  53. };
  54. const LabelWithBorder = styled('div')`
  55. background-color: ${p => p.theme.backgroundSecondary};
  56. border-bottom: 1px solid ${p => p.theme.innerBorder};
  57. border-width: 1px 0;
  58. color: ${p => p.theme.subText};
  59. font-size: ${p => p.theme.fontSizeMedium};
  60. :first-child {
  61. border-top: none;
  62. }
  63. :last-child {
  64. border-bottom: none;
  65. }
  66. `;
  67. const GroupLabel = styled('div')`
  68. padding: ${space(0.25)} ${space(1)};
  69. `;
  70. const AutoCompleteItem = styled('div')<{
  71. isHighlighted: boolean;
  72. disabled?: boolean;
  73. itemSize?: ItemSize;
  74. }>`
  75. /* needed for virtualized lists that do not fill parent height */
  76. /* e.g. breadcrumbs (org height > project, but want same fixed height for both) */
  77. display: flex;
  78. flex-direction: column;
  79. justify-content: center;
  80. font-size: 0.9em;
  81. background-color: ${p => (p.isHighlighted ? p.theme.focus : 'transparent')};
  82. color: ${p => (p.isHighlighted ? p.theme.textColor : 'inherit')};
  83. padding: ${p => getItemPaddingForSize(p.itemSize)};
  84. cursor: ${p => (p.disabled ? 'not-allowed' : 'pointer')};
  85. border-bottom: 1px solid ${p => p.theme.innerBorder};
  86. :last-child {
  87. border-bottom: none;
  88. }
  89. :hover {
  90. color: ${p => p.theme.textColor};
  91. background-color: ${p => p.theme.focus};
  92. }
  93. `;