Commit 7dcb88ee authored by Yuri Mikhin's avatar Yuri Mikhin Committed by Yuri Mikhin

Add "Save as CSV" option to charts.

parent c8e95452
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 3c0-.184.15-.333.333-.333h4.334v2c0 .368.298.666.666.666h2v1.334h1.334v-.92c0-.441-.176-.865-.489-1.178L9.431 1.822a1.667 1.667 0 0 0-1.179-.489H4.333c-.92 0-1.666.747-1.666 1.667v3.667H4V3Zm0 10v-1H2.667v1c0 .92.746 1.667 1.666 1.667H11c.92 0 1.667-.746 1.667-1.667v-1h-1.334v1a.333.333 0 0 1-.333.333H4.333A.333.333 0 0 1 4 13Zm1.167-5.833H4.75c-.575 0-1.042.466-1.042 1.041v2.084c0 .575.466 1.041 1.042 1.041h.417a1.04 1.04 0 0 0 1.041-1.041v-.209a.418.418 0 0 0-.416-.416.418.418 0 0 0-.417.416v.209a.209.209 0 0 1-.208.208H4.75a.209.209 0 0 1-.208-.208V8.208c0-.114.093-.208.208-.208h.417a.21.21 0 0 1 .208.208v.209a.417.417 0 0 0 .833 0v-.209a1.04 1.04 0 0 0-1.041-1.041Zm2.666 0a1.21 1.21 0 0 0-.599 2.258l.662.377a.375.375 0 0 1-.188.7h-.666a.418.418 0 0 0-.417.417c0 .23.188.417.417.417h.666a1.21 1.21 0 0 0 .6-2.258l-.662-.377a.375.375 0 0 1 .188-.7h.458a.418.418 0 0 0 0-.833h-.459Zm2.125.416v.823c0 .6.144 1.188.417 1.719a3.737 3.737 0 0 0 .417-1.719v-.823a.417.417 0 0 1 .833 0v.823a4.58 4.58 0 0 1-.77 2.542l-.134.2a.415.415 0 0 1-.692 0l-.133-.2a4.58 4.58 0 0 1-.771-2.542v-.823a.417.417 0 0 1 .833 0Z" fill="currentColor"/>
</svg>
import { unparse } from 'papaparse';
export default function saveAsCSV(headerRows: Array<string>, dataRows: Array<Array<string>>, filename: string) {
const csv = unparse([
headerRows,
...dataRows,
]);
const blob = new Blob([ csv ], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.setAttribute('href', url);
link.setAttribute('download', filename);
link.click();
link.remove();
URL.revokeObjectURL(url);
}
...@@ -5,9 +5,12 @@ import React, { useRef, useCallback, useState } from 'react'; ...@@ -5,9 +5,12 @@ import React, { useRef, useCallback, useState } from 'react';
import type { TimeChartItem } from './types'; import type { TimeChartItem } from './types';
import imageIcon from 'icons/image.svg'; import imageIcon from 'icons/image.svg';
import repeatArrow from 'icons/repeat_arrow.svg'; import repeatArrowIcon from 'icons/repeat_arrow.svg';
import scopeIcon from 'icons/scope.svg'; import scopeIcon from 'icons/scope.svg';
import svgFileIcon from 'icons/svg_file.svg';
import dotsIcon from 'icons/vertical_dots.svg'; import dotsIcon from 'icons/vertical_dots.svg';
import dayjs from 'lib/date/dayjs';
import saveAsCSV from 'lib/saveAsCSV';
import ChartWidgetGraph from './ChartWidgetGraph'; import ChartWidgetGraph from './ChartWidgetGraph';
import ChartWidgetSkeleton from './ChartWidgetSkeleton'; import ChartWidgetSkeleton from './ChartWidgetSkeleton';
...@@ -78,6 +81,19 @@ const ChartWidget = ({ items, title, description, isLoading, chartHeight }: Prop ...@@ -78,6 +81,19 @@ const ChartWidget = ({ items, title, description, isLoading, chartHeight }: Prop
} }
}, [ title ]); }, [ title ]);
const handleSVGSavingClick = useCallback(() => {
if (items) {
const headerRows = [
'Date', 'Value',
];
const dataRows = items.map((item) => [
dayjs(item.date).format('YYYY-MM-DD'), String(item.value),
]);
saveAsCSV(headerRows, dataRows, `${ title } (Blockscout stats)`);
}
}, [ items, title ]);
if (isLoading) { if (isLoading) {
return <ChartWidgetSkeleton hasDescription={ Boolean(description) } chartHeight={ chartHeight }/>; return <ChartWidgetSkeleton hasDescription={ Boolean(description) } chartHeight={ chartHeight }/>;
} }
...@@ -132,7 +148,7 @@ const ChartWidget = ({ items, title, description, isLoading, chartHeight }: Prop ...@@ -132,7 +148,7 @@ const ChartWidget = ({ items, title, description, isLoading, chartHeight }: Prop
size="sm" size="sm"
variant="outline" variant="outline"
onClick={ handleZoomResetClick } onClick={ handleZoomResetClick }
icon={ <Icon as={ repeatArrow } w={ 4 } h={ 4 }/> } icon={ <Icon as={ repeatArrowIcon } w={ 4 } h={ 4 }/> }
/> />
</Tooltip> </Tooltip>
...@@ -161,6 +177,7 @@ const ChartWidget = ({ items, title, description, isLoading, chartHeight }: Prop ...@@ -161,6 +177,7 @@ const ChartWidget = ({ items, title, description, isLoading, chartHeight }: Prop
<Icon as={ scopeIcon } boxSize={ 4 } mr={ 3 }/> <Icon as={ scopeIcon } boxSize={ 4 } mr={ 3 }/>
View fullscreen View fullscreen
</MenuItem> </MenuItem>
<MenuItem <MenuItem
display="flex" display="flex"
alignItems="center" alignItems="center"
...@@ -169,6 +186,15 @@ const ChartWidget = ({ items, title, description, isLoading, chartHeight }: Prop ...@@ -169,6 +186,15 @@ const ChartWidget = ({ items, title, description, isLoading, chartHeight }: Prop
<Icon as={ imageIcon } boxSize={ 4 } mr={ 3 }/> <Icon as={ imageIcon } boxSize={ 4 } mr={ 3 }/>
Save as PNG Save as PNG
</MenuItem> </MenuItem>
<MenuItem
display="flex"
alignItems="center"
onClick={ handleSVGSavingClick }
>
<Icon as={ svgFileIcon } boxSize={ 4 } mr={ 3 }/>
Save as CSV
</MenuItem>
</MenuList> </MenuList>
</Menu> </Menu>
</Grid> </Grid>
......
...@@ -3588,6 +3588,13 @@ ...@@ -3588,6 +3588,13 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.36.tgz#c0d5f2fe76b47b63e0e0efc3d2049a9970d68794" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.36.tgz#c0d5f2fe76b47b63e0e0efc3d2049a9970d68794"
integrity sha512-V3orv+ggDsWVHP99K3JlwtH20R7J4IhI1Kksgc+64q5VxgfRkQG8Ws3MFm/FZOKDYGy9feGFlZ70/HpCNe9QaA== integrity sha512-V3orv+ggDsWVHP99K3JlwtH20R7J4IhI1Kksgc+64q5VxgfRkQG8Ws3MFm/FZOKDYGy9feGFlZ70/HpCNe9QaA==
"@types/papaparse@^5.3.5":
version "5.3.5"
resolved "https://registry.yarnpkg.com/@types/papaparse/-/papaparse-5.3.5.tgz#e5ad94b1fe98e2a8ea0b03284b83d2cb252bbf39"
integrity sha512-R1icl/hrJPFRpuYj9PVG03WBAlghJj4JW9Py5QdR8FFSxaLmZRyu7xYDCCBZIJNfUv3MYaeBbhBoX958mUTAaw==
dependencies:
"@types/node" "*"
"@types/parse-json@^4.0.0": "@types/parse-json@^4.0.0":
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
...@@ -7708,6 +7715,11 @@ p-try@^2.0.0: ...@@ -7708,6 +7715,11 @@ p-try@^2.0.0:
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
papaparse@^5.3.2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.3.2.tgz#d1abed498a0ee299f103130a6109720404fbd467"
integrity sha512-6dNZu0Ki+gyV0eBsFKJhYr+MdQYAzFUGlBMNj3GNrmHxmz1lfRa24CjFObPXtjcetlOv5Ad299MhIK0znp3afw==
parent-module@^1.0.0: parent-module@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment