File size: 3,829 Bytes
8a37e0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { Box, Flex, IconButton, Tooltip, useShiftModifier } from '@invoke-ai/ui-library';
import { getOverlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
import { Formatter } from 'fracturedjsonjs';
import { isString } from 'lodash-es';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import type { CSSProperties } from 'react';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiCopyBold, PiDownloadSimpleBold } from 'react-icons/pi';

const formatter = new Formatter();

type Props = {
  label: string;
  data: unknown;
  fileName?: string;
  withDownload?: boolean;
  withCopy?: boolean;
  extraCopyActions?: { label: string; getData: (data: unknown) => unknown }[];
};

const overlayscrollbarsOptions = getOverlayScrollbarsParams('scroll', 'scroll').options;

const DataViewer = (props: Props) => {
  const { label, data, fileName, withDownload = true, withCopy = true, extraCopyActions } = props;
  const dataString = useMemo(() => (isString(data) ? data : formatter.Serialize(data)) ?? '', [data]);
  const shift = useShiftModifier();
  const handleCopy = useCallback(() => {
    navigator.clipboard.writeText(dataString);
  }, [dataString]);

  const handleDownload = useCallback(() => {
    const blob = new Blob([dataString]);
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = `${fileName || label}.json`;
    document.body.appendChild(a);
    a.click();
    a.remove();
  }, [dataString, label, fileName]);

  const { t } = useTranslation();

  return (
    <Flex layerStyle="second" borderRadius="base" flexGrow={1} w="full" h="full" position="relative">
      <Box position="absolute" top={0} left={0} right={0} bottom={0} overflow="auto" p={4} fontSize="sm">
        <OverlayScrollbarsComponent defer style={overlayScrollbarsStyles} options={overlayscrollbarsOptions}>
          <pre>{dataString}</pre>
        </OverlayScrollbarsComponent>
      </Box>
      <Flex position="absolute" top={0} insetInlineEnd={0} p={2}>
        {withDownload && (
          <Tooltip label={`${t('gallery.download')} ${label} JSON`}>
            <IconButton
              aria-label={`${t('gallery.download')} ${label} JSON`}
              icon={<PiDownloadSimpleBold size={16} />}
              variant="ghost"
              opacity={0.7}
              onClick={handleDownload}
            />
          </Tooltip>
        )}
        {withCopy && (
          <Tooltip label={`${t('gallery.copy')} ${label} JSON`}>
            <IconButton
              aria-label={`${t('gallery.copy')} ${label} JSON`}
              icon={<PiCopyBold size={16} />}
              variant="ghost"
              opacity={0.7}
              onClick={handleCopy}
            />
          </Tooltip>
        )}
        {shift &&
          extraCopyActions?.map(({ label, getData }) => (
            <ExtraCopyAction label={label} getData={getData} data={data} key={label} />
          ))}
      </Flex>
    </Flex>
  );
};

export default memo(DataViewer);

const overlayScrollbarsStyles: CSSProperties = {
  height: '100%',
  width: '100%',
};

type ExtraCopyActionProps = {
  label: string;
  data: unknown;
  getData: (data: unknown) => unknown;
};
const ExtraCopyAction = ({ label, data, getData }: ExtraCopyActionProps) => {
  const { t } = useTranslation();
  const handleCopy = useCallback(() => {
    navigator.clipboard.writeText(JSON.stringify(getData(data), null, 2));
  }, [data, getData]);

  return (
    <Tooltip label={`${t('gallery.copy')} ${label} JSON`}>
      <IconButton
        aria-label={`${t('gallery.copy')} ${label} JSON`}
        icon={<PiCopyBold size={16} />}
        variant="ghost"
        opacity={0.7}
        onClick={handleCopy}
      />
    </Tooltip>
  );
};