Commit b16ab20b authored by tom's avatar tom

type and styles refinements

parent 43a3a998
import { useToken } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import json from 'data/charts_eth_txs.json'; import json from 'data/charts_eth_txs.json';
...@@ -5,16 +6,14 @@ import Area from 'ui/shared/graphs/Area'; ...@@ -5,16 +6,14 @@ import Area from 'ui/shared/graphs/Area';
import Axis from 'ui/shared/graphs/Axis'; import Axis from 'ui/shared/graphs/Axis';
import GridLine from 'ui/shared/graphs/GridLine'; import GridLine from 'ui/shared/graphs/GridLine';
import Line from 'ui/shared/graphs/Line'; import Line from 'ui/shared/graphs/Line';
import useController from 'ui/shared/graphs/useController'; import useTimeGraphController from 'ui/shared/graphs/useTimeGraphController';
const dimensions = { const dimensions = {
width: 600, width: 600,
height: 300, height: 300,
margin: { top: 30, right: 30, bottom: 30, left: 60 }, margin: { top: 0, right: 0, bottom: 20, left: 65 },
}; };
const data = { const data = {
name: 'VCIT',
color: '#5e4fa2',
items: json.map((d) => ({ ...d, date: new Date(d.date) })), items: json.map((d) => ({ ...d, date: new Date(d.date) })),
}; };
...@@ -22,13 +21,15 @@ const EthereumDailyTxsChart = () => { ...@@ -22,13 +21,15 @@ const EthereumDailyTxsChart = () => {
const { width, height, margin } = dimensions; const { width, height, margin } = dimensions;
const svgWidth = width + margin.left + margin.right; const svgWidth = width + margin.left + margin.right;
const svgHeight = height + margin.top + margin.bottom; const svgHeight = height + margin.top + margin.bottom;
const controller = useController({ data, width, height }); const controller = useTimeGraphController({ data, width, height });
const { yTickFormat, xScale, yScale } = controller; const { yTickFormat, xScale, yScale } = controller;
const lineColor = useToken('colors', 'blue.500');
return ( return (
<svg width={ svgWidth } height={ svgHeight }> <svg width={ svgWidth } height={ svgHeight }>
<g transform={ `translate(${ margin.left },${ margin.top })` }> <g transform={ `translate(${ margin.left },${ margin.top })` }>
{ /* base grid line */ } { /* BASE GRID LINE */ }
<GridLine <GridLine
type="horizontal" type="horizontal"
scale={ yScale } scale={ yScale }
...@@ -37,6 +38,7 @@ const EthereumDailyTxsChart = () => { ...@@ -37,6 +38,7 @@ const EthereumDailyTxsChart = () => {
disableAnimation disableAnimation
/> />
{ /* GIRD LINES */ }
<GridLine <GridLine
type="vertical" type="vertical"
scale={ xScale } scale={ xScale }
...@@ -51,31 +53,32 @@ const EthereumDailyTxsChart = () => { ...@@ -51,31 +53,32 @@ const EthereumDailyTxsChart = () => {
size={ width } size={ width }
/> />
{ /* GRAPH */ }
<Line <Line
data={ data.items } data={ data.items }
xScale={ xScale } xScale={ xScale }
yScale={ yScale } yScale={ yScale }
color={ data.color } stroke={ lineColor }
animation="left" animation="left"
/> />
<Area <Area
data={ data.items } data={ data.items }
color={ data.color } color={ lineColor }
xScale={ xScale } xScale={ xScale }
yScale={ yScale } yScale={ yScale }
/> />
{ /* AXISES */ }
<Axis <Axis
type="left" type="left"
scale={ yScale } scale={ yScale }
transform="translate(0, -10)"
ticks={ 5 } ticks={ 5 }
tickFormat={ yTickFormat } tickFormat={ yTickFormat }
/> />
<Axis <Axis
type="bottom" type="bottom"
scale={ xScale } scale={ xScale }
transform={ `translate(10, ${ height - height / 6 })` } transform={ `translate(0, ${ height })` }
ticks={ 5 } ticks={ 5 }
/> />
</g> </g>
......
import * as d3 from 'd3'; import * as d3 from 'd3';
import React from 'react'; import React from 'react';
interface DataItem { import type { TimeGraphItem } from 'ui/shared/graphs/types';
date: Date;
value: number;
}
interface Props { interface Props extends React.SVGProps<SVGPathElement> {
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;
data: Array<DataItem>; data: Array<TimeGraphItem>;
disableAnimation?: boolean; disableAnimation?: boolean;
} }
...@@ -28,7 +25,7 @@ const Area = ({ xScale, yScale, color, data, disableAnimation, ...props }: Props ...@@ -28,7 +25,7 @@ const Area = ({ xScale, yScale, color, data, disableAnimation, ...props }: Props
}, [ disableAnimation ]); }, [ disableAnimation ]);
const d = React.useMemo(() => { const d = React.useMemo(() => {
const area = d3.area<DataItem>() const area = d3.area<TimeGraphItem>()
.x(({ date }) => xScale(date)) .x(({ date }) => xScale(date))
.y1(({ value }) => yScale(value)) .y1(({ value }) => yScale(value))
.y0(() => yScale(yScale.domain()[0])); .y0(() => yScale(yScale.domain()[0]));
...@@ -40,8 +37,8 @@ const Area = ({ xScale, yScale, color, data, disableAnimation, ...props }: Props ...@@ -40,8 +37,8 @@ const Area = ({ xScale, yScale, color, data, disableAnimation, ...props }: Props
<path ref={ ref } d={ d } fill={ `url(#gradient-${ color })` } opacity={ 0 } { ...props }/> <path ref={ ref } d={ d } fill={ `url(#gradient-${ color })` } opacity={ 0 } { ...props }/>
<defs> <defs>
<linearGradient id={ `gradient-${ color }` } x1="0%" x2="0%" y1="0%" y2="100%"> <linearGradient id={ `gradient-${ color }` } x1="0%" x2="0%" y1="0%" y2="100%">
<stop offset="0%" stopColor={ color } stopOpacity={ 0.5 }/> <stop offset="0%" stopColor={ color } stopOpacity={ 0.9 }/>
<stop offset="100%" stopColor={ color } stopOpacity={ 0 }/> <stop offset="100%" stopColor={ color } stopOpacity={ 0.1 }/>
</linearGradient> </linearGradient>
</defs> </defs>
</> </>
......
import { useColorModeValue, useToken } from '@chakra-ui/react';
import * as d3 from 'd3'; import * as d3 from 'd3';
import React from 'react'; import React from 'react';
interface Props { interface Props extends Omit<React.SVGProps<SVGGElement>, 'scale'> {
type: 'left' | 'bottom'; type: 'left' | 'bottom';
scale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>; scale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>;
disableAnimation?: boolean; disableAnimation?: boolean;
transform?: string;
ticks: number; ticks: number;
tickFormat?: (domainValue: d3.AxisDomain, index: number) => string; tickFormat?: (domainValue: d3.AxisDomain, index: number) => string;
} }
const Axis = ({ type, scale, ticks, transform, tickFormat, disableAnimation, ...props }: Props) => { const Axis = ({ type, scale, ticks, tickFormat, disableAnimation, ...props }: Props) => {
const ref = React.useRef<SVGGElement>(null); const ref = React.useRef<SVGGElement>(null);
const textColorToken = useColorModeValue('blackAlpha.500', 'whiteAlpha.500');
const textColor = useToken('colors', textColorToken);
React.useEffect(() => { React.useEffect(() => {
if (!ref.current) { if (!ref.current) {
return; return;
...@@ -31,12 +34,12 @@ const Axis = ({ type, scale, ticks, transform, tickFormat, disableAnimation, ... ...@@ -31,12 +34,12 @@ const Axis = ({ type, scale, ticks, transform, tickFormat, disableAnimation, ...
axisGroup.select('.domain').remove(); axisGroup.select('.domain').remove();
axisGroup.selectAll('line').remove(); axisGroup.selectAll('line').remove();
axisGroup.selectAll('text') axisGroup.selectAll('text')
.attr('opacity', 0.5) .attr('opacity', 1)
.attr('color', 'white') .attr('color', textColor)
.attr('font-size', '0.75rem'); .attr('font-size', '0.75rem');
}, [ scale, ticks, tickFormat, disableAnimation, type ]); }, [ scale, ticks, tickFormat, disableAnimation, type, textColor ]);
return <g ref={ ref } transform={ transform } { ...props }/>; return <g ref={ ref } { ...props }/>;
}; };
export default React.memo(Axis); export default React.memo(Axis);
import { useColorModeValue, useToken } from '@chakra-ui/react';
import * as d3 from 'd3'; import * as d3 from 'd3';
import React from 'react'; import React from 'react';
interface Props { interface Props extends Omit<React.SVGProps<SVGGElement>, 'scale'> {
type: 'vertical' | 'horizontal'; type: 'vertical' | 'horizontal';
scale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>; scale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>;
disableAnimation?: boolean; disableAnimation?: boolean;
size: number; size: number;
transform?: string;
ticks: number; ticks: number;
} }
const GridLine = ({ type, scale, ticks, size, transform, disableAnimation, ...props }: Props) => { const GridLine = ({ type, scale, ticks, size, disableAnimation, ...props }: Props) => {
const ref = React.useRef<SVGGElement>(null); const ref = React.useRef<SVGGElement>(null);
const strokeColorToken = useColorModeValue('blackAlpha.300', 'whiteAlpha.300');
const strokeColor = useToken('colors', strokeColorToken);
React.useEffect(() => { React.useEffect(() => {
if (!ref.current) { if (!ref.current) {
return; return;
...@@ -29,10 +32,10 @@ const GridLine = ({ type, scale, ticks, size, transform, disableAnimation, ...pr ...@@ -29,10 +32,10 @@ const GridLine = ({ type, scale, ticks, size, transform, disableAnimation, ...pr
} }
gridGroup.select('.domain').remove(); gridGroup.select('.domain').remove();
gridGroup.selectAll('text').remove(); gridGroup.selectAll('text').remove();
gridGroup.selectAll('line').attr('stroke', 'rgba(255, 255, 255, 0.1)'); gridGroup.selectAll('line').attr('stroke', strokeColor);
}, [ scale, ticks, size, disableAnimation, type ]); }, [ scale, ticks, size, disableAnimation, type, strokeColor ]);
return <g ref={ ref } transform={ transform } { ...props }/>; return <g ref={ ref } { ...props }/>;
}; };
export default React.memo(GridLine); export default React.memo(GridLine);
import * as d3 from 'd3'; import * as d3 from 'd3';
import React from 'react'; import React from 'react';
interface DataItem { import type { TimeGraphItem } from 'ui/shared/graphs/types';
date: Date;
value: number;
}
interface Props { interface Props extends React.SVGProps<SVGPathElement> {
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; data: Array<TimeGraphItem>;
data: Array<DataItem>;
animation: 'left' | 'fadeIn' | 'none'; animation: 'left' | 'fadeIn' | 'none';
} }
const Line = ({ xScale, yScale, color, data, animation, ...props }: Props) => { const Line = ({ xScale, yScale, data, animation, ...props }: Props) => {
const ref = React.useRef<SVGPathElement>(null); const ref = React.useRef<SVGPathElement>(null);
// Define different types of animation that we can use // Define different types of animation that we can use
...@@ -68,7 +64,7 @@ const Line = ({ xScale, yScale, color, data, animation, ...props }: Props) => { ...@@ -68,7 +64,7 @@ const Line = ({ xScale, yScale, color, data, animation, ...props }: Props) => {
} }
}, [ xScale, yScale, animation ]); }, [ xScale, yScale, animation ]);
const line = d3.line<DataItem>() const line = d3.line<TimeGraphItem>()
.x((d) => xScale(d.date)) .x((d) => xScale(d.date))
.y((d) => yScale(d.value)); .y((d) => yScale(d.value));
...@@ -76,7 +72,6 @@ const Line = ({ xScale, yScale, color, data, animation, ...props }: Props) => { ...@@ -76,7 +72,6 @@ const Line = ({ xScale, yScale, color, data, animation, ...props }: Props) => {
<path <path
ref={ ref } ref={ ref }
d={ line(data) || undefined } d={ line(data) || undefined }
stroke={ color }
strokeWidth={ 1 } strokeWidth={ 1 }
fill="none" fill="none"
opacity={ 0 } opacity={ 0 }
......
export interface TimeGraphItem {
date: Date;
value: number;
}
import * as d3 from 'd3'; import * as d3 from 'd3';
import { useMemo } from 'react'; import { useMemo } from 'react';
interface DataItem { import type { TimeGraphItem } from 'ui/shared/graphs/types';
date: Date;
value: number;
}
interface Props { interface Props {
data: { data: {
items: Array<DataItem>; items: Array<TimeGraphItem>;
}; };
width: number; width: number;
height: number; height: number;
} }
const useController = ({ data, width, height }: Props) => { const useTimeGraphController = ({ data, width, height }: Props) => {
const xMin = useMemo( const xMin = useMemo(
() => d3.min(data.items, ({ date }) => date) || new Date(), () => d3.min(data.items, ({ date }) => date) || new Date(),
...@@ -44,7 +41,7 @@ const useController = ({ data, width, height }: Props) => { ...@@ -44,7 +41,7 @@ const useController = ({ data, width, height }: Props) => {
const yScale = useMemo(() => { const yScale = useMemo(() => {
const indention = (yMax - yMin) * 0.5; const indention = (yMax - yMin) * 0.5;
return d3.scaleLinear() return d3.scaleLinear()
.domain([ Math.max(yMin - indention, 0), yMax + indention ]) .domain([ yMin >= 0 && yMin - indention <= 0 ? 0 : yMin - indention, yMax + indention ])
.range([ height, 0 ]); .range([ height, 0 ]);
}, [ height, yMin, yMax ]); }, [ height, yMin, yMax ]);
...@@ -53,9 +50,11 @@ const useController = ({ data, width, height }: Props) => { ...@@ -53,9 +50,11 @@ const useController = ({ data, width, height }: Props) => {
[ height, yMin, yMax ], [ height, yMin, yMax ],
); );
const xTickFormat = (d: d3.AxisDomain) => d.toLocaleString();
const yTickFormat = (d: d3.AxisDomain) => d.toLocaleString(); const yTickFormat = (d: d3.AxisDomain) => d.toLocaleString();
return { return {
xTickFormat,
yTickFormat, yTickFormat,
xScale, xScale,
yScale, yScale,
...@@ -63,4 +62,4 @@ const useController = ({ data, width, height }: Props) => { ...@@ -63,4 +62,4 @@ const useController = ({ data, width, height }: Props) => {
}; };
}; };
export default useController; export default useTimeGraphController;
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