UNPKG

react-financial-charts

Version:
377 lines 13.9 kB
import * as PropTypes from "prop-types"; import * as React from "react"; import { functor, identity, isDefined, isNotDefined, noop, } from "./utils"; const aliases = { mouseleave: "mousemove", panend: "pan", pinchzoom: "pan", mousedown: "mousemove", click: "mousemove", contextmenu: "mousemove", dblclick: "mousemove", dragstart: "drag", dragend: "drag", dragcancel: "drag", }; class GenericComponent extends React.Component { constructor(props, context) { super(props, context); this.drawOnCanvas = this.drawOnCanvas.bind(this); this.getMoreProps = this.getMoreProps.bind(this); this.listener = this.listener.bind(this); this.draw = this.draw.bind(this); this.updateMoreProps = this.updateMoreProps.bind(this); this.evaluateType = this.evaluateType.bind(this); this.isHover = this.isHover.bind(this); this.preCanvasDraw = this.preCanvasDraw.bind(this); this.postCanvasDraw = this.postCanvasDraw.bind(this); this.getPanConditions = this.getPanConditions.bind(this); this.shouldTypeProceed = this.shouldTypeProceed.bind(this); this.preEvaluate = this.preEvaluate.bind(this); const { generateSubscriptionId } = context; this.suscriberId = generateSubscriptionId(); this.moreProps = {}; this.state = { updateCount: 0, }; } updateMoreProps(moreProps) { Object.keys(moreProps).forEach((key) => { this.moreProps[key] = moreProps[key]; }); } shouldTypeProceed(type, moreProps) { return true; } preEvaluate() { /// empty } listener(type, moreProps, state, e) { if (isDefined(moreProps)) { this.updateMoreProps(moreProps); } this.evaluationInProgress = true; this.evaluateType(type, e); this.evaluationInProgress = false; } evaluateType(type, e) { const newType = aliases[type] || type; const proceed = this.props.drawOn.indexOf(newType) > -1; if (!proceed) { return; } // @ts-ignore this.preEvaluate(type, this.moreProps, e); if (!this.shouldTypeProceed(type, this.moreProps)) { return; } switch (type) { case "zoom": case "mouseenter": // DO NOT DRAW FOR THESE EVENTS break; case "mouseleave": { this.moreProps.hovering = false; const moreProps = this.getMoreProps(); if (this.props.onUnHover) { this.props.onUnHover(moreProps, e); } break; } case "contextmenu": { if (this.props.onContextMenu) { this.props.onContextMenu(this.getMoreProps(), e); } if (this.moreProps.hovering && this.props.onContextMenuWhenHover) { this.props.onContextMenuWhenHover(this.getMoreProps(), e); } break; } case "mousedown": { if (this.props.onMouseDown) { this.props.onMouseDown(this.getMoreProps(), e); } break; } case "click": { const moreProps = this.getMoreProps(); if (this.moreProps.hovering) { this.props.onClickWhenHover(moreProps, e); } else { this.props.onClickOutside(moreProps, e); } if (this.props.onClick) { this.props.onClick(moreProps, e); } break; } case "mousemove": { const prevHover = this.moreProps.hovering; this.moreProps.hovering = this.isHover(e); const { amIOnTop, setCursorClass } = this.context; if (this.moreProps.hovering && !this.props.selected /* && !prevHover */ && amIOnTop(this.suscriberId) && isDefined(this.props.onHover)) { setCursorClass("react-financial-charts-pointer-cursor"); this.iSetTheCursorClass = true; } else if (this.moreProps.hovering && this.props.selected && amIOnTop(this.suscriberId)) { setCursorClass(this.props.interactiveCursorClass); this.iSetTheCursorClass = true; } else if (prevHover && !this.moreProps.hovering && this.iSetTheCursorClass) { this.iSetTheCursorClass = false; setCursorClass(null); } const moreProps = this.getMoreProps(); if (this.moreProps.hovering && !prevHover) { if (this.props.onHover) { this.props.onHover(moreProps, e); } } if (prevHover && !this.moreProps.hovering) { if (this.props.onUnHover) { this.props.onUnHover(moreProps, e); } } if (this.props.onMouseMove) { this.props.onMouseMove(moreProps, e); } break; } case "dblclick": { const moreProps = this.getMoreProps(); if (this.props.onDoubleClick) { this.props.onDoubleClick(moreProps, e); } if (this.moreProps.hovering && this.props.onDoubleClickWhenHover) { this.props.onDoubleClickWhenHover(moreProps, e); } break; } case "pan": { this.moreProps.hovering = false; if (this.props.onPan) { this.props.onPan(this.getMoreProps(), e); } break; } case "panend": { if (this.props.onPanEnd) { this.props.onPanEnd(this.getMoreProps(), e); } break; } case "dragstart": { if (this.getPanConditions().draggable) { const { amIOnTop } = this.context; if (amIOnTop(this.suscriberId)) { this.dragInProgress = true; this.props.onDragStart(this.getMoreProps(), e); } } break; } case "drag": { if (this.dragInProgress && this.props.onDrag) { this.props.onDrag(this.getMoreProps(), e); } break; } case "dragend": { if (this.dragInProgress && this.props.onDragComplete) { this.props.onDragComplete(this.getMoreProps(), e); } this.dragInProgress = false; break; } case "dragcancel": { if (this.dragInProgress || this.iSetTheCursorClass) { const { setCursorClass } = this.context; setCursorClass(null); } break; } } } isHover(e) { return isDefined(this.props.isHover) ? this.props.isHover(this.getMoreProps(), e) : false; } getPanConditions() { const draggable = (!!(this.props.selected && this.moreProps.hovering) || (this.props.enableDragOnHover && this.moreProps.hovering)); return { draggable, panEnabled: !this.props.disablePan, }; } // @ts-ignore draw({ trigger, force } = { force: false }) { const type = aliases[trigger] || trigger; const proceed = this.props.drawOn.indexOf(type) > -1; if (proceed || this.props.selected /* this is to draw as soon as you select */ || force) { const { chartCanvasType } = this.context; const { canvasDraw } = this.props; if (isNotDefined(canvasDraw) || chartCanvasType === "svg") { const { updateCount } = this.state; this.setState({ updateCount: updateCount + 1, }); } else { this.drawOnCanvas(); } } } UNSAFE_componentWillMount() { const { subscribe, chartId } = this.context; const { clip, edgeClip } = this.props; subscribe(this.suscriberId, { chartId, clip, edgeClip, listener: this.listener, draw: this.draw, getPanConditions: this.getPanConditions, }); this.UNSAFE_componentWillReceiveProps(this.props, this.context); } componentWillUnmount() { const { unsubscribe } = this.context; unsubscribe(this.suscriberId); if (this.iSetTheCursorClass) { const { setCursorClass } = this.context; setCursorClass(null); } } componentDidMount() { this.componentDidUpdate(this.props); } componentDidUpdate(prevProps) { const { chartCanvasType } = this.context; const { canvasDraw, selected, interactiveCursorClass } = this.props; if (prevProps.selected !== selected) { const { setCursorClass } = this.context; if (selected && this.moreProps.hovering) { this.iSetTheCursorClass = true; setCursorClass(interactiveCursorClass); } else { this.iSetTheCursorClass = false; setCursorClass(null); } } if (isDefined(canvasDraw) && !this.evaluationInProgress && chartCanvasType !== "svg") { this.updateMoreProps(this.moreProps); this.drawOnCanvas(); } } UNSAFE_componentWillReceiveProps(nextProps, nextContext) { const { xScale, plotData, chartConfig, getMutableState } = nextContext; this.props.debug(nextContext); this.moreProps = Object.assign(Object.assign(Object.assign({}, this.moreProps), getMutableState()), { /* ^ this is so mouseXY, currentCharts, currentItem are available to newly created components like MouseHoverText which is created right after a new interactive object is drawn */ xScale, plotData, chartConfig }); } getMoreProps() { const { xScale, plotData, chartConfig, morePropsDecorator, xAccessor, displayXAccessor, width, height, } = this.context; const { chartId, fullData } = this.context; const moreProps = Object.assign({ xScale, plotData, chartConfig, xAccessor, displayXAccessor, width, height, chartId, fullData }, this.moreProps); return (morePropsDecorator || identity)(moreProps); } preCanvasDraw(ctx, moreProps) { // do nothing } postCanvasDraw(ctx, moreProps) { // empty } drawOnCanvas() { const { canvasDraw, canvasToDraw } = this.props; const { getCanvasContexts } = this.context; const moreProps = this.getMoreProps(); const ctx = canvasToDraw(getCanvasContexts()); this.preCanvasDraw(ctx, moreProps); canvasDraw(ctx, moreProps); this.postCanvasDraw(ctx, moreProps); } render() { const { chartCanvasType, chartId } = this.context; const { canvasDraw, clip, svgDraw } = this.props; if (isDefined(canvasDraw) && chartCanvasType !== "svg") { return null; } const suffix = isDefined(chartId) ? "-" + chartId : ""; const style = clip ? { clipPath: `url(#chart-area-clip${suffix})` } : undefined; return (React.createElement("g", { style: style }, svgDraw(this.getMoreProps()))); } } GenericComponent.defaultProps = { svgDraw: functor(null), draw: [], canvasToDraw: (contexts) => contexts.mouseCoord, clip: true, edgeClip: false, selected: false, disablePan: false, enableDragOnHover: false, onClickWhenHover: noop, onClickOutside: noop, onDragStart: noop, onMouseMove: noop, onMouseDown: noop, debug: noop, }; GenericComponent.contextTypes = { width: PropTypes.number.isRequired, height: PropTypes.number.isRequired, margin: PropTypes.object.isRequired, chartId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), getCanvasContexts: PropTypes.func, chartCanvasType: PropTypes.string, 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, }; export default GenericComponent; export function getAxisCanvas(contexts) { return contexts.axes; } export function getMouseCanvas(contexts) { return contexts.mouseCoord; } //# sourceMappingURL=GenericComponent.js.map