mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
97 lines
3.2 KiB
TypeScript
97 lines
3.2 KiB
TypeScript
import { useToken } from '@chakra-ui/react';
|
|
import * as d3 from 'd3';
|
|
import React from 'react';
|
|
|
|
import type { TimeChartData } from '../types';
|
|
|
|
import type { CurrentPoint } from './ChartTooltipPoint';
|
|
import { calculateRowTransformValue, LABEL_WIDTH, PADDING } from './utils';
|
|
|
|
type Props = {
|
|
lineNum: number;
|
|
} & ({ label: string; children?: never } | { children: React.ReactNode; label?: never });
|
|
|
|
const ChartTooltipRow = ({ label, lineNum, children }: Props) => {
|
|
const labelColor = useToken('colors', 'blue.100');
|
|
const textColor = useToken('colors', 'white');
|
|
|
|
return (
|
|
<g className="ChartTooltip__row" transform={ calculateRowTransformValue(lineNum) }>
|
|
{ children || (
|
|
<>
|
|
<text
|
|
className="ChartTooltip__label"
|
|
transform="translate(0,0)"
|
|
dominantBaseline="hanging"
|
|
fill={ labelColor[0] }
|
|
>
|
|
{ label }
|
|
</text>
|
|
<text
|
|
className="ChartTooltip__value"
|
|
transform={ `translate(${ LABEL_WIDTH },0)` }
|
|
dominantBaseline="hanging"
|
|
fill={ textColor[0] }
|
|
/>
|
|
</>
|
|
) }
|
|
</g>
|
|
);
|
|
};
|
|
|
|
export default React.memo(ChartTooltipRow);
|
|
|
|
interface UseRenderRowsParams {
|
|
data: TimeChartData;
|
|
xScale: d3.ScaleTime<number, number>;
|
|
minWidth: number;
|
|
}
|
|
|
|
interface UseRenderRowsReturnType {
|
|
width: number;
|
|
}
|
|
|
|
export function useRenderRows(ref: React.RefObject<SVGGElement | null>, { data, xScale, minWidth }: UseRenderRowsParams) {
|
|
return React.useCallback((x: number, currentPoints: Array<CurrentPoint>): UseRenderRowsReturnType => {
|
|
|
|
// update "transform" prop of all rows
|
|
const isIncompleteData = currentPoints.some(({ item }) => item.isApproximate);
|
|
d3.select(ref.current)
|
|
.selectAll<Element, TimeChartData>('.ChartTooltip__row')
|
|
.attr('transform', (datum, index) => {
|
|
return calculateRowTransformValue(index - (isIncompleteData ? 0 : 1));
|
|
});
|
|
|
|
// update date and indicators value
|
|
// here we assume that the first value element contains the date
|
|
const valueNodes = d3.select(ref.current)
|
|
.selectAll<Element, TimeChartData>('.ChartTooltip__value')
|
|
.text((_, index) => {
|
|
if (index === 0) {
|
|
const date = xScale.invert(x);
|
|
const dateValue = data[0].items.find((item) => item.date.getTime() === date.getTime())?.dateLabel;
|
|
const dateValueFallback = d3.timeFormat('%e %b %Y')(xScale.invert(x));
|
|
return dateValue || dateValueFallback;
|
|
}
|
|
|
|
const { datumIndex, item } = currentPoints.find(({ datumIndex }) => datumIndex === index - 1) || {};
|
|
if (datumIndex === undefined || !item) {
|
|
return null;
|
|
}
|
|
|
|
const value = data[datumIndex]?.valueFormatter?.(item.value) ?? item.value.toLocaleString(undefined, { minimumSignificantDigits: 1 });
|
|
const units = data[datumIndex]?.units ? ` ${ data[datumIndex]?.units }` : '';
|
|
|
|
return value + units;
|
|
})
|
|
.nodes();
|
|
|
|
const valueWidths = valueNodes.map((node) => node?.getBoundingClientRect?.().width);
|
|
const maxValueWidth = Math.max(...valueWidths);
|
|
const maxRowWidth = Math.max(minWidth, 2 * PADDING + LABEL_WIDTH + maxValueWidth);
|
|
|
|
return { width: maxRowWidth };
|
|
|
|
}, [ data, minWidth, ref, xScale ]);
|
|
}
|