-
Notifications
You must be signed in to change notification settings - Fork 719
/
DataProvider.tsx
107 lines (98 loc) · 3.78 KB
/
DataProvider.tsx
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
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ScaleConfig, ScaleConfigToD3Scale } from '@visx/scale';
import React, { useContext, useMemo } from 'react';
import createOrdinalScale from '@visx/scale/lib/scales/ordinal';
import { AxisScaleOutput } from '@visx/axis';
import { XYChartTheme } from '../types';
import ThemeContext from '../context/ThemeContext';
import DataContext from '../context/DataContext';
import useDataRegistry from '../hooks/useDataRegistry';
import useDimensions, { Dimensions } from '../hooks/useDimensions';
import useScales from '../hooks/useScales';
import isDiscreteScale from '../utils/isDiscreteScale';
/** Props that can be passed to initialize/update the provider config. */
export type DataProviderProps<
XScaleConfig extends ScaleConfig<AxisScaleOutput, any, any>,
YScaleConfig extends ScaleConfig<AxisScaleOutput, any, any>
> = {
/* Optionally define the initial dimensions. */
initialDimensions?: Partial<Dimensions>;
/* Optional chart theme provided by DataProvider, overrides any theme already available in context. */
theme?: XYChartTheme;
/* x-scale configuration whose shape depends on scale type. */
xScale: XScaleConfig;
/* y-scale configuration whose shape depends on scale type. */
yScale: YScaleConfig;
/* Any React children. */
children: React.ReactNode;
/* Determines whether Series will be plotted horizontally (e.g., horizontal bars). By default this will try to be inferred based on scale types. */
horizontal?: boolean | 'auto';
};
export default function DataProvider<
XScaleConfig extends ScaleConfig<AxisScaleOutput>,
YScaleConfig extends ScaleConfig<AxisScaleOutput>,
Datum extends object
>({
initialDimensions,
theme: propsTheme,
xScale: xScaleConfig,
yScale: yScaleConfig,
children,
horizontal: initialHorizontal = 'auto',
}: DataProviderProps<XScaleConfig, YScaleConfig>) {
// `DataProvider` provides a theme so that `ThemeProvider` is not strictly needed.
// `props.theme` takes precedent over `context.theme`, which has a default even if
// a ThemeProvider is not present.
const contextTheme = useContext(ThemeContext);
const theme = propsTheme || contextTheme;
const [{ width, height, margin }, setDimensions] = useDimensions(initialDimensions);
const innerWidth = Math.max(0, width - margin.left - margin.right);
const innerHeight = Math.max(0, height - margin.top - margin.bottom);
type XScale = ScaleConfigToD3Scale<XScaleConfig, AxisScaleOutput, any, any>;
type YScale = ScaleConfigToD3Scale<YScaleConfig, AxisScaleOutput, any, any>;
const dataRegistry = useDataRegistry<XScale, YScale, Datum>();
const { xScale, yScale }: { xScale?: XScale; yScale?: YScale } = useScales({
dataRegistry,
xScaleConfig,
yScaleConfig,
xRange: [margin.left, Math.max(0, width - margin.right)],
yRange: [Math.max(0, height - margin.bottom), margin.top],
});
const registryKeys = dataRegistry.keys();
const colorScale = useMemo(
() =>
createOrdinalScale({
domain: registryKeys,
range: theme.colors,
}),
[registryKeys, theme.colors],
);
const horizontal =
initialHorizontal === 'auto'
? isDiscreteScale(yScaleConfig) || yScaleConfig.type === 'time' || yScaleConfig.type === 'utc'
: initialHorizontal;
return (
<DataContext.Provider
// everything returned here should be memoized between renders
// to avoid child re-renders
value={{
dataRegistry,
registerData: dataRegistry.registerData,
unregisterData: dataRegistry.unregisterData,
xScale,
yScale,
colorScale,
theme,
width,
height,
margin,
innerWidth,
innerHeight,
setDimensions,
horizontal,
}}
>
{children}
</DataContext.Provider>
);
}