Commit 5fb143c1 authored by tom's avatar tom Committed by isstuev

rewrite to support touch events

parent d8e8cba1
...@@ -25,7 +25,7 @@ const ChainIndicatorChartContainer = ({ data, isError, isLoading }: Props) => { ...@@ -25,7 +25,7 @@ const ChainIndicatorChartContainer = ({ data, isError, isLoading }: Props) => {
return <ChainIndicatorChart data={ data }/>; return <ChainIndicatorChart data={ data }/>;
})(); })();
return <Flex h="270px" alignItems="flex-start">{ content }</Flex>; return <Flex h={{ base: '130px', lg: '270px' }} alignItems="flex-start">{ content }</Flex>;
}; };
export default React.memo(ChainIndicatorChartContainer); export default React.memo(ChainIndicatorChartContainer);
...@@ -64,7 +64,17 @@ const ChainIndicators = () => { ...@@ -64,7 +64,17 @@ const ChainIndicators = () => {
})(); })();
return ( return (
<Flex p={ 8 } borderRadius="lg" boxShadow="lg" bgColor={ bgColor } columnGap={ 12 } w="100%" alignItems="stretch"> <Flex
p={ 8 }
borderRadius="lg"
boxShadow="lg"
bgColor={ bgColor }
columnGap={ 12 }
rowGap={ 12 }
flexDir={{ base: 'column', lg: 'row' }}
w="100%"
alignItems="stretch"
>
<Flex flexGrow={ 1 } flexDir="column"> <Flex flexGrow={ 1 } flexDir="column">
<Flex alignItems="center"> <Flex alignItems="center">
<Text fontWeight={ 500 } fontFamily="heading" fontSize="lg">{ indicator?.title }</Text> <Text fontWeight={ 500 } fontFamily="heading" fontSize="lg">{ indicator?.title }</Text>
......
...@@ -5,6 +5,9 @@ import React from 'react'; ...@@ -5,6 +5,9 @@ import React from 'react';
import type { TimeChartItem, TimeChartData } from 'ui/shared/chart/types'; import type { TimeChartItem, TimeChartData } from 'ui/shared/chart/types';
import type { Pointer } from 'ui/shared/chart/utils/pointerTracker';
import { trackPointer } from 'ui/shared/chart/utils/pointerTracker';
interface Props { interface Props {
width?: number; width?: number;
height?: number; height?: number;
...@@ -27,7 +30,6 @@ const ChartTooltip = ({ xScale, yScale, width, height, data, anchorEl, ...props ...@@ -27,7 +30,6 @@ const ChartTooltip = ({ xScale, yScale, width, height, data, anchorEl, ...props
const bgColor = useToken('colors', 'blackAlpha.900'); const bgColor = useToken('colors', 'blackAlpha.900');
const ref = React.useRef(null); const ref = React.useRef(null);
const isPressed = React.useRef(false);
const drawLine = React.useCallback( const drawLine = React.useCallback(
(x: number) => { (x: number) => {
...@@ -67,8 +69,7 @@ const ChartTooltip = ({ xScale, yScale, width, height, data, anchorEl, ...props ...@@ -67,8 +69,7 @@ const ChartTooltip = ({ xScale, yScale, width, height, data, anchorEl, ...props
.text(data[i].valueFormatter?.(d.value) || d.value.toLocaleString()); .text(data[i].valueFormatter?.(d.value) || d.value.toLocaleString());
}, [ data ]); }, [ data ]);
const drawCircles = React.useCallback((event: MouseEvent) => { const drawPoints = React.useCallback((x: number) => {
const [ x ] = d3.pointer(event, anchorEl);
const xDate = xScale.invert(x); const xDate = xScale.invert(x);
const bisectDate = d3.bisector<TimeChartItem, unknown>((d) => d.date).left; const bisectDate = d3.bisector<TimeChartItem, unknown>((d) => d.date).left;
let baseXPos = 0; let baseXPos = 0;
...@@ -101,53 +102,50 @@ const ChartTooltip = ({ xScale, yScale, width, height, data, anchorEl, ...props ...@@ -101,53 +102,50 @@ const ChartTooltip = ({ xScale, yScale, width, height, data, anchorEl, ...props
}); });
return [ baseXPos, baseYPos ]; return [ baseXPos, baseYPos ];
}, [ anchorEl, data, updateDisplayedValue, xScale, yScale ]); }, [ data, updateDisplayedValue, xScale, yScale ]);
const followPoints = React.useCallback((event: MouseEvent) => { const draw = React.useCallback((pointer: Pointer) => {
const [ baseXPos, baseYPos ] = drawCircles(event); if (pointer.point) {
const [ baseXPos, baseYPos ] = drawPoints(pointer.point[0]);
drawLine(baseXPos); drawLine(baseXPos);
drawContent(baseXPos, baseYPos); drawContent(baseXPos, baseYPos);
}, [ drawCircles, drawLine, drawContent ]); }
}, [ drawPoints, drawLine, drawContent ]);
React.useEffect(() => { React.useEffect(() => {
const anchorD3 = d3.select(anchorEl); const anchorD3 = d3.select(anchorEl);
const subscriptions: Array<string> = [];
anchorD3 anchorD3
.on('mousedown.tooltip', () => { .on('touchmove.tooltip', (event: PointerEvent) => event.preventDefault()) // prevent scrolling
isPressed.current = true; .on('pointerenter.tooltip pointerdown.tooltip', (event: PointerEvent) => {
d3.select(ref.current).attr('opacity', 0); const newSubscriptions = trackPointer(event, {
}) start: () => {
.on('mouseup.tooltip', () => {
isPressed.current = false;
})
.on('mouseout.tooltip', () => {
d3.select(ref.current).attr('opacity', 0);
})
.on('mouseover.tooltip', () => {
d3.select(ref.current).attr('opacity', 1);
})
.on('mousemove.tooltip', (event: MouseEvent) => {
if (!isPressed.current) {
d3.select(ref.current).attr('opacity', 1); d3.select(ref.current).attr('opacity', 1);
d3.select(ref.current) d3.select(ref.current)
.selectAll('.ChartTooltip__linePoint') .selectAll('.ChartTooltip__linePoint')
.attr('opacity', 1); .attr('opacity', 1);
followPoints(event); },
} move: (pointer) => {
draw(pointer);
},
out: () => {
d3.select(ref.current).attr('opacity', 0);
},
end: () => {
d3.select(ref.current).attr('opacity', 0);
},
}); });
d3.select('body').on('mouseup.tooltip', function(event) { subscriptions.push(...newSubscriptions);
const isOutside = event.target !== anchorD3.node();
if (isOutside) {
isPressed.current = false;
}
}); });
return () => { return () => {
anchorD3.on('mousedown.tooltip mouseup.tooltip mouseout.tooltip mouseover.tooltip mousemove.tooltip', null); anchorD3.on('touchmove.tooltip pointerenter.tooltip pointerdown.tooltip', null);
d3.select('body').on('mouseup.tooltip', null); subscriptions && anchorD3.on(subscriptions.join(' '), null);
}; };
}, [ anchorEl, followPoints ]); }, [ anchorEl, draw ]);
return ( return (
<g ref={ ref } opacity={ 0 } { ...props }> <g ref={ ref } opacity={ 0 } { ...props }>
......
import * as d3 from 'd3';
export interface Pointer {
id: number;
point: [number, number] | null;
prev: [number, number] | null;
sourceEvent?: PointerEvent;
}
export interface PointerOptions {
start?: (tracker: Pointer) => void;
move?: (tracker: Pointer) => void;
out?: (tracker: Pointer) => void;
end?: (tracker: Pointer) => void;
}
export function trackPointer(event: PointerEvent, { start, move, out, end }: PointerOptions) {
const tracker: Pointer = {
id: event.pointerId,
point: null,
prev: null,
};
const id = event.pointerId;
const target = event.target as Element;
tracker.point = d3.pointer(event, target);
target.setPointerCapture(id);
d3.select(target)
.on(`pointerup.${ id } pointercancel.${ id } lostpointercapture.${ id }`, (sourceEvent: PointerEvent) => {
if (sourceEvent.pointerId !== id) {
return;
}
tracker.sourceEvent = sourceEvent;
d3.select(target).on(`.${ id }`, null);
target.releasePointerCapture(id);
end?.(tracker);
})
.on(`pointermove.${ id }`, (sourceEvent) => {
if (sourceEvent.pointerId !== id) {
return;
}
tracker.sourceEvent = sourceEvent;
tracker.prev = tracker.point;
tracker.point = d3.pointer(sourceEvent, target);
move?.(tracker);
})
.on(`pointerout.${ id }`, (e) => {
if (e.pointerId !== id) {
return;
}
tracker.sourceEvent = e;
tracker.point = null;
out?.(tracker);
});
start?.(tracker);
return [ 'pointerup', 'pointercancel', 'lostpointercapture', 'pointermove', 'pointerout' ].map((event) => `${ event }.${ id }`);
}
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