Commit 94f14326 authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into custom-select

parents 24398f23 d9453168
<svg viewBox="2 2 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.667 3.75c0-.688-.563-1.25-1.25-1.25-.688 0-1.25.563-1.25 1.25 0 .688.562 1.25 1.25 1.25.687 0 1.25-.563 1.25-1.25Zm0 12.5c0-.688-.563-1.25-1.25-1.25-.688 0-1.25.563-1.25 1.25 0 .688.562 1.25 1.25 1.25.687 0 1.25-.563 1.25-1.25Zm0-6.25c0-.688-.563-1.25-1.25-1.25-.688 0-1.25.563-1.25 1.25 0 .688.562 1.25 1.25 1.25.687 0 1.25-.563 1.25-1.25Z" fill="currentColor" fill-opacity=".8"/>
</svg>
......@@ -25,3 +25,8 @@ export type StatsChart = {
description: string;
apiMethodURL: string;
}
export interface ModalChart {
id: string;
title: string;
}
......@@ -18,6 +18,9 @@ const Stats = () => {
handleIntervalChange,
debounceFilterCharts,
displayedCharts,
showChartFullscreen,
clearFullscreenChart,
fullscreenChart,
} = useStats();
return (
......@@ -40,6 +43,9 @@ const Stats = () => {
<ChartsWidgetsList
charts={ displayedCharts }
onChartFullscreenClick={ showChartFullscreen }
fullscreenChart={ fullscreenChart }
onModalClose={ clearFullscreenChart }
/>
</Page>
);
......
import { Box, Button, Grid, Heading, Text, useColorModeValue } from '@chakra-ui/react';
import { Box, Button, Grid, Heading, Icon, IconButton, Menu, MenuButton, MenuItem, MenuList, Text, useColorModeValue, VisuallyHidden } from '@chakra-ui/react';
import React, { useCallback } from 'react';
import type { ModalChart } from 'types/client/stats';
import dotsIcon from 'icons/vertical-dots.svg';
import ChartWidgetGraph from './ChartWidgetGraph';
import { demoChartsData } from './constants/demo-charts-data';
type Props = {
id: string;
onFullscreenClick: (chart: ModalChart) => void;
apiMethodURL: string;
title: string;
description: string;
}
const ChartWidget = ({ title, description }: Props) => {
const ChartWidget = ({ id, title, description, onFullscreenClick }: Props) => {
const [ isZoomResetInitial, setIsZoomResetInitial ] = React.useState(true);
const handleZoom = useCallback(() => {
......@@ -21,6 +27,10 @@ const ChartWidget = ({ title, description }: Props) => {
setIsZoomResetInitial(true);
}, []);
const handleFullscreenClick = useCallback(() => {
onFullscreenClick({ id, title });
}, [ id, title, onFullscreenClick ]);
return (
<Box
padding={{ base: 3, md: 4 }}
......@@ -28,7 +38,10 @@ const ChartWidget = ({ title, description }: Props) => {
border="1px"
borderColor={ useColorModeValue('gray.200', 'gray.600') }
>
<Grid>
<Grid
gridTemplateColumns="auto auto 36px"
gridColumnGap={ 4 }
>
<Heading
mb={ 1 }
size={{ base: 'xs', md: 'sm' }}
......@@ -50,7 +63,7 @@ const ChartWidget = ({ title, description }: Props) => {
<Button
gridColumn={ 2 }
justifySelf="end"
alignSelf="center"
alignSelf="top"
gridRow="1/3"
size="sm"
variant="outline"
......@@ -59,6 +72,26 @@ const ChartWidget = ({ title, description }: Props) => {
Reset zoom
</Button>
) }
<Menu>
<MenuButton
gridColumn={ 3 }
gridRow="1/3"
justifySelf="end"
w="36px"
h="32px"
icon={ <Icon as={ dotsIcon } w={ 4 } h={ 4 } color={ useColorModeValue('black', 'white') }/> }
colorScheme="transparent"
as={ IconButton }
>
<VisuallyHidden>
Open chart options menu
</VisuallyHidden>
</MenuButton>
<MenuList>
<MenuItem onClick={ handleFullscreenClick }>View fullscreen</MenuItem>
</MenuList>
</Menu>
</Grid>
<ChartWidgetGraph
......
......@@ -20,7 +20,7 @@ interface Props {
isZoomResetInitial: boolean;
}
const CHART_MARGIN = { bottom: 20, left: 65, right: 30, top: 10 };
const CHART_MARGIN = { bottom: 20, left: 52, right: 30, top: 10 };
const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) => {
const ref = React.useRef<SVGSVGElement>(null);
......
import { Grid, GridItem, Heading, List, ListItem } from '@chakra-ui/react';
import { Box, Grid, GridItem, Heading, List, ListItem } from '@chakra-ui/react';
import React from 'react';
import type { StatsSection } from 'types/client/stats';
import type { ModalChart, StatsSection } from 'types/client/stats';
import { apos } from 'lib/html-entities';
import EmptySearchResult from '../apps/EmptySearchResult';
import ChartWidget from './ChartWidget';
import FullscreenChartModal from './FullscreenChartModal';
type Props = {
charts: Array<StatsSection>;
onChartFullscreenClick: (chart: ModalChart) => void;
fullscreenChart: ModalChart | null;
onModalClose: () => void;
}
const ChartsWidgetsList = ({ charts }: Props) => {
const ChartsWidgetsList = ({ charts, onChartFullscreenClick, fullscreenChart, onModalClose }: Props) => {
const isAnyChartDisplayed = charts.some((section) => section.charts.some(chart => chart.visible));
return isAnyChartDisplayed ? (
<List>
{
charts.map((section) => (
<ListItem
display={ section.charts.every((chart) => !chart.visible) ? 'none' : 'block' }
key={ section.id }
mb={ 8 }
_last={{
marginBottom: 0,
}}
>
<Heading
size="md"
mb={ 4 }
>
{ section.title }
</Heading>
return (
<Box>
{ isAnyChartDisplayed ? (
<List>
{
charts.map((section) => (
<ListItem
display={ section.charts.every((chart) => !chart.visible) ? 'none' : 'block' }
key={ section.id }
mb={ 8 }
_last={{
marginBottom: 0,
}}
>
<Heading
size="md"
mb={ 4 }
>
{ section.title }
</Heading>
<Grid
templateColumns={{
sm: 'repeat(2, 1fr)',
}}
gap={ 4 }
>
{ section.charts.map((chart) => (
<GridItem
key={ chart.id }
display={ chart.visible ? 'block' : 'none' }
<Grid
templateColumns={{
sm: 'repeat(2, 1fr)',
}}
gap={ 4 }
>
<ChartWidget
apiMethodURL={ chart.apiMethodURL }
title={ chart.title }
description={ chart.description }
/>
</GridItem>
)) }
</Grid>
</ListItem>
))
}
</List>
) : (
<EmptySearchResult text={ `Couldn${ apos }t find a chart that matches your filter query.` }/>
{ section.charts.map((chart) => (
<GridItem
key={ chart.id }
display={ chart.visible ? 'block' : 'none' }
>
<ChartWidget
id={ chart.id }
onFullscreenClick={ onChartFullscreenClick }
apiMethodURL={ chart.apiMethodURL }
title={ chart.title }
description={ chart.description }
/>
</GridItem>
)) }
</Grid>
</ListItem>
))
}
</List>
) : (
<EmptySearchResult text={ `Couldn${ apos }t find a chart that matches your filter query.` }/>
) }
{ fullscreenChart && (
<FullscreenChartModal
id={ fullscreenChart.id }
title={ fullscreenChart.title }
onClose={ onModalClose }
/>
) }
</Box>
);
};
......
import { Button, Flex, Heading, Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay } from '@chakra-ui/react';
import React, { useCallback } from 'react';
import ChartWidgetGraph from './ChartWidgetGraph';
import { demoChartsData } from './constants/demo-charts-data';
type Props = {
id: string;
title: string;
onClose: () => void;
}
const FullscreenChartModal = ({
id,
title,
onClose,
}: Props) => {
const [ isZoomResetInitial, setIsZoomResetInitial ] = React.useState(true);
const handleZoom = useCallback(() => {
setIsZoomResetInitial(false);
}, []);
const handleZoomResetClick = useCallback(() => {
setIsZoomResetInitial(true);
}, []);
return (
<Modal
isOpen={ Boolean(id) }
onClose={ onClose }
size="full"
isCentered
>
<ModalOverlay/>
<ModalContent>
<ModalHeader>
<Flex
alignItems="center"
>
<Heading
as="h2"
gridColumn={ 2 }
fontSize={{ base: '2xl', sm: '3xl' }}
fontWeight="medium"
lineHeight={ 1 }
color="blue.600"
>
{ title }
</Heading>
{ !isZoomResetInitial && (
<Button
ml="auto"
gridColumn={ 2 }
justifySelf="end"
alignSelf="top"
gridRow="1/3"
size="md"
variant="outline"
onClick={ handleZoomResetClick }
>
Reset zoom
</Button>
) }
</Flex>
</ModalHeader>
<ModalCloseButton/>
<ModalBody
h="100%"
>
<ChartWidgetGraph
items={ demoChartsData }
onZoom={ handleZoom }
isZoomResetInitial={ isZoomResetInitial }
title="test"
/>
</ModalBody>
</ModalContent>
</Modal>
);
};
export default FullscreenChartModal;
import debounce from 'lodash/debounce';
import React, { useCallback, useEffect, useState } from 'react';
import type { StatsChart, StatsIntervalIds, StatsSection, StatsSectionIds } from 'types/client/stats';
import type { ModalChart, StatsChart, StatsIntervalIds, StatsSection, StatsSectionIds } from 'types/client/stats';
import { statsChartsScheme } from './constants/charts-scheme';
......@@ -17,6 +17,7 @@ export default function useStats() {
const [ displayedCharts, setDisplayedCharts ] = useState<Array<StatsSection>>(statsChartsScheme);
const [ section, setSection ] = useState<StatsSectionIds>('all');
const [ interval, setInterval ] = useState<StatsIntervalIds>('all');
const [ fullscreenChart, setFullscreenChart ] = useState<ModalChart | null>(null);
const [ filterQuery, setFilterQuery ] = useState('');
// eslint-disable-next-line react-hooks/exhaustive-deps
......@@ -47,6 +48,22 @@ export default function useStats() {
setInterval(newInterval);
}, []);
const showChartFullscreen = useCallback((chart: ModalChart) => {
setFullscreenChart(chart);
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
}
}, []);
const clearFullscreenChart = useCallback(() => {
setFullscreenChart(null);
if (document.fullscreenElement) {
document.exitFullscreen();
}
}, []);
useEffect(() => {
filterCharts(filterQuery, section);
}, [ filterQuery, section, filterCharts ]);
......@@ -58,6 +75,9 @@ export default function useStats() {
handleIntervalChange,
debounceFilterCharts,
displayedCharts,
showChartFullscreen,
clearFullscreenChart,
fullscreenChart,
}), [
section,
handleSectionChange,
......@@ -65,5 +85,8 @@ export default function useStats() {
handleIntervalChange,
debounceFilterCharts,
displayedCharts,
showChartFullscreen,
clearFullscreenChart,
fullscreenChart,
]);
}
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