profilingContextMenu.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import {forwardRef} from 'react';
  2. import styled from '@emotion/styled';
  3. import {IconCheckmark} from 'sentry/icons';
  4. import space from 'sentry/styles/space';
  5. interface MenuProps
  6. extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
  7. children: React.ReactNode;
  8. }
  9. const Menu = styled(
  10. forwardRef((props: MenuProps, ref: React.Ref<HTMLDivElement> | undefined) => {
  11. return <div ref={ref} role="menu" {...props} />;
  12. })
  13. )`
  14. position: absolute;
  15. font-size: ${p => p.theme.fontSizeMedium};
  16. z-index: ${p => p.theme.zIndex.dropdown};
  17. background: ${p => p.theme.backgroundElevated};
  18. border: 1px solid ${p => p.theme.border};
  19. border-radius: ${p => p.theme.borderRadius};
  20. box-shadow: ${p => p.theme.dropShadowHeavy};
  21. width: auto;
  22. min-width: 164px;
  23. overflow: auto;
  24. padding-bottom: ${space(0.5)};
  25. `;
  26. export {Menu as ProfilingContextMenu};
  27. const MenuContentContainer = styled('div')`
  28. cursor: pointer;
  29. display: flex;
  30. align-items: center;
  31. font-weight: normal;
  32. padding: 0 ${space(1)};
  33. border-radius: ${p => p.theme.borderRadius};
  34. box-sizing: border-box;
  35. background: ${p => (p.tabIndex === 0 ? p.theme.hover : undefined)};
  36. &:focus {
  37. color: ${p => p.theme.textColor};
  38. background: ${p => p.theme.hover};
  39. outline: none;
  40. }
  41. `;
  42. const MenuItemCheckboxLabel = styled('label')`
  43. display: flex;
  44. align-items: center;
  45. font-weight: normal;
  46. margin: 0;
  47. cursor: pointer;
  48. flex: 1 1 100%;
  49. `;
  50. interface MenuItemCheckboxProps
  51. extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
  52. checked?: boolean;
  53. }
  54. const MenuItemCheckbox = forwardRef(
  55. (props: MenuItemCheckboxProps, ref: React.Ref<HTMLDivElement> | undefined) => {
  56. const {children, checked, ...rest} = props;
  57. return (
  58. <MenuContentOuterContainer>
  59. <MenuContentContainer ref={ref} role="menuitem" {...rest}>
  60. <MenuItemCheckboxLabel>
  61. <MenuLeadingItem>
  62. <Input type="checkbox" checked={checked} onChange={() => void 0} />
  63. <IconCheckmark />
  64. </MenuLeadingItem>
  65. <MenuContent>{children}</MenuContent>
  66. </MenuItemCheckboxLabel>
  67. </MenuContentContainer>
  68. </MenuContentOuterContainer>
  69. );
  70. }
  71. );
  72. export {MenuItemCheckbox as ProfilingContextMenuItemCheckbox};
  73. interface MenuItemButtonProps
  74. extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {}
  75. const MenuItemButton = forwardRef(
  76. (props: MenuItemButtonProps, ref: React.Ref<HTMLDivElement> | undefined) => {
  77. const {children, ...rest} = props;
  78. return (
  79. <MenuContentOuterContainer>
  80. <MenuContentContainer ref={ref} role="menuitem" {...rest}>
  81. <MenuButton>{children}</MenuButton>
  82. </MenuContentContainer>
  83. </MenuContentOuterContainer>
  84. );
  85. }
  86. );
  87. export {MenuItemButton as ProfilingContextMenuItemButton};
  88. const MenuButton = styled('button')`
  89. background: transparent;
  90. border: none;
  91. `;
  92. const MenuLeadingItem = styled('div')`
  93. display: flex;
  94. align-items: center;
  95. height: 1.4em;
  96. width: 1em;
  97. gap: ${space(1)};
  98. padding: ${space(1)} 0;
  99. position: relative;
  100. `;
  101. const MenuContent = styled('div')`
  102. position: relative;
  103. width: 100%;
  104. display: flex;
  105. gap: ${space(0.5)};
  106. justify-content: space-between;
  107. padding: ${space(0.5)} 0;
  108. margin-left: ${space(0.5)};
  109. text-transform: capitalize;
  110. margin-bottom: 0;
  111. line-height: 1.4;
  112. white-space: nowrap;
  113. overflow: hidden;
  114. text-overflow: ellipsis;
  115. `;
  116. const Input = styled('input')`
  117. position: absolute;
  118. opacity: 0;
  119. cursor: pointer;
  120. height: 0;
  121. padding-right: ${space(1)};
  122. & + svg {
  123. position: absolute;
  124. left: 50%;
  125. top: 50%;
  126. transform: translate(-50%, -50%);
  127. width: 1em;
  128. height: 1.4em;
  129. display: none;
  130. }
  131. &:checked + svg {
  132. display: block;
  133. }
  134. `;
  135. interface MenuItemProps
  136. extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
  137. children: React.ReactNode;
  138. }
  139. const MenuItem = styled(
  140. forwardRef((props: MenuItemProps, ref: React.Ref<HTMLDivElement> | undefined) => {
  141. const {children, ...rest} = props;
  142. return (
  143. <MenuContentOuterContainer>
  144. <MenuContentContainer ref={ref} role="menuitem" {...rest}>
  145. <MenuContent>{children}</MenuContent>
  146. </MenuContentContainer>
  147. </MenuContentOuterContainer>
  148. );
  149. })
  150. )`
  151. cursor: pointer;
  152. color: ${p => p.theme.textColor};
  153. background: transparent;
  154. padding: 0 ${space(0.5)};
  155. &:focus {
  156. outline: none;
  157. }
  158. &:active: {
  159. background: transparent;
  160. }
  161. `;
  162. export {MenuItem as ProfilingContextMenuItem};
  163. const MenuContentOuterContainer = styled('div')`
  164. padding: 0 ${space(0.5)};
  165. `;
  166. const MenuGroup = styled('div')`
  167. padding-top: 0;
  168. padding-bottom: ${space(1)};
  169. &:last-of-type {
  170. padding-bottom: 0;
  171. }
  172. `;
  173. export {MenuGroup as ProfilingContextMenuGroup};
  174. interface MenuHeadingProps
  175. extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
  176. children: React.ReactNode;
  177. }
  178. const MenuHeading = styled((props: MenuHeadingProps) => {
  179. const {children, ...rest} = props;
  180. return <div {...rest}>{children}</div>;
  181. })`
  182. text-transform: uppercase;
  183. line-height: 1.5;
  184. font-weight: 600;
  185. color: ${p => p.theme.subText};
  186. margin-bottom: 0;
  187. cursor: default;
  188. font-size: 75%;
  189. padding: ${space(0.5)} ${space(1.5)};
  190. `;
  191. export {MenuHeading as ProfilingContextMenuHeading};
  192. const Layer = styled('div')`
  193. width: 100%;
  194. height: 100%;
  195. position: absolute;
  196. left: 0;
  197. top: 0;
  198. z-index: ${p => p.theme.zIndex.dropdown - 1};
  199. `;
  200. export {Layer as ProfilingContextMenuLayer};