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