UNPKG

victory-core

Version:
474 lines (452 loc) 15.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createAccessor = createAccessor; exports.degreesToRadians = degreesToRadians; exports.evaluateProp = evaluateProp; exports.evaluateStyle = evaluateStyle; exports.getCurrentAxis = getCurrentAxis; exports.getDefaultStyles = getDefaultStyles; exports.getPadding = getPadding; exports.getPoint = getPoint; exports.getPolarOrigin = getPolarOrigin; exports.getRadius = getRadius; exports.getRange = getRange; exports.getStyles = getStyles; exports.invert = invert; exports.isFunction = isFunction; exports.isHorizontal = isHorizontal; exports.isNil = isNil; exports.isTooltip = isTooltip; exports.mapValues = mapValues; exports.modifyProps = modifyProps; exports.omit = omit; exports.radiansToDegrees = radiansToDegrees; exports.range = range; exports.reduceChildren = reduceChildren; exports.scalePoint = scalePoint; var _react = _interopRequireWildcard(require("react")); var _defaults = _interopRequireDefault(require("lodash/defaults")); var _property = _interopRequireDefault(require("lodash/property")); var _pick = _interopRequireDefault(require("lodash/pick")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } /** * Determine the range of a cartesian axis */ function getCartesianRange(options) { const vertical = options.axis !== "x"; if (vertical) { return [options.height - options.padding.bottom, options.padding.top]; } return [options.padding.left, options.width - options.padding.right]; } /** * Determine the range of a polar axis in radians */ function getPolarRange(options) { if (options.axis === "x") { const startAngle = degreesToRadians(options.startAngle || 0); const endAngle = degreesToRadians(options.endAngle || 360); return [startAngle, endAngle]; } return [options.innerRadius || 0, getRadius({ height: options.height, width: options.width, padding: options.padding })]; } /** * Creates an object composed of the inverted keys and values of object. * If object contains duplicate values, subsequent values overwrite property assignments of previous values. */ function invert(original) { return Object.entries(original).reduce((acc, current) => { acc[current[1]] = current[0]; return acc; }, {}); } /** * creates an object with some keys excluded * replacement for lodash.omit for performance. does not mimic the entire lodash.omit api * @param {Object} originalObject: created object will be based on this object * @param {Array<String>} ks: an array of keys to omit from the new object * @returns {Object} new object with same properties as originalObject */ function omit(originalObject, ks) { if (ks === void 0) { ks = []; } // code based on babel's _objectWithoutProperties const newObject = {}; for (const key in originalObject) { // @ts-expect-error String is not assignable to Key if (ks.indexOf(key) >= 0) { continue; } if (!Object.prototype.hasOwnProperty.call(originalObject, key)) { continue; } newObject[key] = originalObject[key]; } return newObject; } /** * Coalesce the x and y values from a data point */ function getPoint(datum) { const { _x, _x1, _x0, _voronoiX, _y, _y1, _y0, _voronoiY } = datum; const defaultX = _x1 ?? _x; const defaultY = _y1 ?? _y; const point = { x: _voronoiX ?? defaultX, x0: _x0 ?? _x, y: _voronoiY ?? defaultY, y0: _y0 ?? _y }; return (0, _defaults.default)({}, point, datum); } /** * Scale a point based on the origin, direction, and given scale function */ function scalePoint(props, datum) { const { scale, polar, horizontal } = props; const d = getPoint(datum); const origin = props.origin || { x: 0, y: 0 }; const x = horizontal ? scale.y(d.y) : scale.x(d.x); const x0 = horizontal ? scale.y(d.y0) : scale.x(d.x0); const y = horizontal ? scale.x(d.x) : scale.y(d.y); const y0 = horizontal ? scale.x(d.x0) : scale.y(d.y0); return { x: polar ? y * Math.cos(x) + origin.x : x, x0: polar ? y0 * Math.cos(x0) + origin.x : x0, y: polar ? -y * Math.sin(x) + origin.y : y, y0: polar ? -y0 * Math.sin(x0) + origin.x : y0 }; } /** * Returns a padding value from a number or partial padding values */ function getPadding(padding) { const paddingVal = typeof padding === "number" ? padding : 0; const paddingObj = typeof padding === "object" ? padding : {}; return { top: paddingObj.top || paddingVal, bottom: paddingObj.bottom || paddingVal, left: paddingObj.left || paddingVal, right: paddingObj.right || paddingVal }; } /** * Returns true if the component is defined as a tooltip */ function isTooltip(component) { const labelRole = component && component.type && component.type.role; return labelRole === "tooltip"; } function getDefaultStyles(props, role) { const { theme = {}, labelComponent } = props; const defaultStyles = theme[role] && theme[role].style || {}; if (!isTooltip(labelComponent)) { return defaultStyles; } const tooltipStyle = theme.tooltip && theme.tooltip.style || {}; const labelStyle = (0, _defaults.default)({}, tooltipStyle, defaultStyles.labels); return (0, _defaults.default)({}, { labels: labelStyle }, defaultStyles); } function getStyles(style, defaultStyles) { const width = "100%"; const height = "100%"; if (!style) { return (0, _defaults.default)({ parent: { height, width } }, defaultStyles); } const { data, labels, parent } = style; const defaultParent = defaultStyles && defaultStyles.parent || {}; const defaultLabels = defaultStyles && defaultStyles.labels || {}; const defaultData = defaultStyles && defaultStyles.data || {}; return { parent: (0, _defaults.default)({}, parent, defaultParent, { width, height }), labels: (0, _defaults.default)({}, labels, defaultLabels), data: (0, _defaults.default)({}, data, defaultData) }; } /** * Returns the value of a prop or accessor function with the given props */ function evaluateProp(prop, props) { return isFunction(prop) ? prop(props) : prop; } function evaluateStyle(style, props) { if (props.disableInlineStyles) { return {}; } if (!style || !Object.keys(style).some(value => isFunction(style[value]))) { return style; } return Object.keys(style).reduce((prev, curr) => { prev[curr] = evaluateProp(style[curr], props); return prev; }, {}); } function degreesToRadians(degrees) { return typeof degrees === "number" ? degrees * (Math.PI / 180) : degrees; } function radiansToDegrees(radians) { return typeof radians === "number" ? radians / (Math.PI / 180) : radians; } /** * Get the maximum radius that will fit in the container */ function getRadius(options) { const { width, height, padding } = options; const { left, right, top, bottom } = padding; return Math.min(width - left - right, height - top - bottom) / 2; } /** * Returns the origin for a polar chart within the padded area */ function getPolarOrigin(props) { const { width, height } = props; const { top, bottom, left, right } = getPadding(props.padding); const radius = Math.min(width - left - right, height - top - bottom) / 2; const offsetWidth = width / 2 + left - right; const offsetHeight = height / 2 + top - bottom; return { x: offsetWidth + radius > width ? radius + left - right : offsetWidth, y: offsetHeight + radius > height ? radius + top - bottom : offsetHeight }; } /** * Determine the range of an axis based on the given props */ function getRange(props, axis) { if (props.range && props.range[axis]) { return props.range[axis]; } else if (props.range && Array.isArray(props.range)) { return props.range; } return props.polar ? getPolarRange({ axis, innerRadius: props.innerRadius, startAngle: props.startAngle, endAngle: props.endAngle, height: props.height, width: props.width, padding: getPadding(props.padding) }) : getCartesianRange({ axis, height: props.height, width: props.width, padding: getPadding(props.padding) }); } /** * Checks if `value` is `null` or `undefined`. * @returns {boolean} Returns `true` if `value` is nullish, else `false`. */ function isNil(value) { // eslint-disable-next-line eqeqeq return value == null; } /** * Checks if `value` is classified as a `Function` object. * * @since 0.1.0 * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a function, else `false`. */ function isFunction(value) { return typeof value === "function"; } function createAccessor(key) { // creates a data accessor function // given a property key, path, array index, or null for identity. if (isFunction(key)) { return key; } else if (key === null || key === undefined) { // null/undefined means "return the data item itself" return x => x; } // otherwise, assume it is an array index, property key or path (_.property handles all three) return (0, _property.default)(key); } function modifyProps(props, fallbackProps, role) { const theme = props.theme && props.theme[role] ? props.theme[role] : {}; const themeProps = omit(theme, ["style"]); const horizontal = isHorizontal(props); const defaultObject = horizontal === undefined ? {} : { horizontal }; return (0, _defaults.default)(defaultObject, props, themeProps, fallbackProps); } /** * Returns the given axis or the opposite axis when horizontal * @param {string} axis: the given axis, either "x" pr "y" * @param {Boolean} horizontal: true when the chart is flipped to the horizontal orientation * @returns {String} the dimension appropriate for the axis given its props "x" or "y" */ function getCurrentAxis(axis, horizontal) { const otherAxis = axis === "x" ? "y" : "x"; return horizontal ? otherAxis : axis; } /** * Creates an object with the same keys as object and values generated by running * each own enumerable string keyed property of object through the function fn */ function mapValues(values, fn) { if (values) { return Object.keys(values).reduce((acc, key) => { acc[key] = fn(values[key]); return acc; }, {}); } } /** * Creates an array of numbers (positive and/or negative) progressing * from start up to, but not including, end. * A step of -1 is used if a negative start is specified without an end or step. * If end is not specified, it's set to start with start then set to 0. * * @param start The length of the array to create, or the start value * @param end [The end value] If this is defined, start is the start value * @returns An array of the given length */ function range(start, end, increment) { // when the end index is not given, start from 0 const startIndex = end ? start : 0; // when the end index is not given, the end of the range is the start index let endIndex = end ? end : start; // ensure endIndex is not a falsy value if (!endIndex) endIndex = 0; const k = endIndex - startIndex; // the value range const length = Math.abs(k); // the length of the range const sign = k / length || 1; // the sign of the range (negative or positive) const inc = increment || 1; // the step size of each increment // normalize the array length when dealing with floating point values const arrayLength = Math.max(Math.ceil(length / inc), 0); return Array.from(Array(arrayLength), (_, i) => startIndex + i * sign * inc); } /** * @param {Array} children: an array of child components * @param {Function} iteratee: a function with arguments "child", "childName", and "parent" * @param {Object} parentProps: props from the parent that are applied to children * @param {any} initialMemo: The object in which the iteration results are combined. * @param {Function} combine: Combines the result of the iteratee with the current memo * to the memo for the next iteration step * @returns {Array} returns an array of results from calling the iteratee on all nested children */ /* eslint-disable max-params */ function reduceChildren(children, iteratee, parentProps, // @ts-expect-error These defaults are hard to type initialMemo, combine) { if (parentProps === void 0) { parentProps = {}; } if (initialMemo === void 0) { initialMemo = []; } if (combine === void 0) { combine = (memo, item) => // @ts-expect-error These defaults are hard to type memo.concat(item); } const sharedProps = ["data", "domain", "categories", "polar", "startAngle", "endAngle", "minDomain", "maxDomain", "horizontal"]; const traverseChildren = (childArray, names, parent) => { return childArray.reduce((memo, child, index) => { let newMemo = memo; const childRole = child.type && child.type.role; const childName = child.props.name || `${childRole}-${names[index]}`; if (child.props && child.props.children) { const childProps = Object.assign({}, child.props, (0, _pick.default)(parentProps, sharedProps)); const nestedChildren = child.type && child.type.role === "stack" && isFunction(child.type.getChildren) ? child.type.getChildren(childProps) : _react.default.Children.toArray(child.props.children).map(c => { const nestedChildProps = Object.assign({}, c.props, (0, _pick.default)(childProps, sharedProps)); return /*#__PURE__*/_react.default.cloneElement(c, nestedChildProps); }); const childNames = nestedChildren.map((c, i) => `${childName}-${i}`); const nestedResults = traverseChildren(nestedChildren, childNames, child); newMemo = combine(newMemo, nestedResults); } else { const result = iteratee(child, childName, parent); if (result) { newMemo = combine(newMemo, result); } } return newMemo; }, initialMemo); }; const validChildren = children.filter(_react.isValidElement); const childNames = validChildren.map((c, i) => i); return traverseChildren(validChildren, childNames); } /** * @param {Object} props: the props object * @returns {Boolean} returns true if the props object contains `horizontal: true` of if any * children or nested children are horizontal */ function isHorizontal(props) { if (props.horizontal !== undefined || !props.children) { return props.horizontal; } const traverseChildren = childArray => { return childArray.reduce((memo, child) => { const childProps = child.props || {}; if (memo || childProps.horizontal || !childProps.children) { return memo || childProps.horizontal; } return traverseChildren(_react.default.Children.toArray(childProps.children)); }, false); }; return traverseChildren(_react.default.Children.toArray(props.children)); }