dropdownBubble.tsx 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import {css, Theme} from '@emotion/react';
  2. import styled from '@emotion/styled';
  3. import space from 'sentry/styles/space';
  4. import SettingsHeader from 'sentry/views/settings/components/settingsHeader';
  5. type Params = {
  6. /**
  7. * Menu alignment
  8. */
  9. alignMenu: 'left' | 'right';
  10. /**
  11. * If this is true, will make a single corner blended with actor (depends on anchor orientation)
  12. */
  13. blendCorner: boolean;
  14. /**
  15. * If this is true, will make corners blend with its opener (so no border radius)
  16. */
  17. blendWithActor?: boolean;
  18. /**
  19. * If true, the menu will be visually detached from actor.
  20. */
  21. detached?: boolean;
  22. /**
  23. * The width of the menu
  24. */
  25. width?: string;
  26. };
  27. type ParamsWithTheme = Params & {theme: Theme};
  28. /**
  29. * If `blendCorner` is false, then we apply border-radius to all corners
  30. *
  31. * Otherwise apply radius to opposite side of `alignMenu` *unless it is fixed width*
  32. */
  33. const getMenuBorderRadius = ({
  34. blendWithActor,
  35. blendCorner,
  36. detached,
  37. alignMenu,
  38. width,
  39. theme,
  40. }: ParamsWithTheme) => {
  41. const radius = theme.borderRadius;
  42. if (!blendCorner || detached) {
  43. return css`
  44. border-radius: ${radius};
  45. `;
  46. }
  47. // If menu width is the same width as the control
  48. const isFullWidth = width === '100%';
  49. // No top border radius if widths match
  50. const hasTopLeftRadius = !blendWithActor && !isFullWidth && alignMenu !== 'left';
  51. const hasTopRightRadius = !blendWithActor && !isFullWidth && !hasTopLeftRadius;
  52. return css`
  53. border-radius: ${hasTopLeftRadius ? radius : 0} ${hasTopRightRadius ? radius : 0}
  54. ${radius} ${radius};
  55. `;
  56. };
  57. const DropdownBubble = styled('div')<Params>`
  58. background: ${p => p.theme.background};
  59. color: ${p => p.theme.textColor};
  60. border: 1px solid ${p => p.theme.border};
  61. position: absolute;
  62. right: 0;
  63. ${p => (p.width ? `width: ${p.width}` : '')};
  64. ${p => (p.alignMenu === 'left' ? 'left: 0;' : '')};
  65. ${p =>
  66. p.detached
  67. ? `
  68. top: 100%;
  69. margin-top: ${space(1)};
  70. box-shadow: ${p.theme.dropShadowHeavy};
  71. `
  72. : `
  73. top: calc(100% - 1px);
  74. box-shadow: ${p.theme.dropShadowLight};
  75. `};
  76. ${getMenuBorderRadius};
  77. /* This is needed to be able to cover e.g. pagination buttons, but also be
  78. * below dropdown actor button's zindex */
  79. z-index: ${p => p.theme.zIndex.dropdownAutocomplete.menu};
  80. ${SettingsHeader} & {
  81. z-index: ${p => p.theme.zIndex.dropdownAutocomplete.menu + 2};
  82. }
  83. `;
  84. export default DropdownBubble;