From bec345b62153ecdf5c01380a8e29ad7398daaafd Mon Sep 17 00:00:00 2001 From: Nathan Knight Date: Fri, 8 Jul 2022 11:45:39 -0400 Subject: [PATCH] feat: Migrated to new React context API --- packages/annotations/src/Label.tsx | 10 +- packages/axes/src/XAxis.tsx | 8 +- packages/axes/src/YAxis.tsx | 9 +- packages/coordinates/src/CrossHairCursor.tsx | 7 +- packages/coordinates/src/Cursor.tsx | 9 +- packages/core/package.json | 4 +- packages/core/src/Chart.tsx | 174 ++++++------- packages/core/src/ChartCanvas.tsx | 229 +++++++++--------- packages/core/src/GenericChartComponent.tsx | 28 +-- packages/core/src/GenericComponent.tsx | 45 +--- packages/core/src/index.ts | 2 +- packages/core/src/utils/ChartDataUtil.ts | 209 ++++++++-------- packages/interactive/src/InteractiveText.tsx | 10 +- .../src/InteractiveYCoordinate.tsx | 10 +- packages/interactive/src/ZoomButtons.tsx | 11 +- packages/series/src/AlternateDataSeries.tsx | 31 +-- packages/tooltip/src/HoverTooltip.tsx | 8 +- 17 files changed, 368 insertions(+), 436 deletions(-) diff --git a/packages/annotations/src/Label.tsx b/packages/annotations/src/Label.tsx index 98f226aa0..41b50967e 100644 --- a/packages/annotations/src/Label.tsx +++ b/packages/annotations/src/Label.tsx @@ -1,6 +1,5 @@ -import { GenericComponent, functor } from "@react-financial-charts/core"; +import { GenericComponent, functor, ChartCanvasContext } from "@react-financial-charts/core"; import { ScaleContinuousNumeric } from "d3-scale"; -import * as PropTypes from "prop-types"; import * as React from "react"; export interface LabelProps { @@ -33,12 +32,7 @@ export class Label extends React.Component { selectCanvas: (canvases: any) => canvases.bg, }; - public static contextTypes = { - canvasOriginX: PropTypes.number, - canvasOriginY: PropTypes.number, - margin: PropTypes.object.isRequired, - ratio: PropTypes.number.isRequired, - }; + public static contextType = ChartCanvasContext; public render() { const { selectCanvas } = this.props; diff --git a/packages/axes/src/XAxis.tsx b/packages/axes/src/XAxis.tsx index 6d84349b6..550ab78e9 100644 --- a/packages/axes/src/XAxis.tsx +++ b/packages/axes/src/XAxis.tsx @@ -1,5 +1,4 @@ -import { strokeDashTypes } from "@react-financial-charts/core"; -import * as PropTypes from "prop-types"; +import { ChartContext, strokeDashTypes } from "@react-financial-charts/core"; import * as React from "react"; import { Axis } from "./Axis"; @@ -67,10 +66,7 @@ export class XAxis extends React.Component { zoomCursorClassName: "react-financial-charts-ns-resize-cursor", }; - public static contextTypes = { - yAxisZoom: PropTypes.func.isRequired, - chartId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, - chartConfig: PropTypes.object.isRequired, - }; + public static contextType = ChartContext; public render() { const { diff --git a/packages/coordinates/src/CrossHairCursor.tsx b/packages/coordinates/src/CrossHairCursor.tsx index 6fa91fb86..58dcd7af8 100644 --- a/packages/coordinates/src/CrossHairCursor.tsx +++ b/packages/coordinates/src/CrossHairCursor.tsx @@ -1,10 +1,10 @@ -import * as PropTypes from "prop-types"; import * as React from "react"; import { getStrokeDasharrayCanvas, strokeDashTypes, GenericComponent, getMouseCanvas, + ChartCanvasContext, } from "@react-financial-charts/core"; const defaultCustomX = (props: CrossHairCursorProps, moreProps: any) => { @@ -31,10 +31,7 @@ export class CrossHairCursor extends React.Component { strokeWidth: 1, }; - public static contextTypes = { - margin: PropTypes.object.isRequired, - ratio: PropTypes.number.isRequired, - }; + public static contextType = ChartCanvasContext; public render() { return ( diff --git a/packages/coordinates/src/Cursor.tsx b/packages/coordinates/src/Cursor.tsx index 1f0cb32e5..2b93fcae1 100644 --- a/packages/coordinates/src/Cursor.tsx +++ b/packages/coordinates/src/Cursor.tsx @@ -1,12 +1,12 @@ import { + ChartCanvasContext, first, - getStrokeDasharrayCanvas, GenericComponent, getMouseCanvas, + getStrokeDasharrayCanvas, last, strokeDashTypes, } from "@react-financial-charts/core"; -import * as PropTypes from "prop-types"; import * as React from "react"; export interface CursorProps { @@ -39,10 +39,7 @@ export class Cursor extends React.Component { xCursorShapeStrokeStyle: "rgba(0, 0, 0, 0.5)", }; - public static contextTypes = { - margin: PropTypes.object.isRequired, - ratio: PropTypes.number.isRequired, - }; + public static contextType = ChartCanvasContext; public render() { return ( diff --git a/packages/core/package.json b/packages/core/package.json index 164e399a4..a6a83dda1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -48,7 +48,7 @@ "prop-types": "^15.7.2" }, "peerDependencies": { - "react": "^16.0.0 || ^17.0.0", - "react-dom": "^16.0.0 || ^17.0.0" + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" } } diff --git a/packages/core/src/Chart.tsx b/packages/core/src/Chart.tsx index 103188787..130ba2432 100644 --- a/packages/core/src/Chart.tsx +++ b/packages/core/src/Chart.tsx @@ -1,7 +1,17 @@ -import { scaleLinear, ScaleContinuousNumeric } from "d3-scale"; -import * as PropTypes from "prop-types"; +import { ScaleContinuousNumeric, scaleLinear } from "d3-scale"; import * as React from "react"; -import { PureComponent } from "./utils"; +import { ChartCanvasContext, chartCanvasContextDefaultValue, ChartCanvasContextType } from "./ChartCanvas"; +import { ChartConfig } from "./utils/ChartDataUtil"; + +export type ChartContextType = Omit, "chartConfig"> & { + chartConfig: ChartConfig; +}; +export const ChartContext = React.createContext({ + ...chartCanvasContextDefaultValue, + // @ts-ignore + chartConfig: {}, + chartId: 0, +}); export interface ChartProps { readonly flipYScale?: boolean; @@ -24,91 +34,89 @@ export interface ChartProps { readonly yScale?: ScaleContinuousNumeric; } -export class Chart extends PureComponent { - public static defaultProps = { - flipYScale: false, - id: 0, - origin: [0, 0], - padding: 0, - yPan: true, - yPanEnabled: false, - yScale: scaleLinear(), - }; - - public static contextTypes = { - chartConfig: PropTypes.array, - subscribe: PropTypes.func.isRequired, - unsubscribe: PropTypes.func.isRequired, - }; +export const Chart = React.memo((props: React.PropsWithChildren) => { + const { + // flipYScale = false, + id = 0, + // origin = [0, 0], + // padding = 0, + // yPan = true, + // yPanEnabled = false, + // yScale = scaleLinear(), + onContextMenu, + onDoubleClick, + } = props; + + const chartCanvasContextValue = React.useContext(ChartCanvasContext); + const { subscribe, unsubscribe, chartConfig } = chartCanvasContextValue; + + const listener = React.useCallback( + (type: string, moreProps: any, _: any, e: React.MouseEvent) => { + switch (type) { + case "contextmenu": { + if (onContextMenu === undefined) { + return; + } + + const { currentCharts } = moreProps; + if (currentCharts.indexOf(id) > -1) { + onContextMenu(e, moreProps); + } + + break; + } + case "dblclick": { + if (onDoubleClick === undefined) { + return; + } - public static childContextTypes = { - chartConfig: PropTypes.object.isRequired, - chartId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, - }; + const { currentCharts } = moreProps; + if (currentCharts.indexOf(id) > -1) { + onDoubleClick(e, moreProps); + } - public componentDidMount() { - const { id } = this.props; - const { subscribe } = this.context; + break; + } + } + }, + [onContextMenu, onDoubleClick, id], + ); + React.useEffect(() => { subscribe(`chart_${id}`, { - listener: this.listener, + listener, }); - } - - public componentWillUnmount() { - const { id } = this.props; - const { unsubscribe } = this.context; - - unsubscribe(`chart_${id}`); - } - - public getChildContext() { - const { id: chartId } = this.props; - - const chartConfig = this.context.chartConfig.find(({ id }: any) => id === chartId); + return () => unsubscribe(`chart_${id}`); + }, [subscribe, unsubscribe, id, listener]); + const config = chartConfig.find(({ id }) => id === props.id)!; + const contextValue = React.useMemo(() => { return { - chartId, - chartConfig, + ...chartCanvasContextValue, + chartId: id, + chartConfig: config, }; - } - - public render() { - const { origin } = this.context.chartConfig.find(({ id }: any) => id === this.props.id); - - const [x, y] = origin; - - return {this.props.children}; - } - - private readonly listener = (type: string, moreProps: any, _: any, e: React.MouseEvent) => { - const { id, onContextMenu, onDoubleClick } = this.props; - - switch (type) { - case "contextmenu": { - if (onContextMenu === undefined) { - return; - } - - const { currentCharts } = moreProps; - if (currentCharts.indexOf(id) > -1) { - onContextMenu(e, moreProps); - } - - break; - } - case "dblclick": { - if (onDoubleClick === undefined) { - return; - } - - const { currentCharts } = moreProps; - if (currentCharts.indexOf(id) > -1) { - onDoubleClick(e, moreProps); - } - - break; - } - } - }; -} + }, [id, config, chartCanvasContextValue]); + + const { + origin: [x, y], + } = config; + + return ( + + {props.children} + + ); +}); + +export const ChartDefaultConfig = { + flipYScale: false, + id: 0, + origin: [0, 0], + padding: 0, + yPan: true, + yPanEnabled: false, + yScale: scaleLinear(), +}; + +Chart.displayName = "Chart"; diff --git a/packages/core/src/ChartCanvas.tsx b/packages/core/src/ChartCanvas.tsx index 08ca64e39..f7b2a20ac 100644 --- a/packages/core/src/ChartCanvas.tsx +++ b/packages/core/src/ChartCanvas.tsx @@ -1,10 +1,10 @@ import { extent as d3Extent, max, min } from "d3-array"; import { ScaleContinuousNumeric, ScaleTime } from "d3-scale"; -import * as PropTypes from "prop-types"; import * as React from "react"; import { clearCanvas, functor, head, identity, isDefined, isNotDefined, last, shallowEqual } from "./utils"; -import { mouseBasedZoomAnchor, IZoomAnchorOptions } from "./zoom/zoomBehavior"; +import { IZoomAnchorOptions, mouseBasedZoomAnchor } from "./zoom/zoomBehavior"; import { + ChartConfig, getChartConfigWithUpdatedYScales, getCurrentCharts, getCurrentItem, @@ -67,6 +67,51 @@ const getCursorStyle = () => { return ; }; +export interface ChartCanvasContextType { + width: number; + height: number; + margin: {}; + chartId: number | string; + getCanvasContexts?: () => void; + xScale: Function; + // Not sure if it should be optional + xAccessor: (data: any) => TXAxis; + displayXAccessor: (data: any) => TXAxis; + plotData: any[]; + fullData: any[]; + chartConfig: ChartConfig[]; + morePropsDecorator?: () => void; + generateSubscriptionId?: () => void; + getMutableState: () => {}; + amIOnTop: (id: string) => boolean; + subscribe: (id: string, rest: any) => void; + unsubscribe: (id: string) => void; + setCursorClass: (className: string) => void; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-function +const noop = () => {}; +export const chartCanvasContextDefaultValue: ChartCanvasContextType = { + amIOnTop: () => false, + chartConfig: [], + chartId: 0, + displayXAccessor: () => 0, + fullData: [], + getMutableState: () => ({}), + height: 0, + margin: {}, + plotData: [], + setCursorClass: noop, + subscribe: noop, + unsubscribe: noop, + width: 0, + xAccessor: () => 0, + xScale: noop, +}; +export const ChartCanvasContext = React.createContext>( + chartCanvasContextDefaultValue, +); + const getDimensions = (props: ChartCanvasProps) => { const { margin, height, width } = props; return { @@ -343,10 +388,10 @@ export interface ChartCanvasProps { } interface ChartCanvasState { - xAccessor?: (data: any) => TXAxis; + xAccessor: (data: any) => TXAxis; displayXAccessor?: any; filterData?: any; - chartConfig?: any; + chartConfig: ChartConfig[]; plotData: any[]; xScale: ScaleContinuousNumeric | ScaleTime; } @@ -378,50 +423,6 @@ export class ChartCanvas extends React.Component< zoomMultiplier: 1.1, }; - public static childContextTypes = { - plotData: PropTypes.array, - fullData: PropTypes.array, - chartConfig: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, - origin: PropTypes.arrayOf(PropTypes.number).isRequired, - padding: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.shape({ - top: PropTypes.number, - bottom: PropTypes.number, - }), - ]), - yExtents: PropTypes.arrayOf(PropTypes.func), - yExtentsProvider: PropTypes.func, - yScale: PropTypes.func.isRequired, - mouseCoordinates: PropTypes.shape({ - at: PropTypes.string, - format: PropTypes.func, - }), - width: PropTypes.number.isRequired, - height: PropTypes.number.isRequired, - }), - ).isRequired, - xScale: PropTypes.func.isRequired, - xAccessor: PropTypes.func.isRequired, - displayXAccessor: PropTypes.func.isRequired, - width: PropTypes.number.isRequired, - height: PropTypes.number.isRequired, - margin: PropTypes.object.isRequired, - ratio: PropTypes.number.isRequired, - getCanvasContexts: PropTypes.func, - xAxisZoom: PropTypes.func, - yAxisZoom: PropTypes.func, - amIOnTop: PropTypes.func, - redraw: PropTypes.func, - subscribe: PropTypes.func, - unsubscribe: PropTypes.func, - setCursorClass: PropTypes.func, - generateSubscriptionId: PropTypes.func, - getMutableState: PropTypes.func, - }; - private readonly canvasContainerRef = React.createRef(); private readonly eventCaptureRef = React.createRef(); private finalPinch?: boolean; @@ -635,6 +636,7 @@ export class ChartCanvas extends React.Component< plotData, mouseXY, currentItem, + xAccessor, }; }; @@ -1116,9 +1118,10 @@ export class ChartCanvas extends React.Component< this.triggerEvent("dblclick", {}, e); }; - public getChildContext() { + public getContextValues() { const dimensions = getDimensions(this.props); return { + chartId: -1, fullData: this.fullData, plotData: this.state.plotData, width: dimensions.width, @@ -1235,72 +1238,74 @@ export class ChartCanvas extends React.Component< const cursor = getCursorStyle(); return ( -
- - +
- {cursor} - - - - - {chartConfig.map((each: any, idx: number) => ( - - + + + {cursor} + + + - ))} - - - - - {this.props.children} - - -
+ {chartConfig.map((each: any, idx: number) => ( + + + + ))} + + + + + {this.props.children} + + +
+ ); } } diff --git a/packages/core/src/GenericChartComponent.tsx b/packages/core/src/GenericChartComponent.tsx index e0f609543..fb0108cb2 100644 --- a/packages/core/src/GenericChartComponent.tsx +++ b/packages/core/src/GenericChartComponent.tsx @@ -1,36 +1,12 @@ -import * as PropTypes from "prop-types"; - import { GenericComponent } from "./GenericComponent"; import { isDefined } from "./utils"; +import { ChartContext } from "./Chart"; const ALWAYS_TRUE_TYPES = ["drag", "dragend"]; export class GenericChartComponent extends GenericComponent { public static defaultProps = GenericComponent.defaultProps; - - public static contextTypes = { - width: PropTypes.number.isRequired, - height: PropTypes.number.isRequired, - margin: PropTypes.object.isRequired, - chartId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - getCanvasContexts: PropTypes.func, - xScale: PropTypes.func.isRequired, - xAccessor: PropTypes.func.isRequired, - displayXAccessor: PropTypes.func.isRequired, - plotData: PropTypes.array.isRequired, - fullData: PropTypes.array.isRequired, - chartConfig: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired, - morePropsDecorator: PropTypes.func, - generateSubscriptionId: PropTypes.func, - getMutableState: PropTypes.func.isRequired, - amIOnTop: PropTypes.func.isRequired, - subscribe: PropTypes.func.isRequired, - unsubscribe: PropTypes.func.isRequired, - setCursorClass: PropTypes.func.isRequired, - canvasOriginX: PropTypes.number, - canvasOriginY: PropTypes.number, - ratio: PropTypes.number.isRequired, - }; + public static contextType = ChartContext; public constructor(props: any, context: any) { super(props, context); diff --git a/packages/core/src/GenericComponent.tsx b/packages/core/src/GenericComponent.tsx index 385429bf5..ba9db2ac2 100644 --- a/packages/core/src/GenericComponent.tsx +++ b/packages/core/src/GenericComponent.tsx @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import * as PropTypes from "prop-types"; import * as React from "react"; import { functor, identity } from "./utils"; import { ICanvasContexts } from "./CanvasContainer"; +import { ChartCanvasContext } from "./ChartCanvas"; -const aliases = { +const aliases: Record = { mouseleave: "mousemove", // to draw interactive after mouse exit panend: "pan", pinchzoom: "pan", @@ -64,33 +64,12 @@ export class GenericComponent extends React.Component -1; @@ -336,7 +313,7 @@ export class GenericComponent extends React.Component { return contexts.axes; }; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index b9af656cc..ff52e78f2 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,4 +1,4 @@ -export { ChartCanvas } from "./ChartCanvas"; +export { ChartCanvas, ChartCanvasContext } from "./ChartCanvas"; export * from "./Chart"; export * from "./GenericChartComponent"; export * from "./GenericComponent"; diff --git a/packages/core/src/utils/ChartDataUtil.ts b/packages/core/src/utils/ChartDataUtil.ts index c62ea6ddc..46a5a46e3 100644 --- a/packages/core/src/utils/ChartDataUtil.ts +++ b/packages/core/src/utils/ChartDataUtil.ts @@ -3,19 +3,33 @@ import { ScaleContinuousNumeric, ScaleTime } from "d3-scale"; import flattenDeep from "lodash.flattendeep"; import * as React from "react"; -import { Chart, ChartProps } from "../Chart"; - -import { - functor, - getClosestItem, - isDefined, - isNotDefined, - isObject, - last, - mapObject, - shallowEqual, - zipper, -} from "./index"; +import { ChartDefaultConfig, ChartProps } from "../Chart"; + +import { functor, getClosestItem, isNotDefined, isObject, last, mapObject, shallowEqual, zipper } from "./index"; + +export interface ChartConfig { + id: number | string; + // readonly origin: number[] | ((width: number, height: number) => number[]); + readonly origin: number[]; + readonly padding: number | { top: number; bottom: number }; + readonly originalYExtentsProp?: number[] | ((data: any) => number) | ((data: any) => number[]); + readonly yExtents?: number[] | ((data: any) => number) | ((data: any) => number[]); + readonly yExtentsCalculator?: (options: { + plotData: any[]; + xDomain: any; + xAccessor: any; + displayXAccessor: any; + fullData: any[]; + }) => number[]; + readonly flipYScale?: boolean; + readonly yScale: ScaleContinuousNumeric; + readonly yPan: boolean; + readonly yPanEnabled: boolean; + readonly realYDomain?: number[]; + readonly width: number; + readonly height: number; + mouseCoordinates?: { at: string; format: () => unknown }; +} export function getChartOrigin(origin: any, contextWidth: number, contextHeight: number) { const originCoordinates = typeof origin === "function" ? origin(contextWidth, contextHeight) : origin; @@ -43,7 +57,7 @@ function values(func: any) { }; } -function isArraySize2AndNumber(yExtentsProp: any) { +function isArraySize2AndNumber(yExtentsProp: any): yExtentsProp is [number, number] { if (Array.isArray(yExtentsProp) && yExtentsProp.length === 2) { const [a, b] = yExtentsProp; return typeof a === "number" && typeof b === "number"; @@ -65,75 +79,74 @@ const isChartProps = (props: ChartProps | any | undefined): props is ChartProps return true; }; -export function getNewChartConfig(innerDimension: any, children: any, existingChartConfig: any[] = []) { - return React.Children.map(children, (each) => { - if (each !== undefined && each !== null && isChartProps(each.props)) { - const chartProps = { - ...Chart.defaultProps, - ...each.props, - }; +export function getNewChartConfig(innerDimension: any, children: any, existingChartConfig: ChartConfig[] = []) { + return React.Children.map(children, (each): ChartConfig | undefined => { + if (!each || !isChartProps(each.props)) { + return undefined; + } + const chartProps = { + ...ChartDefaultConfig, + ...(each.props as ChartProps), + }; - const { - id, - origin, - padding, - yExtents: yExtentsProp, - yScale: yScaleProp, - flipYScale, - yExtentsCalculator, - } = chartProps; - - const yScale = yScaleProp.copy(); - const { width, height, availableHeight } = getDimensions(innerDimension, chartProps); - - const { yPan } = chartProps; - let { yPanEnabled } = chartProps; - const yExtents = isDefined(yExtentsProp) - ? (Array.isArray(yExtentsProp) ? yExtentsProp : [yExtentsProp]).map(functor) - : undefined; - - const prevChartConfig = existingChartConfig.find((d) => d.id === id); - - if (isArraySize2AndNumber(yExtentsProp)) { - if ( - isDefined(prevChartConfig) && - prevChartConfig.yPan && - prevChartConfig.yPanEnabled && - yPan && - yPanEnabled && - shallowEqual(prevChartConfig.originalYExtentsProp, yExtentsProp) - ) { - yScale.domain(prevChartConfig.yScale.domain()); - } else { - const [a, b] = yExtentsProp; - yScale.domain([a, b]); - } - } else if (isDefined(prevChartConfig) && prevChartConfig.yPanEnabled) { - if (isArraySize2AndNumber(prevChartConfig.originalYExtentsProp)) { - // do nothing - } else { - yScale.domain(prevChartConfig.yScale.domain()); - yPanEnabled = true; - } + const { + id, + origin, + padding, + yExtents: yExtentsProp, + yScale: yScaleProp = ChartDefaultConfig.yScale, + flipYScale, + yExtentsCalculator, + } = chartProps; + + const yScale = yScaleProp.copy(); + const { width, height, availableHeight } = getDimensions(innerDimension, chartProps); + + const { yPan } = chartProps; + let { yPanEnabled } = chartProps; + const yExtents = yExtentsProp + ? (Array.isArray(yExtentsProp) ? yExtentsProp : [yExtentsProp]).map(functor) + : undefined; + + const prevChartConfig = existingChartConfig.find((d) => d.id === id); + + if (isArraySize2AndNumber(yExtentsProp)) { + if ( + !!prevChartConfig && + prevChartConfig.yPan && + prevChartConfig.yPanEnabled && + yPan && + yPanEnabled && + shallowEqual(prevChartConfig.originalYExtentsProp, yExtentsProp) + ) { + yScale.domain(prevChartConfig.yScale.domain()); + } else { + const [a, b] = yExtentsProp; + yScale.domain([a, b]); + } + } else if (!!prevChartConfig && prevChartConfig.yPanEnabled) { + if (isArraySize2AndNumber(prevChartConfig.originalYExtentsProp)) { + // do nothing + } else { + yScale.domain(prevChartConfig.yScale.domain()); + yPanEnabled = true; } - - return { - id, - origin: functor(origin)(width, availableHeight), - padding, - originalYExtentsProp: yExtentsProp, - yExtents, - yExtentsCalculator, - flipYScale, - yScale, - yPan, - yPanEnabled, - width, - height, - }; } - return undefined; + return { + id, + origin: functor(origin)(width, availableHeight), + padding, + originalYExtentsProp: yExtentsProp, + yExtents, + yExtentsCalculator, + flipYScale, + yScale, + yPan, + yPanEnabled, + width, + height, + }; }).filter((each: any) => each !== undefined); } @@ -173,20 +186,20 @@ function yDomainFromYExtents(yExtents: any, yScale: any, plotData: any[]) { const allYValues: number[] = flattenDeep(yValues); - const realYDomain = yScale.invert ? extent(allYValues) : [...new Set(allYValues).values()]; + const realYDomain = yScale.invert ? (extent(allYValues) as [number, number]) : [...new Set(allYValues).values()]; return realYDomain; } export function getChartConfigWithUpdatedYScales( - chartConfig: any, + chartConfig: ChartConfig[], { plotData, xAccessor, displayXAccessor, fullData }: any, xDomain: any, dy?: number, - chartsToPan?: string[], -) { - const yDomains = chartConfig.map(({ yExtentsCalculator, yExtents, yScale }: any) => { - const realYDomain = isDefined(yExtentsCalculator) + chartsToPan?: (string | number)[], +): ChartConfig[] { + const yDomains = chartConfig.map(({ yExtentsCalculator, yExtents, yScale }) => { + const realYDomain = yExtentsCalculator ? yExtentsCalculator({ plotData, xDomain, xAccessor, displayXAccessor, fullData }) : yDomainFromYExtents(yExtents, yScale, plotData); @@ -204,22 +217,24 @@ export function getChartConfigWithUpdatedYScales( }; }); - const combine = zipper().combine((config: any, { realYDomain, yDomainDY, prevYDomain }: any) => { - const { id, padding, height, yScale, yPan, flipYScale, yPanEnabled = false } = config; + const combine = zipper().combine( + (config: ChartConfig, { realYDomain, yDomainDY, prevYDomain }: typeof yDomains[number]): ChartConfig => { + const { id, padding, height, yScale, yPan, flipYScale, yPanEnabled = false } = config; - const another = chartsToPan !== undefined ? chartsToPan.indexOf(id) > -1 : true; - const domain = yPan && yPanEnabled ? (another ? yDomainDY : prevYDomain) : realYDomain; + const another = chartsToPan !== undefined ? chartsToPan.indexOf(id) > -1 : true; + const domain = yPan && yPanEnabled ? (another ? yDomainDY : prevYDomain) : realYDomain; - const newYScale = setRange(yScale.copy().domain(domain), height, padding, flipYScale); + const newYScale = setRange(yScale.copy().domain(domain), height, padding, flipYScale); - return { - ...config, - yScale: newYScale, - realYDomain, - }; - }); + return { + ...config, + yScale: newYScale, + realYDomain, + }; + }, + ); - const updatedChartConfig = combine(chartConfig, yDomains); + const updatedChartConfig = combine(chartConfig, yDomains) as ChartConfig[]; return updatedChartConfig; } diff --git a/packages/interactive/src/InteractiveText.tsx b/packages/interactive/src/InteractiveText.tsx index 5b9364e42..f76fdbb64 100644 --- a/packages/interactive/src/InteractiveText.tsx +++ b/packages/interactive/src/InteractiveText.tsx @@ -1,6 +1,5 @@ -import * as PropTypes from "prop-types"; import * as React from "react"; -import { isDefined, noop, getMouseCanvas, GenericChartComponent } from "@react-financial-charts/core"; +import { ChartContext, GenericChartComponent, getMouseCanvas, isDefined, noop } from "@react-financial-charts/core"; import { HoverTextNearMouse } from "./components"; import { getValueFromOverride, isHoverForInteractiveType, saveNodeType, terminate } from "./utils"; import { EachText } from "./wrapper"; @@ -56,12 +55,7 @@ export class InteractiveText extends React.Component { zoomMultiplier: 1.5, }; - public static contextTypes = { - xScale: PropTypes.func.isRequired, - chartConfig: PropTypes.object.isRequired, - plotData: PropTypes.array.isRequired, - xAccessor: PropTypes.func.isRequired, - xAxisZoom: PropTypes.func.isRequired, - }; + public static contextType = ChartContext; private interval?: number; diff --git a/packages/series/src/AlternateDataSeries.tsx b/packages/series/src/AlternateDataSeries.tsx index f021a8b42..07fc8e647 100644 --- a/packages/series/src/AlternateDataSeries.tsx +++ b/packages/series/src/AlternateDataSeries.tsx @@ -1,36 +1,29 @@ -import * as PropTypes from "prop-types"; import * as React from "react"; +import { ChartCanvasContext } from "@react-financial-charts/core"; export interface AlternateDataSeriesProps { readonly data: TData[]; } -export class AlternateDataSeries extends React.Component> { - public static contextTypes = { - plotData: PropTypes.array, - xAccessor: PropTypes.func.isRequired, - }; - - public static childContextTypes = { - plotData: PropTypes.array, - }; - - public getChildContext() { - const { data } = this.props; - const { plotData, xAccessor } = this.context; +export const AlternateDataSeries = ({ + data, + children, +}: React.PropsWithChildren>) => { + const context = React.useContext(ChartCanvasContext); + const contextValue = React.useMemo(() => { + const { plotData, xAccessor } = context; const startDate = xAccessor(plotData[0]); const endDate = xAccessor(plotData[plotData.length - 1]); return { + ...context, plotData: data.filter((d) => { const date = xAccessor(d); return date > startDate && date < endDate; }), }; - } + }, [data, context]); - public render() { - return this.props.children; - } -} + return {children}; +}; diff --git a/packages/tooltip/src/HoverTooltip.tsx b/packages/tooltip/src/HoverTooltip.tsx index 492ba91e7..8f656a3ff 100644 --- a/packages/tooltip/src/HoverTooltip.tsx +++ b/packages/tooltip/src/HoverTooltip.tsx @@ -1,7 +1,6 @@ import { max, sum } from "d3-array"; -import * as PropTypes from "prop-types"; import * as React from "react"; -import { first, isDefined, GenericComponent, last } from "@react-financial-charts/core"; +import { ChartCanvasContext, first, GenericComponent, isDefined, last } from "@react-financial-charts/core"; const PADDING = 4; const X = 8; @@ -226,10 +225,7 @@ export class HoverTooltip extends React.Component { fontSize: 14, }; - public static contextTypes = { - margin: PropTypes.object.isRequired, - ratio: PropTypes.number.isRequired, - }; + public static contextType = ChartCanvasContext; public render() { return ;