All files / src/plugins/gravity-charts/renderer/withSplitPane withSplitPane.tsx

0% Statements 0/33
0% Branches 0/6
0% Functions 0/12
0% Lines 0/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129                                                                                                                                                                                                                                                                 
import React from 'react';
 
import {Chart, getDefaultTooltipHeaderFormat} from '@gravity-ui/charts';
import type {ChartData, ChartProps, ChartRef} from '@gravity-ui/charts';
import {getComponentName} from '@gravity-ui/uikit';
import isEmpty from 'es-toolkit/compat/isEmpty';
 
import {SplitTooltip} from '../../../../components/SplitTooltip';
 
import {TooltipContent} from './TooltipContent';
import type {TooltipContentRef} from './TooltipContent';
import {useViewportSplitTooltipLayout} from './useViewportSplitTooltipLayout';
 
type WithSplitPaneProps = {
    onReflowError: (error: unknown) => void;
};
 
type PointerMoveHandler = NonNullable<
    NonNullable<NonNullable<ChartData['chart']>['events']>['pointermove']
>;
 
const SplitPaneContent = (
    props: ChartProps &
        WithSplitPaneProps & {
            ChartComponent: typeof Chart;
            forwardedRef: React.ForwardedRef<ChartRef>;
        },
) => {
    const {data, ChartComponent, forwardedRef, onReflowError, ...restProps} = props;
    const chartRef = React.useRef<ChartRef>(null);
    React.useImperativeHandle(
        forwardedRef,
        () => ({
            reflow: (options) => {
                if (chartRef.current) {
                    return chartRef.current.reflow(options);
                }
                const unavailable = Promise.reject<void>(new Error('Chart host is unavailable'));
                unavailable.catch(() => undefined);
                return unavailable;
            },
        }),
        [],
    );
    const tooltipRef = React.useRef<TooltipContentRef>(null);
    const [tooltipVisible, setTooltipVisible] = React.useState(false);
    const layout = useViewportSplitTooltipLayout();
 
    const headerFormat = React.useMemo(() => {
        return (
            data.tooltip?.headerFormat ??
            getDefaultTooltipHeaderFormat({
                seriesData: data.series.data,
                yAxes: data.yAxis,
                xAxis: data.xAxis,
            })
        );
    }, [data.tooltip?.headerFormat, data.series.data, data.yAxis, data.xAxis]);
 
    const resultData = React.useMemo(() => {
        const userPointerMoveHandler = data.chart?.events?.pointermove;
        const pointerMoveHandler: PointerMoveHandler = (pointerMoveData, event) => {
            if (!isEmpty(pointerMoveData?.hovered)) {
                setTooltipVisible(true);
                tooltipRef.current?.redraw(pointerMoveData);
            }
 
            userPointerMoveHandler?.(pointerMoveData, event);
        };
 
        return {
            defaultState: {
                hoveredPosition: {x: 0, y: 0},
            },
            ...data,
            chart: {
                ...data.chart,
                events: {
                    ...data.chart?.events,
                    pointermove: pointerMoveHandler,
                },
            },
            tooltip: {
                ...data.tooltip,
                enabled: false,
            },
        } satisfies ChartData;
    }, [data]);
 
    return (
        <SplitTooltip
            layout={layout}
            resizerVisible={tooltipVisible}
            onMainPaneSizeChange={() => {
                chartRef.current?.reflow().catch(onReflowError);
            }}
            renderMainPane={() => (
                <ChartComponent {...restProps} ref={chartRef} data={resultData} />
            )}
            renderTooltip={() => (
                <TooltipContent
                    ref={tooltipRef}
                    renderer={resultData.tooltip.renderer}
                    headerFormat={headerFormat}
                    valueFormat={resultData.tooltip.valueFormat}
                    rowRenderer={resultData.tooltip.rowRenderer}
                    totals={resultData.tooltip.totals}
                    rows={resultData.tooltip.rows}
                />
            )}
        />
    );
};
 
export function withSplitPane(ChartComponent: typeof Chart) {
    const componentName = getComponentName(ChartComponent);
    const component = React.forwardRef<ChartRef, ChartProps & WithSplitPaneProps>(
        function WithSplitPaneComponent(props, ref) {
            return (
                <SplitPaneContent ChartComponent={ChartComponent} forwardedRef={ref} {...props} />
            );
        },
    );
 
    component.displayName = `withSplitPane(${componentName})`;
 
    return component;
}