detailPanel.tsx 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import {useEffect, useRef, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import {Button} from 'sentry/components/button';
  4. import {IconClose} from 'sentry/icons/iconClose';
  5. import {t} from 'sentry/locale';
  6. import {space} from 'sentry/styles/space';
  7. import useKeyPress from 'sentry/utils/useKeyPress';
  8. import useOnClickOutside from 'sentry/utils/useOnClickOutside';
  9. import SlideOverPanel from 'sentry/views/starfish/components/slideOverPanel';
  10. type DetailProps = {
  11. children: React.ReactNode;
  12. detailKey?: string;
  13. onClose?: () => void;
  14. };
  15. type DetailState = {
  16. collapsed: boolean;
  17. };
  18. export default function Detail({children, detailKey, onClose}: DetailProps) {
  19. const [state, setState] = useState<DetailState>({collapsed: true});
  20. const escapeKeyPressed = useKeyPress('Escape');
  21. // Any time the key prop changes (due to user interaction), we want to open the panel
  22. useEffect(() => {
  23. if (detailKey) {
  24. setState({collapsed: false});
  25. } else {
  26. setState({collapsed: true});
  27. }
  28. }, [detailKey]);
  29. const panelRef = useRef<HTMLDivElement>(null);
  30. useOnClickOutside(panelRef, () => {
  31. if (!state.collapsed) {
  32. onClose?.();
  33. setState({collapsed: true});
  34. }
  35. });
  36. useEffect(() => {
  37. if (escapeKeyPressed) {
  38. if (!state.collapsed) {
  39. onClose?.();
  40. setState({collapsed: true});
  41. }
  42. }
  43. // eslint-disable-next-line react-hooks/exhaustive-deps
  44. }, [escapeKeyPressed]);
  45. return (
  46. <SlideOverPanel collapsed={state.collapsed} ref={panelRef}>
  47. <CloseButtonWrapper>
  48. <CloseButton
  49. priority="link"
  50. size="zero"
  51. borderless
  52. aria-label={t('Close Details')}
  53. icon={<IconClose size="sm" />}
  54. onClick={() => {
  55. setState({collapsed: true});
  56. onClose?.();
  57. }}
  58. />
  59. </CloseButtonWrapper>
  60. <DetailWrapper>{children}</DetailWrapper>
  61. </SlideOverPanel>
  62. );
  63. }
  64. const CloseButton = styled(Button)`
  65. color: ${p => p.theme.gray300};
  66. &:hover {
  67. color: ${p => p.theme.gray400};
  68. }
  69. `;
  70. const CloseButtonWrapper = styled('div')`
  71. justify-content: flex-end;
  72. display: flex;
  73. padding: ${space(2)};
  74. `;
  75. const DetailWrapper = styled('div')`
  76. padding: 0 ${space(4)};
  77. `;