Commit 43cf0fae authored by Yuri Mikhin's avatar Yuri Mikhin Committed by Yuri Mikhin

Chart refinements.

parent f54a17da
...@@ -2,12 +2,14 @@ import type { NextPage } from 'next'; ...@@ -2,12 +2,14 @@ import type { NextPage } from 'next';
import Head from 'next/head'; import Head from 'next/head';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config';
import Stats from '../ui/pages/Stats'; import Stats from '../ui/pages/Stats';
const StatsPage: NextPage = () => { const StatsPage: NextPage = () => {
return ( return (
<> <>
<Head><title>Ethereum Stats</title></Head> <Head><title>{ appConfig.network.name } Stats</title></Head>
<Stats/> <Stats/>
</> </>
); );
......
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
...@@ -21,7 +22,7 @@ const Stats = () => { ...@@ -21,7 +22,7 @@ const Stats = () => {
return ( return (
<Page> <Page>
<PageTitle text="Ethereum Stats"/> <PageTitle text={ `${ appConfig.network.name } Stats` }/>
<Box mb={{ base: 6, sm: 8 }}> <Box mb={{ base: 6, sm: 8 }}>
<NumberWidgetsList/> <NumberWidgetsList/>
......
...@@ -6,6 +6,7 @@ import React from 'react'; ...@@ -6,6 +6,7 @@ import React from 'react';
import type { TimeChartItem } from 'ui/shared/chart/types'; import type { TimeChartItem } from 'ui/shared/chart/types';
interface Props extends React.SVGProps<SVGPathElement> { interface Props extends React.SVGProps<SVGPathElement> {
id?: string;
xScale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>; xScale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>;
yScale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>; yScale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>;
color?: string; color?: string;
...@@ -13,10 +14,11 @@ interface Props extends React.SVGProps<SVGPathElement> { ...@@ -13,10 +14,11 @@ interface Props extends React.SVGProps<SVGPathElement> {
disableAnimation?: boolean; disableAnimation?: boolean;
} }
const ChartArea = ({ xScale, yScale, color, data, disableAnimation, ...props }: Props) => { const ChartArea = ({ id, xScale, yScale, color, data, disableAnimation, ...props }: Props) => {
const ref = React.useRef(null); const ref = React.useRef(null);
const theme = useTheme(); const theme = useTheme();
const gradientColorId = `${ id || 'gradient' }-${ color }-color`;
const gradientStopColor = useToken('colors', useColorModeValue('whiteAlpha.200', 'blackAlpha.100')); const gradientStopColor = useToken('colors', useColorModeValue('whiteAlpha.200', 'blackAlpha.100'));
const defaultGradient = { const defaultGradient = {
startColor: useToken('colors', useColorModeValue('blue.100', 'blue.400')), startColor: useToken('colors', useColorModeValue('blue.100', 'blue.400')),
...@@ -45,11 +47,11 @@ const ChartArea = ({ xScale, yScale, color, data, disableAnimation, ...props }: ...@@ -45,11 +47,11 @@ const ChartArea = ({ xScale, yScale, color, data, disableAnimation, ...props }:
return ( return (
<> <>
<path ref={ ref } d={ d } fill={ color ? `url(#gradient-${ color })` : 'url(#gradient-chart-area-default)' } opacity={ 0 } { ...props }/> <path ref={ ref } d={ d } fill={ color ? `url(#${ gradientColorId })` : 'url(#gradient-chart-area-default)' } opacity={ 0 } { ...props }/>
{ color ? ( { color ? (
<defs> <defs>
<linearGradient id={ `gradient-${ color }` } x1="0%" x2="0%" y1="0%" y2="100%"> <linearGradient id={ `${ gradientColorId }` } x1="0%" x2="0%" y1="0%" y2="100%">
<stop offset="20%" stopColor={ color }/> <stop offset="0%" stopColor={ color }/>
<stop offset="100%" stopColor={ gradientStopColor }/> <stop offset="100%" stopColor={ gradientStopColor }/>
</linearGradient> </linearGradient>
</defs> </defs>
......
...@@ -9,6 +9,7 @@ import type { Pointer } from 'ui/shared/chart/utils/pointerTracker'; ...@@ -9,6 +9,7 @@ import type { Pointer } from 'ui/shared/chart/utils/pointerTracker';
import { trackPointer } from 'ui/shared/chart/utils/pointerTracker'; import { trackPointer } from 'ui/shared/chart/utils/pointerTracker';
interface Props { interface Props {
chartId?: string;
width?: number; width?: number;
height?: number; height?: number;
data: TimeChartData; data: TimeChartData;
...@@ -22,7 +23,7 @@ const PADDING = 16; ...@@ -22,7 +23,7 @@ const PADDING = 16;
const LINE_SPACE = 10; const LINE_SPACE = 10;
const POINT_SIZE = 16; const POINT_SIZE = 16;
const ChartTooltip = ({ xScale, yScale, width, height, data, anchorEl, ...props }: Props) => { const ChartTooltip = ({ chartId, xScale, yScale, width, height, data, anchorEl, ...props }: Props) => {
const lineColor = useToken('colors', 'gray.400'); const lineColor = useToken('colors', 'gray.400');
const titleColor = useToken('colors', 'blue.100'); const titleColor = useToken('colors', 'blue.100');
const textColor = useToken('colors', 'white'); const textColor = useToken('colors', 'white');
...@@ -71,10 +72,10 @@ const ChartTooltip = ({ xScale, yScale, width, height, data, anchorEl, ...props ...@@ -71,10 +72,10 @@ const ChartTooltip = ({ xScale, yScale, width, height, data, anchorEl, ...props
); );
const updateDisplayedValue = React.useCallback((d: TimeChartItem, i: number) => { const updateDisplayedValue = React.useCallback((d: TimeChartItem, i: number) => {
d3.selectAll('.ChartTooltip__value') d3.selectAll(`${ chartId ? `#${ chartId }` : '' } .ChartTooltip__value`)
.filter((td, tIndex) => tIndex === i) .filter((td, tIndex) => tIndex === i)
.text(data[i].valueFormatter?.(d.value) || d.value.toLocaleString()); .text(data[i].valueFormatter?.(d.value) || d.value.toLocaleString());
}, [ data ]); }, [ data, chartId ]);
const drawPoints = React.useCallback((x: number) => { const drawPoints = React.useCallback((x: number) => {
const xDate = xScale.invert(x); const xDate = xScale.invert(x);
......
...@@ -27,14 +27,15 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) = ...@@ -27,14 +27,15 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) =
const [ range, setRange ] = React.useState<[ number, number ]>([ 0, Infinity ]); const [ range, setRange ] = React.useState<[ number, number ]>([ 0, Infinity ]);
const { width, height, innerWidth, innerHeight } = useChartSize(ref.current, CHART_MARGIN); const { width, height, innerWidth, innerHeight } = useChartSize(ref.current, CHART_MARGIN);
const overlayRef = React.useRef<SVGRectElement>(null); const overlayRef = React.useRef<SVGRectElement>(null);
const color = useToken('colors', 'blue.500'); const color = useToken('colors', 'blue.200');
const chartId = useMemo(() => `chart-${ crypto.randomUUID() }`, []);
const displayedData = useMemo(() => items.slice(range[0], range[1]).map((d) => const displayedData = useMemo(() => items.slice(range[0], range[1]).map((d) =>
({ ...d, date: new Date(d.date) })), [ items, range ]); ({ ...d, date: new Date(d.date) })), [ items, range ]);
const chartData = [ { items: items, name: title, color } ]; const chartData = [ { items: items, name: 'Value', color } ];
const { yTickFormat, xScale, yScale } = useTimeChartController({ const { yTickFormat, xScale, yScale } = useTimeChartController({
data: [ { items: displayedData, name: 'chart', color } ], data: [ { items: displayedData, name: title, color } ],
width: innerWidth, width: innerWidth,
height: innerHeight, height: innerHeight,
}); });
...@@ -51,7 +52,7 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) = ...@@ -51,7 +52,7 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) =
}, [ isZoomResetInitial ]); }, [ isZoomResetInitial ]);
return ( return (
<svg width={ width || '100%' } height={ height || '100%' } ref={ ref } cursor="pointer"> <svg width={ width || '100%' } height={ height || '100%' } ref={ ref } cursor="pointer" id={ chartId }>
<g transform={ `translate(${ CHART_MARGIN?.left || 0 },${ CHART_MARGIN?.top || 0 })` } opacity={ width ? 1 : 0 }> <g transform={ `translate(${ CHART_MARGIN?.left || 0 },${ CHART_MARGIN?.top || 0 })` } opacity={ width ? 1 : 0 }>
<ChartGridLine <ChartGridLine
...@@ -63,6 +64,7 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) = ...@@ -63,6 +64,7 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) =
/> />
<ChartArea <ChartArea
id={ chartId }
data={ displayedData } data={ displayedData }
color={ color } color={ color }
xScale={ xScale } xScale={ xScale }
...@@ -97,6 +99,7 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) = ...@@ -97,6 +99,7 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) =
/> />
<ChartTooltip <ChartTooltip
chartId={ chartId }
anchorEl={ overlayRef.current } anchorEl={ overlayRef.current }
width={ innerWidth } width={ innerWidth }
height={ innerHeight } height={ innerHeight }
......
...@@ -9,8 +9,7 @@ type Props = { ...@@ -9,8 +9,7 @@ type Props = {
const NumberWidget = ({ label, value }: Props) => { const NumberWidget = ({ label, value }: Props) => {
return ( return (
<Box <Box
border="1px" bg={ useColorModeValue('blue.50', 'blue.800') }
borderColor={ useColorModeValue('gray.200', 'gray.600') }
p={ 3 } p={ 3 }
borderRadius={ 12 } borderRadius={ 12 }
> >
......
...@@ -6,12 +6,18 @@ import type { StatsInterval, StatsIntervalIds, StatsSection, StatsSectionIds } f ...@@ -6,12 +6,18 @@ import type { StatsInterval, StatsIntervalIds, StatsSection, StatsSectionIds } f
import FilterInput from 'ui/shared/FilterInput'; import FilterInput from 'ui/shared/FilterInput';
import { STATS_INTERVALS, STATS_SECTIONS } from './constants'; import { STATS_INTERVALS, STATS_SECTIONS } from './constants';
import { statsChartsScheme } from './constants/charts-scheme';
import StatsDropdownMenu from './StatsDropdownMenu'; import StatsDropdownMenu from './StatsDropdownMenu';
const sectionsList = Object.keys(STATS_SECTIONS).map((id: string) => ({ const listedSections = statsChartsScheme
id: id, .filter(section => section.charts.length > 0);
title: STATS_SECTIONS[id as StatsSectionIds],
})) as Array<StatsSection>; const sectionsList = Object.keys(STATS_SECTIONS)
.filter(key => key === 'all' || listedSections.some(section => section.id === key))
.map((id: string) => ({
id: id,
title: STATS_SECTIONS[id as StatsSectionIds],
})) as Array<StatsSection>;
const intervalList = Object.keys(STATS_INTERVALS).map((id: string) => ({ const intervalList = Object.keys(STATS_INTERVALS).map((id: string) => ({
id: id, id: id,
......
...@@ -14,9 +14,7 @@ function isChartNameMatches(q: string, chart: StatsChart) { ...@@ -14,9 +14,7 @@ function isChartNameMatches(q: string, chart: StatsChart) {
} }
export default function useStats() { export default function useStats() {
const [ isLoading, setIsLoading ] = useState(true); const [ displayedCharts, setDisplayedCharts ] = useState<Array<StatsSection>>(statsChartsScheme);
const [ defaultCharts, setDefaultCharts ] = useState<Array<StatsSection>>();
const [ displayedCharts, setDisplayedCharts ] = useState<Array<StatsSection>>([]);
const [ section, setSection ] = useState<StatsSectionIds>('all'); const [ section, setSection ] = useState<StatsSectionIds>('all');
const [ interval, setInterval ] = useState<StatsIntervalIds>('all'); const [ interval, setInterval ] = useState<StatsIntervalIds>('all');
const [ filterQuery, setFilterQuery ] = useState(''); const [ filterQuery, setFilterQuery ] = useState('');
...@@ -25,7 +23,7 @@ export default function useStats() { ...@@ -25,7 +23,7 @@ export default function useStats() {
const debounceFilterCharts = useCallback(debounce(q => setFilterQuery(q), 500), []); const debounceFilterCharts = useCallback(debounce(q => setFilterQuery(q), 500), []);
const filterCharts = useCallback((q: string, currentSection: StatsSectionIds) => { const filterCharts = useCallback((q: string, currentSection: StatsSectionIds) => {
const charts = defaultCharts const charts = statsChartsScheme
?.map((section: StatsSection) => { ?.map((section: StatsSection) => {
const charts = section.charts.map((chart: StatsChart) => ({ const charts = section.charts.map((chart: StatsChart) => ({
...chart, ...chart,
...@@ -39,7 +37,7 @@ export default function useStats() { ...@@ -39,7 +37,7 @@ export default function useStats() {
}); });
setDisplayedCharts(charts || []); setDisplayedCharts(charts || []);
}, [ defaultCharts ]); }, []);
const handleSectionChange = useCallback((newSection: StatsSectionIds) => { const handleSectionChange = useCallback((newSection: StatsSectionIds) => {
setSection(newSection); setSection(newSection);
...@@ -53,19 +51,12 @@ export default function useStats() { ...@@ -53,19 +51,12 @@ export default function useStats() {
filterCharts(filterQuery, section); filterCharts(filterQuery, section);
}, [ filterQuery, section, filterCharts ]); }, [ filterQuery, section, filterCharts ]);
useEffect(() => {
setDefaultCharts(statsChartsScheme);
setDisplayedCharts(statsChartsScheme);
setIsLoading(false);
}, []);
return React.useMemo(() => ({ return React.useMemo(() => ({
section, section,
handleSectionChange, handleSectionChange,
interval, interval,
handleIntervalChange, handleIntervalChange,
debounceFilterCharts, debounceFilterCharts,
isLoading,
displayedCharts, displayedCharts,
}), [ }), [
section, section,
...@@ -74,6 +65,5 @@ export default function useStats() { ...@@ -74,6 +65,5 @@ export default function useStats() {
handleIntervalChange, handleIntervalChange,
debounceFilterCharts, debounceFilterCharts,
displayedCharts, displayedCharts,
isLoading,
]); ]);
} }
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