profileDragDropImport.tsx 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import {useCallback, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import {t} from 'sentry/locale';
  4. import {
  5. importDroppedProfile,
  6. ProfileGroup,
  7. } from 'sentry/utils/profiling/profile/importProfile';
  8. export interface ProfileDragDropImportProps {
  9. children: React.ReactNode;
  10. onImport: (profile: ProfileGroup) => void;
  11. }
  12. function ProfileDragDropImport({
  13. onImport,
  14. children,
  15. }: ProfileDragDropImportProps): React.ReactElement {
  16. const [dropState, setDropState] = useState<'idle' | 'dragover' | 'processing'>('idle');
  17. const [errorMessage, setErrorMessage] = useState<string | null>(null);
  18. const onDrop = useCallback(
  19. (evt: React.DragEvent<HTMLDivElement>) => {
  20. evt.preventDefault();
  21. evt.stopPropagation();
  22. const file = evt.dataTransfer.items[0].getAsFile();
  23. if (file) {
  24. setDropState('processing');
  25. importDroppedProfile(file)
  26. .then(profile => {
  27. setDropState('idle');
  28. setErrorMessage(null);
  29. onImport(profile);
  30. })
  31. .catch(e => {
  32. setDropState('idle');
  33. setErrorMessage(e.message);
  34. });
  35. }
  36. },
  37. [onImport]
  38. );
  39. const onDragEnter = useCallback((evt: React.DragEvent<HTMLDivElement>) => {
  40. evt.preventDefault();
  41. evt.stopPropagation();
  42. setDropState('dragover');
  43. }, []);
  44. const onDragLeave = useCallback((evt: React.DragEvent<HTMLDivElement>) => {
  45. evt.preventDefault();
  46. evt.stopPropagation();
  47. setDropState('idle');
  48. }, []);
  49. // This is required to indicate that onDrop is supported on this element
  50. const onDragOver = useCallback((evt: React.DragEvent<HTMLDivElement>) => {
  51. evt.preventDefault();
  52. }, []);
  53. return (
  54. <DragDropContainer onDragEnter={onDragEnter}>
  55. {dropState === 'idle' ? null : (
  56. <Overlay onDrop={onDrop} onDragOver={onDragOver} onDragLeave={onDragLeave}>
  57. {t('Drop here')}
  58. <p>{errorMessage}</p>
  59. </Overlay>
  60. )}
  61. {children}
  62. </DragDropContainer>
  63. );
  64. }
  65. const DragDropContainer = styled('div')`
  66. display: flex;
  67. flex-direction: column;
  68. flex: 1 1 100%;
  69. `;
  70. const Overlay = styled('div')`
  71. position: absolute;
  72. left: 0;
  73. top: 0;
  74. width: 100%;
  75. height: 100%;
  76. display: grid;
  77. grid: auto/50%;
  78. place-content: center;
  79. z-index: ${p => p.theme.zIndex.modal};
  80. text-align: center;
  81. `;
  82. export {ProfileDragDropImport};