recharts
Version:
React charts
515 lines (450 loc) • 18.6 kB
JavaScript
import _isNil from "lodash/isNil";
import _isString from "lodash/isString";
import _isObject from "lodash/isObject";
import _isFunction from "lodash/isFunction";
import _isArray from "lodash/isArray";
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
import React, { Children } from 'react';
import PropTypes from 'prop-types';
import { isNumber } from './DataUtils';
import { shallowEqual } from './PureRender';
export var PRESENTATION_ATTRIBUTES = {
'aria-current': PropTypes.string,
// state
'aria-details': PropTypes.any,
'aria-disabled': PropTypes.any,
// state
'aria-hidden': PropTypes.any,
// state
'aria-invalid': PropTypes.any,
// state
'aria-keyshortcuts': PropTypes.any,
'aria-label': PropTypes.any,
'aria-roledescription': PropTypes.any,
// Widget Attributes
'aria-autocomplete': PropTypes.any,
'aria-checked': PropTypes.any,
'aria-expanded': PropTypes.any,
'aria-haspopup': PropTypes.any,
'aria-level': PropTypes.any,
'aria-modal': PropTypes.any,
'aria-multiline': PropTypes.any,
'aria-multiselectable': PropTypes.any,
'aria-orientation': PropTypes.any,
'aria-placeholder': PropTypes.any,
'aria-pressed': PropTypes.any,
'aria-readonly': PropTypes.any,
'aria-required': PropTypes.any,
'aria-selected': PropTypes.any,
'aria-sort': PropTypes.any,
'aria-valuemax': PropTypes.any,
'aria-valuemin': PropTypes.any,
'aria-valuenow': PropTypes.any,
'aria-valuetext': PropTypes.any,
// Live Region Attributes
'aria-atomic': PropTypes.any,
'aria-busy': PropTypes.any,
'aria-live': PropTypes.any,
'aria-relevant': PropTypes.any,
// Drag-and-Drop Attributes
'aria-dropeffect': PropTypes.any,
'aria-grabbed': PropTypes.any,
// Relationship Attributes
'aria-activedescendant': PropTypes.any,
'aria-colcount': PropTypes.any,
'aria-colindex': PropTypes.any,
'aria-colspan': PropTypes.any,
'aria-controls': PropTypes.any,
'aria-describedby': PropTypes.any,
'aria-errormessage': PropTypes.any,
'aria-flowto': PropTypes.any,
'aria-labelledby': PropTypes.any,
'aria-owns': PropTypes.any,
'aria-posinset': PropTypes.any,
'aria-rowcount': PropTypes.any,
'aria-rowindex': PropTypes.any,
'aria-rowspan': PropTypes.any,
'aria-setsize': PropTypes.any,
alignmentBaseline: PropTypes.string,
angle: PropTypes.number,
baselineShift: PropTypes.string,
clip: PropTypes.string,
clipPath: PropTypes.string,
clipRule: PropTypes.string,
color: PropTypes.string,
colorInterpolation: PropTypes.string,
colorInterpolationFilters: PropTypes.string,
colorProfile: PropTypes.string,
colorRendering: PropTypes.string,
cursor: PropTypes.string,
direction: PropTypes.oneOf(['ltr', 'rtl', 'inherit']),
display: PropTypes.string,
dominantBaseline: PropTypes.string,
enableBackground: PropTypes.string,
fill: PropTypes.string,
fillOpacity: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
fillRule: PropTypes.oneOf(['nonzero', 'evenodd', 'inherit']),
filter: PropTypes.string,
floodColor: PropTypes.string,
floodOpacity: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
font: PropTypes.string,
fontFamily: PropTypes.string,
fontSize: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
fontSizeAdjust: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
fontStretch: PropTypes.oneOf(['normal', 'wider', 'narrower', 'ultra-condensed', 'extra-condensed', 'condensed', 'semi-condensed', 'semi-expanded', 'expanded', 'extra-expanded', 'ultra-expanded', 'inherit']),
fontStyle: PropTypes.oneOf(['normal', 'italic', 'oblique', 'inherit']),
fontVariant: PropTypes.oneOf(['normal', 'small-caps', 'inherit']),
fontWeight: PropTypes.oneOf(['normal', 'bold', 'bolder', 'lighter', 100, 200, 300, 400, 500, 600, 700, 800, 900, 'inherit']),
glyphOrientationHorizontal: PropTypes.string,
glyphOrientationVertical: PropTypes.string,
imageRendering: PropTypes.oneOf(['auto', 'optimizeSpeed', 'optimizeQuality', 'inherit']),
kerning: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
letterSpacing: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
lightingColor: PropTypes.string,
lineHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
markerEnd: PropTypes.string,
markerMid: PropTypes.string,
markerStart: PropTypes.string,
mask: PropTypes.string,
opacity: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
overflow: PropTypes.oneOf(['visible', 'hidden', 'scroll', 'auto', 'inherit']),
pointerEvents: PropTypes.oneOf(['visiblePainted', 'visibleFill', 'visibleStroke', 'visible', 'painted', 'fill', 'stroke', 'all', 'none', 'inherit']),
shapeRendering: PropTypes.oneOf(['auto', 'optimizeSpeed', 'crispEdges', 'geometricPrecision', 'inherit']),
stopColor: PropTypes.string,
stopOpacity: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
stroke: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
strokeDasharray: PropTypes.string,
strokeDashoffset: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
strokeLinecap: PropTypes.oneOf(['butt', 'round', 'square', 'inherit']),
strokeLinejoin: PropTypes.oneOf(['miter', 'round', 'bevel', 'inherit']),
strokeMiterlimit: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
strokeOpacity: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
strokeWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
textAnchor: PropTypes.oneOf(['start', 'middle', 'end', 'inherit']),
textDecoration: PropTypes.oneOf(['none', 'underline', 'overline', 'line-through', 'blink', 'inherit']),
textRendering: PropTypes.oneOf(['auto', 'optimizeSpeed', 'optimizeLegibility', 'geometricPrecision', 'inherit']),
unicodeBidi: PropTypes.oneOf(['normal', 'embed', 'bidi-override', 'inherit']),
visibility: PropTypes.oneOf(['visible', 'hidden', 'collapse', 'inherit']),
wordSpacing: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
writingMode: PropTypes.oneOf(['lr-tb', 'rl-tb', 'tb-rl', 'lr', 'rl', 'tb', 'inherit']),
transform: PropTypes.string,
role: PropTypes.string,
focusable: PropTypes.string,
tabIndex: PropTypes.string,
style: PropTypes.object,
width: PropTypes.number,
height: PropTypes.number,
dx: PropTypes.number,
dy: PropTypes.number,
x: PropTypes.number,
y: PropTypes.number,
r: PropTypes.number,
// The radius of Rectangle
radius: PropTypes.oneOfType([PropTypes.number, PropTypes.array])
};
export var EVENT_ATTRIBUTES = {
onClick: PropTypes.func,
onMouseDown: PropTypes.func,
onMouseUp: PropTypes.func,
onMouseOver: PropTypes.func,
onMouseMove: PropTypes.func,
onMouseOut: PropTypes.func,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
onTouchEnd: PropTypes.func,
onTouchMove: PropTypes.func,
onTouchStart: PropTypes.func,
onTouchCancel: PropTypes.func
};
var REACT_BROWSER_EVENT_MAP = {
click: 'onClick',
mousedown: 'onMouseDown',
mouseup: 'onMouseUp',
mouseover: 'onMouseOver',
mousemove: 'onMouseMove',
mouseout: 'onMouseOut',
mouseenter: 'onMouseEnter',
mouseleave: 'onMouseLeave',
touchcancel: 'onTouchCancel',
touchend: 'onTouchEnd',
touchmove: 'onTouchMove',
touchstart: 'onTouchStart'
};
export var SCALE_TYPES = ['auto', 'linear', 'pow', 'sqrt', 'log', 'identity', 'time', 'band', 'point', 'ordinal', 'quantile', 'quantize', 'utc', 'sequential', 'threshold'];
export var LEGEND_TYPES = ['plainline', 'line', 'square', 'rect', 'circle', 'cross', 'diamond', 'star', 'triangle', 'wye', 'none'];
export var TOOLTIP_TYPES = ['none'];
/**
* Get the display name of a component
* @param {Object} Comp Specified Component
* @return {String} Display name of Component
*/
export var getDisplayName = function getDisplayName(Comp) {
if (typeof Comp === 'string') {
return Comp;
}
if (!Comp) {
return '';
}
return Comp.displayName || Comp.name || 'Component';
};
/*
* Find and return all matched children by type. `type` can be a React element class or
* string
*/
export var findAllByType = function findAllByType(children, type) {
var result = [];
var types = [];
if (_isArray(type)) {
types = type.map(function (t) {
return getDisplayName(t);
});
} else {
types = [getDisplayName(type)];
}
React.Children.forEach(children, function (child) {
var childType = child && child.type && (child.type.displayName || child.type.name);
if (types.indexOf(childType) !== -1) {
result.push(child);
}
});
return result;
};
/*
* Return the first matched child by type, return null otherwise.
* `type` can be a React element class or string.
*/
export var findChildByType = function findChildByType(children, type) {
var result = findAllByType(children, type);
return result && result[0];
};
/*
* Create a new array of children excluding the ones matched the type
*/
export var withoutType = function withoutType(children, type) {
var newChildren = [];
var types;
if (_isArray(type)) {
types = type.map(function (t) {
return getDisplayName(t);
});
} else {
types = [getDisplayName(type)];
}
React.Children.forEach(children, function (child) {
if (child && child.type && child.type.displayName && types.indexOf(child.type.displayName) !== -1) {
return;
}
newChildren.push(child);
});
return newChildren;
};
/**
* get all the presentation attribute of svg element
* @param {Object} el A react element or the props of a react element
* @return {Object} attributes or null
*/
export var getPresentationAttributes = function getPresentationAttributes(el) {
if (!el || _isFunction(el)) {
return null;
}
var props = React.isValidElement(el) ? el.props : el;
if (!_isObject(props)) {
return null;
}
var out = null; // eslint-disable-next-line no-restricted-syntax
for (var i in props) {
if ({}.hasOwnProperty.call(props, i) && PRESENTATION_ATTRIBUTES[i]) {
if (!out) out = {};
out[i] = props[i];
}
}
return out;
};
var getEventHandlerOfElement = function getEventHandlerOfElement(originalHandler, props) {
return function (e) {
originalHandler(props, e);
return null;
};
};
/**
* get all the event attribute of svg element
* @param {Object} el A react element or the props of a react element
* @param {Function} newHandler New handler of event
* @param {Boolean} wrapCallback Wrap callback and return more parameters or not
* @return {Object} attributes or null
*/
export var filterEventAttributes = function filterEventAttributes(el, newHandler) {
var wrapCallback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
if (!el || _isFunction(el)) {
return null;
}
var props = React.isValidElement(el) ? el.props : el;
if (!_isObject(props)) {
return null;
}
var out = null; // eslint-disable-next-line no-restricted-syntax
for (var i in props) {
if ({}.hasOwnProperty.call(props, i) && EVENT_ATTRIBUTES[i]) {
if (!out) out = {};
out[i] = newHandler || (wrapCallback ? getEventHandlerOfElement(props[i], props) : props[i]);
}
}
return out;
};
var getEventHandlerOfChild = function getEventHandlerOfChild(originalHandler, data, index) {
return function (e) {
originalHandler(data, index, e);
return null;
};
};
export var filterEventsOfChild = function filterEventsOfChild(props, data, index) {
if (!_isObject(props)) {
return null;
}
var out = null; // eslint-disable-next-line no-restricted-syntax
for (var i in props) {
if ({}.hasOwnProperty.call(props, i) && EVENT_ATTRIBUTES[i] && _isFunction(props[i])) {
if (!out) out = {};
out[i] = getEventHandlerOfChild(props[i], data, index);
}
}
return out;
};
/**
* validate the width and height props of a chart element
* @param {Object} el A chart element
* @return {Boolean} true If the props width and height are number, and greater than 0
*/
export var validateWidthHeight = function validateWidthHeight(el) {
if (!el || !el.props) {
return false;
}
var _el$props = el.props,
width = _el$props.width,
height = _el$props.height;
if (!isNumber(width) || width <= 0 || !isNumber(height) || height <= 0) {
return false;
}
return true;
};
export var isSsr = function isSsr() {
return !(typeof window !== 'undefined' && window.document && window.document.createElement && window.setTimeout);
};
var SVG_TAGS = ['a', 'altGlyph', 'altGlyphDef', 'altGlyphItem', 'animate', 'animateColor', 'animateMotion', 'animateTransform', 'circle', 'clipPath', 'color-profile', 'cursor', 'defs', 'desc', 'ellipse', 'feBlend', 'feColormatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence', 'filter', 'font', 'font-face', 'font-face-format', 'font-face-name', 'font-face-url', 'foreignObject', 'g', 'glyph', 'glyphRef', 'hkern', 'image', 'line', 'lineGradient', 'marker', 'mask', 'metadata', 'missing-glyph', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', 'script', 'set', 'stop', 'style', 'svg', 'switch', 'symbol', 'text', 'textPath', 'title', 'tref', 'tspan', 'use', 'view', 'vkern'];
var isSvgElement = function isSvgElement(child) {
return child && child.type && _isString(child.type) && SVG_TAGS.indexOf(child.type) >= 0;
};
/**
* Filter all the svg elements of children
* @param {Array} children The children of a react element
* @return {Array} All the svg elements
*/
export var filterSvgElements = function filterSvgElements(children) {
var svgElements = [];
React.Children.forEach(children, function (entry) {
if (entry && entry.type && _isString(entry.type) && SVG_TAGS.indexOf(entry.type) >= 0) {
svgElements.push(entry);
}
});
return svgElements;
};
export var isSingleChildEqual = function isSingleChildEqual(nextChild, prevChild) {
if (_isNil(nextChild) && _isNil(prevChild)) {
return true;
}
if (!_isNil(nextChild) && !_isNil(prevChild)) {
var _ref = nextChild.props || {},
nextChildren = _ref.children,
nextProps = _objectWithoutProperties(_ref, ["children"]);
var _ref2 = prevChild.props || {},
prevChildren = _ref2.children,
prevProps = _objectWithoutProperties(_ref2, ["children"]);
if (nextChildren && prevChildren) {
// eslint-disable-next-line no-use-before-define
return shallowEqual(nextProps, prevProps) && isChildrenEqual(nextChildren, prevChildren);
}
if (!nextChildren && !prevChildren) {
return shallowEqual(nextProps, prevProps);
}
return false;
}
return false;
};
/**
* Wether props of children changed
* @param {Object} nextChildren The latest children
* @param {Object} prevChildren The prev children
* @return {Boolean} equal or not
*/
export var isChildrenEqual = function isChildrenEqual(nextChildren, prevChildren) {
if (nextChildren === prevChildren) {
return true;
}
if (Children.count(nextChildren) !== Children.count(prevChildren)) {
return false;
}
var count = Children.count(nextChildren);
if (count === 0) {
return true;
}
if (count === 1) {
return isSingleChildEqual(_isArray(nextChildren) ? nextChildren[0] : nextChildren, _isArray(prevChildren) ? prevChildren[0] : prevChildren);
}
for (var i = 0; i < count; i++) {
var nextChild = nextChildren[i];
var prevChild = prevChildren[i];
if (_isArray(nextChild) || _isArray(prevChild)) {
if (!isChildrenEqual(nextChild, prevChild)) {
return false;
}
} else if (!isSingleChildEqual(nextChild, prevChild)) {
return false;
}
}
return true;
};
export var renderByOrder = function renderByOrder(children, renderMap) {
var elements = [];
var record = {};
Children.forEach(children, function (child, index) {
if (child && isSvgElement(child)) {
elements.push(child);
} else if (child && renderMap[getDisplayName(child.type)]) {
var displayName = getDisplayName(child.type);
var _renderMap$displayNam = renderMap[displayName],
handler = _renderMap$displayNam.handler,
once = _renderMap$displayNam.once;
if (once && !record[displayName] || !once) {
var results = handler(child, displayName, index);
if (_isArray(results)) {
elements = [elements].concat(_toConsumableArray(results));
} else {
elements.push(results);
}
record[displayName] = true;
}
}
});
return elements;
};
export var getReactEventByType = function getReactEventByType(e) {
var type = e && e.type;
if (type && REACT_BROWSER_EVENT_MAP[type]) {
return REACT_BROWSER_EVENT_MAP[type];
}
return null;
};
export var parseChildIndex = function parseChildIndex(child, children) {
var result = -1;
Children.forEach(children, function (entry, index) {
if (entry === child) {
result = index;
}
});
return result;
};