UNPKG

victory-chart

Version:
224 lines (207 loc) 8.03 kB
import React, { PropTypes } from "react"; import { partialRight } from "lodash"; import { PropTypes as CustomPropTypes, Helpers, VictoryTransition, VictoryLabel, addEvents, VictoryContainer, VictoryTheme, DefaultTransitions, Candle } from "victory-core"; import CandlestickHelpers from "./helper-methods"; const fallbackProps = { width: 450, height: 300, padding: 50, candleColors: { positive: "#ffffff", negative: "#252525" } }; const defaultData = [ {x: new Date(2016, 6, 1), open: 5, close: 10, high: 15, low: 0}, {x: new Date(2016, 6, 2), open: 10, close: 15, high: 20, low: 5}, {x: new Date(2016, 6, 3), open: 15, close: 20, high: 25, low: 10}, {x: new Date(2016, 6, 4), open: 20, close: 25, high: 30, low: 15}, {x: new Date(2016, 6, 5), open: 25, close: 30, high: 35, low: 20}, {x: new Date(2016, 6, 6), open: 30, close: 35, high: 40, low: 25}, {x: new Date(2016, 6, 7), open: 35, close: 40, high: 45, low: 30}, {x: new Date(2016, 6, 8), open: 40, close: 45, high: 50, low: 35} ]; class VictoryCandlestick extends React.Component { static displayName = "VictoryCandlestick"; static role = "candlestick"; static defaultTransitions = DefaultTransitions.discreteTransitions(); static propTypes = { animate: PropTypes.object, candleColors: PropTypes.shape({ positive: PropTypes.string, negative: PropTypes.string }), categories: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.string), PropTypes.shape({ x: PropTypes.arrayOf(PropTypes.string), y: PropTypes.arrayOf(PropTypes.string) }) ]), close: PropTypes.oneOfType([ PropTypes.func, CustomPropTypes.allOfType([CustomPropTypes.integer, CustomPropTypes.nonNegative]), PropTypes.string, PropTypes.arrayOf(PropTypes.string) ]), containerComponent: PropTypes.element, data: PropTypes.array, domainPadding: PropTypes.oneOfType([ PropTypes.shape({ x: PropTypes.oneOfType([ PropTypes.number, CustomPropTypes.domain ]), y: PropTypes.oneOfType([ PropTypes.number, CustomPropTypes.domain ]) }), PropTypes.number ]), dataComponent: PropTypes.element, domain: PropTypes.oneOfType([ CustomPropTypes.domain, PropTypes.shape({ x: CustomPropTypes.domain, y: CustomPropTypes.domain }) ]), events: PropTypes.arrayOf(PropTypes.shape({ target: PropTypes.oneOf(["data", "labels", "parent"]), eventKey: PropTypes.oneOfType([ PropTypes.array, PropTypes.func, CustomPropTypes.allOfType([CustomPropTypes.integer, CustomPropTypes.nonNegative]), PropTypes.string ]), eventHandlers: PropTypes.object })), eventKey: PropTypes.oneOfType([ PropTypes.func, CustomPropTypes.allOfType([CustomPropTypes.integer, CustomPropTypes.nonNegative]), PropTypes.string ]), groupComponent: PropTypes.element, height: CustomPropTypes.nonNegative, high: PropTypes.oneOfType([ PropTypes.func, CustomPropTypes.allOfType([CustomPropTypes.integer, CustomPropTypes.nonNegative]), PropTypes.string, PropTypes.arrayOf(PropTypes.string) ]), labels: PropTypes.oneOfType([ PropTypes.func, PropTypes.array ]), labelComponent: PropTypes.element, low: PropTypes.oneOfType([ PropTypes.func, CustomPropTypes.allOfType([CustomPropTypes.integer, CustomPropTypes.nonNegative]), PropTypes.string, PropTypes.arrayOf(PropTypes.string) ]), name: PropTypes.string, open: PropTypes.oneOfType([ PropTypes.func, CustomPropTypes.allOfType([CustomPropTypes.integer, CustomPropTypes.nonNegative]), PropTypes.string, PropTypes.arrayOf(PropTypes.string) ]), padding: PropTypes.oneOfType([ PropTypes.number, PropTypes.shape({ top: PropTypes.number, bottom: PropTypes.number, left: PropTypes.number, right: PropTypes.number }) ]), samples: CustomPropTypes.nonNegative, scale: PropTypes.oneOfType([ CustomPropTypes.scale, PropTypes.shape({ x: CustomPropTypes.scale, y: CustomPropTypes.scale }) ]), sharedEvents: PropTypes.shape({ events: PropTypes.array, getEventState: PropTypes.func }), size: PropTypes.oneOfType([ CustomPropTypes.nonNegative, PropTypes.func ]), standalone: PropTypes.bool, style: PropTypes.shape({ parent: PropTypes.object, data: PropTypes.object, labels: PropTypes.object }), theme: PropTypes.object, width: CustomPropTypes.nonNegative, x: PropTypes.oneOfType([ PropTypes.func, CustomPropTypes.allOfType([CustomPropTypes.integer, CustomPropTypes.nonNegative]), PropTypes.string, PropTypes.arrayOf(PropTypes.string) ]) }; static defaultProps = { samples: 50, scale: "linear", data: defaultData, standalone: true, dataComponent: <Candle/>, labelComponent: <VictoryLabel/>, containerComponent: <VictoryContainer/>, groupComponent: <g/>, theme: VictoryTheme.grayscale }; static getDomain = CandlestickHelpers.getDomain.bind(CandlestickHelpers); static getData = CandlestickHelpers.getData.bind(CandlestickHelpers); static getBaseProps = partialRight( CandlestickHelpers.getBaseProps.bind(CandlestickHelpers), fallbackProps); static expectedComponents = [ "dataComponent", "labelComponent", "groupComponent", "containerComponent" ]; renderData(props) { const { dataComponent, labelComponent, groupComponent} = props; const dataComponents = []; const labelComponents = []; for (let index = 0, len = this.dataKeys.length; index < len; index++) { const dataProps = this.getComponentProps(dataComponent, "data", index); dataComponents[index] = React.cloneElement(dataComponent, dataProps); const labelProps = this.getComponentProps(labelComponent, "labels", index); if (labelProps && labelProps.text !== undefined && labelProps.text !== null) { labelComponents[index] = React.cloneElement(labelComponent, labelProps); } } return labelComponents.length > 0 ? React.cloneElement(groupComponent, {}, ...dataComponents, ...labelComponents) : dataComponents; } renderContainer(props, group) { const { containerComponent } = props; const parentProps = this.getComponentProps(containerComponent, "parent", "parent"); return React.cloneElement(containerComponent, parentProps, group); } renderGroup(children, style) { return React.cloneElement( this.props.groupComponent, { role: "presentation", style}, children ); } shouldAnimate() { return !!this.props.animate; } render() { const props = Helpers.modifyProps(this.props, fallbackProps, "candlestick"); const { animate, standalone, style, theme } = props; // If animating, return a `VictoryAnimation` element that will create // a new `VictoryCandlestick` with nearly identical props, except (1) tweened // and (2) `animate` set to null so we don't recurse forever. if (this.shouldAnimate()) { // Do less work by having `VictoryAnimation` tween only values that // make sense to tween. In the future, allow customization of animated // prop whitelist/blacklist? const whitelist = [ "data", "domain", "height", "padding", "samples", "size", "style", "width", "x", "y" ]; return ( <VictoryTransition animate={animate} animationWhitelist={whitelist}> {React.createElement(this.constructor, props)} </VictoryTransition> ); } const styleObject = theme && theme.candlestick && theme.candlestick.style ? theme.candlestick.style : {}; const baseStyle = Helpers.getStyles(style, styleObject, "auto", "100%"); const group = this.renderGroup(this.renderData(props), baseStyle.parent); return standalone ? this.renderContainer(props, group) : group; } } export default addEvents(VictoryCandlestick);