UNPKG

grommet

Version:

focus on the essential experience

582 lines (568 loc) 24.7 kB
"use strict"; exports.__esModule = true; exports.Chart = void 0; var _react = _interopRequireWildcard(require("react")); var _useIsomorphicLayoutEffect = require("../../utils/use-isomorphic-layout-effect"); var _utils = require("../../utils"); var _StyledChart = require("./StyledChart"); var _utils2 = require("./utils"); var _propTypes = require("./propTypes"); var _useThemeValue2 = require("../../utils/useThemeValue"); var _excluded = ["a11yTitle", "bounds", "color", "dash", "direction", "gap", "id", "onClick", "onHover", "opacity", "overflow", "pad", "pattern", "point", "round", "size", "thickness", "type", "values"], _excluded2 = ["color", "label", "onHover", "opacity", "thickness", "value"], _excluded3 = ["color", "label", "onHover", "opacity", "thickness", "value"]; function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; } var gradientMaskColor = '#ffffff'; // use constants so re-renders don't re-trigger effects var defaultValues = []; var Chart = exports.Chart = /*#__PURE__*/_react["default"].forwardRef(function (_ref, ref) { var _theme$chart, _theme$chart4, _theme$chart5; var a11yTitle = _ref.a11yTitle, boundsProp = _ref.bounds, color = _ref.color, dash = _ref.dash, _ref$direction = _ref.direction, direction = _ref$direction === void 0 ? 'vertical' : _ref$direction, gap = _ref.gap, id = _ref.id, onClick = _ref.onClick, onHover = _ref.onHover, opacityProp = _ref.opacity, _ref$overflow = _ref.overflow, overflow = _ref$overflow === void 0 ? false : _ref$overflow, pad = _ref.pad, pattern = _ref.pattern, point = _ref.point, round = _ref.round, sizeProp = _ref.size, thickness = _ref.thickness, _ref$type = _ref.type, type = _ref$type === void 0 ? 'bar' : _ref$type, _ref$values = _ref.values, valuesProp = _ref$values === void 0 ? defaultValues : _ref$values, rest = _objectWithoutPropertiesLoose(_ref, _excluded); var containerRef = (0, _utils.useForwardedRef)(ref); var _useThemeValue = (0, _useThemeValue2.useThemeValue)(), theme = _useThemeValue.theme, passThemeFlag = _useThemeValue.passThemeFlag; var thicknessValue = thickness || ((_theme$chart = theme.chart) == null ? void 0 : _theme$chart.thickness); var values = (0, _react.useMemo)(function () { return (0, _utils2.normalizeValues)(valuesProp); }, [valuesProp]); var horizontal = (0, _react.useMemo)(function () { return direction === 'horizontal'; }, [direction]); // bounds is { x: { min, max }, y: { min, max } } var bounds = (0, _react.useMemo)(function () { return (0, _utils2.normalizeBounds)(boundsProp, values, direction); }, [direction, boundsProp, values]); var strokeWidth = (0, _react.useMemo)(function () { return (0, _utils.parseMetricToNum)(theme.global.edgeSize[thicknessValue] || thicknessValue); }, [theme.global.edgeSize, thicknessValue]); // inset is { top, left, bottom, right } var inset = (0, _react.useMemo)(function () { var result = { top: 0, left: 0, bottom: 0, right: 0 }; if (pad) { if (pad.horizontal) { var padSize = (0, _utils.parseMetricToNum)(theme.global.edgeSize[pad.horizontal] || pad.horizontal); result.left = padSize; result.right = padSize; } if (pad.vertical) { var _padSize = (0, _utils.parseMetricToNum)(theme.global.edgeSize[pad.vertical] || pad.vertical); result.top = _padSize; result.bottom = _padSize; } if (pad.start) { result.left = (0, _utils.parseMetricToNum)(theme.global.edgeSize[pad.start] || pad.start); } if (pad.end) { result.right = (0, _utils.parseMetricToNum)(theme.global.edgeSize[pad.end] || pad.end); } if (typeof pad === 'string') { var _padSize2 = (0, _utils.parseMetricToNum)(theme.global.edgeSize[pad]); result.top = _padSize2; result.left = _padSize2; result.bottom = _padSize2; result.right = _padSize2; } } return result; }, [pad, theme.global.edgeSize]); var strokeDasharray = (0, _react.useMemo)(function () { if (dash) { if (round) return strokeWidth + " " + strokeWidth * 1.5; return strokeWidth * 2 + " " + strokeWidth / 2; } return undefined; }, [dash, round, strokeWidth]); // potentially dynamic sizing var _useState = (0, _react.useState)([0, 0]), containerSize = _useState[0], setContainerSize = _useState[1]; var needContainerSize = (0, _react.useMemo)(function () { return sizeProp && (sizeProp === 'full' || sizeProp === 'fill' || sizeProp.height === 'full' || sizeProp.height === 'fill' || sizeProp.width === 'full' || sizeProp.width === 'fill'); }, [sizeProp]); // size is { width, height } var size = (0, _react.useMemo)(function () { var _theme$chart2, _theme$chart3; var defaultSize = { height: (_theme$chart2 = theme.chart) == null ? void 0 : _theme$chart2.height, width: (_theme$chart3 = theme.chart) == null ? void 0 : _theme$chart3.width }; var gapWidth = gap ? (0, _utils.parseMetricToNum)(theme.global.edgeSize[gap] || gap) : strokeWidth; // autoSize is how wide or tall we'd pefer based on the number of values var autoSize = strokeWidth * values.length + (values.length - 1) * gapWidth; var sizeWidth = typeof sizeProp === 'string' ? sizeProp : (sizeProp == null ? void 0 : sizeProp.width) || horizontal && defaultSize.height || defaultSize.width; var width; if (sizeWidth === 'full' || sizeWidth === 'fill') { width = containerSize[0]; } else if (sizeWidth === 'auto') { width = autoSize; } else { width = (0, _utils.parseMetricToNum)(theme.global.size[sizeWidth] || sizeWidth); } var sizeHeight = typeof sizeProp === 'string' ? sizeProp : (sizeProp == null ? void 0 : sizeProp.height) || horizontal && defaultSize.width || defaultSize.height; var height; if (sizeHeight === 'full' || sizeHeight === 'fill') { height = containerSize[1]; } else if (sizeHeight === 'auto') { height = autoSize; } else { height = (0, _utils.parseMetricToNum)(theme.global.size[sizeHeight] || sizeHeight); } return { width: width, height: height }; }, [containerSize, gap, horizontal, sizeProp, strokeWidth, theme.global.edgeSize, theme.global.size, (_theme$chart4 = theme.chart) == null ? void 0 : _theme$chart4.height, (_theme$chart5 = theme.chart) == null ? void 0 : _theme$chart5.width, values]); // scale is { x, y } var scale = (0, _react.useMemo)(function () { return { x: (size.width - (inset.left + inset.right)) / (bounds.x.max - bounds.x.min), y: (size.height - (inset.top + inset.bottom)) / (bounds.y.max - bounds.y.min) }; }, [bounds, inset, size]); var viewBoxBounds = (0, _react.useMemo)(function () { if (overflow) { return [0, 0, size.width, size.height]; } return [-(strokeWidth / 2), -(strokeWidth / 2), size.width + strokeWidth, size.height + strokeWidth]; }, [overflow, size, strokeWidth]); // set container size when we get ref or when size changes (0, _useIsomorphicLayoutEffect.useLayoutEffect)(function () { if (containerRef.current && needContainerSize) { var containerNode = containerRef.current; if (containerNode) { var parentNode = containerNode.parentNode; if (parentNode) { var rect = parentNode.getBoundingClientRect(); if (rect.width !== containerSize[0] || rect.height !== containerSize[1]) { setContainerSize([rect.width, rect.height]); } } } } }, [containerRef, containerSize, needContainerSize]); // container size, if needed (0, _react.useEffect)(function () { var onResize = function onResize() { var parentNode = containerRef.current.parentNode; var rect = parentNode.getBoundingClientRect(); setContainerSize([rect.width, rect.height]); }; if (needContainerSize) { window.addEventListener('resize', onResize); return function () { return window.removeEventListener('resize', onResize); }; } return undefined; }, [containerRef, needContainerSize]); // rendering helpers, to make rendering code easier to understand var valueCoords = function valueCoords(x, y) { return horizontal ? [y, x] : [x, y]; }; // Converts values to drawing coordinates. // Takes into account the bounds, any inset, and the scale. var valueToCoordinate = function valueToCoordinate(xValue, yValue) { var y = (yValue - bounds.y.min) * scale.y + inset.top; return [(xValue - bounds.x.min) * scale.x + inset.left, // horizontal grows y top down, vertical grows y bottom up horizontal ? y : size.height - y]; }; var useGradient = color && Array.isArray(color); var patternId; function getOpacity(valueOpacity) { return valueOpacity && theme.global.opacity[valueOpacity] || ( // eslint-disable-next-line no-nested-ternary valueOpacity === true ? theme.global.opacity.medium : valueOpacity === false ? undefined : valueOpacity); } var renderBars = function renderBars() { return (values || []).filter(function (_ref2) { var value = _ref2.value; return value[1] !== undefined; }).map(function (valueArg, index) { var valueColor = valueArg.color, label = valueArg.label, valueOnHover = valueArg.onHover, valueOpacity = valueArg.opacity, valueThickness = valueArg.thickness, value = valueArg.value, valueRest = _objectWithoutPropertiesLoose(valueArg, _excluded2); var key = "p-" + index; // Math.min/max are to handle negative values var _valueCoords = valueCoords(value[0], value.length === 2 ? Math.min(Math.max(0, horizontal ? bounds.x.min : bounds.y.min), value[1]) : Math.min(value[1], value[2])), startX = _valueCoords[0], startY = _valueCoords[1]; var _valueCoords2 = valueCoords(value[0], value.length === 2 ? Math.max(Math.min(0, horizontal ? bounds.x.max : bounds.y.max), value[1]) : Math.max(value[1], value[2])), endX = _valueCoords2[0], endY = _valueCoords2[1]; var d = "M " + valueToCoordinate(startX, startY).join(',') + (" L " + valueToCoordinate(endX, endY).join(',')); var hoverProps; if (valueOnHover) { hoverProps = { onMouseOver: function onMouseOver() { return valueOnHover(true); }, onMouseLeave: function onMouseLeave() { return valueOnHover(false); } }; } var clickProps; if (onClick) { clickProps = { onClick: onClick }; } return /*#__PURE__*/_react["default"].createElement("g", { key: key, fill: "none", stroke: valueColor ? (0, _utils.normalizeColor)(valueColor, theme) : undefined, strokeWidth: valueThickness ? (0, _utils.parseMetricToNum)(theme.global.edgeSize[valueThickness] || valueThickness) : undefined, opacity: getOpacity(valueOpacity) }, /*#__PURE__*/_react["default"].createElement("title", null, label), /*#__PURE__*/_react["default"].createElement("path", _extends({ d: d }, hoverProps, clickProps, valueRest, { strokeDasharray: strokeDasharray }))); }); }; var renderLine = function renderLine() { var d = ''; var d2 = ''; (values || []).filter(function (_ref3) { var value = _ref3.value; return value[1] !== undefined; }).forEach(function (_ref4) { var value = _ref4.value; var _valueCoords3 = valueCoords(value[0], value[1]), x = _valueCoords3[0], y = _valueCoords3[1]; d += (d ? ' L' : 'M') + " " + valueToCoordinate(x, y).join(','); if (value[2] !== undefined) { var _valueCoords4 = valueCoords(value[0], value[2]), x2 = _valueCoords4[0], y2 = _valueCoords4[1]; d2 += (d2 ? ' L' : 'M') + " " + valueToCoordinate(x2, y2).join(','); } }); var hoverProps; if (onHover) { hoverProps = { onMouseOver: function onMouseOver() { return onHover(true); }, onMouseLeave: function onMouseLeave() { return onHover(false); } }; } var clickProps; if (onClick) { clickProps = { onClick: onClick }; } return /*#__PURE__*/_react["default"].createElement("g", { fill: "none" }, /*#__PURE__*/_react["default"].createElement("path", _extends({ d: d }, hoverProps, clickProps, { strokeDasharray: strokeDasharray })), d2 && /*#__PURE__*/_react["default"].createElement("path", _extends({ d: d2 }, hoverProps, clickProps, { strokeDasharray: strokeDasharray }))); }; var renderArea = function renderArea() { var d = ''; (values || []).filter(function (_ref5) { var value = _ref5.value; return value[1] !== undefined; }).forEach(function (_ref6, index) { var value = _ref6.value; var _valueCoords5 = valueCoords(value[0], // when a range, second value is on top value[value.length === 2 ? 1 : 2]), x = _valueCoords5[0], y = _valueCoords5[1]; d += (!index ? 'M' : ' L') + " " + valueToCoordinate(x, y).join(','); }); (values || []).filter(function (_ref7) { var value = _ref7.value; return value[1] !== undefined; }).reverse().forEach(function (_ref8) { var value = _ref8.value; var _valueCoords6 = valueCoords(value[0], // Math.max() is to account for value[1] being negative value.length === 2 ? Math.max(0, horizontal ? bounds.x.min : bounds.y.min) : value[1]), x = _valueCoords6[0], y = _valueCoords6[1]; d += " L " + valueToCoordinate(x, y).join(','); }); if (d.length > 0) { d += ' Z'; } var hoverProps; if (onHover) { hoverProps = { onMouseOver: function onMouseOver() { return onHover(true); }, onMouseLeave: function onMouseLeave() { return onHover(false); } }; } var clickProps; if (onClick) { clickProps = { onClick: onClick }; } patternId = pattern && pattern + "-" + id + "-pattern"; return /*#__PURE__*/_react["default"].createElement("g", null, /*#__PURE__*/_react["default"].createElement("path", _extends({ d: d, fill: patternId ? "url(#" + patternId + ")" : undefined }, hoverProps, clickProps))); }; var renderPoints = function renderPoints() { return (values || []).filter(function (_ref9) { var value = _ref9.value; return value[1] !== undefined; }).map(function (valueArg, index) { var valueColor = valueArg.color, label = valueArg.label, valueOnHover = valueArg.onHover, valueOpacity = valueArg.opacity, valueThickness = valueArg.thickness, value = valueArg.value, valueRest = _objectWithoutPropertiesLoose(valueArg, _excluded3); var key = "p-" + index; var hoverProps; if (valueOnHover) { hoverProps = { onMouseOver: function onMouseOver() { return valueOnHover(true); }, onMouseLeave: function onMouseLeave() { return valueOnHover(false); } }; } var clickProps; if (onClick) { clickProps = { onClick: onClick }; } var width = valueThickness ? (0, _utils.parseMetricToNum)(theme.global.edgeSize[valueThickness] || valueThickness) : strokeWidth; var renderPoint = function renderPoint(valueX, valueY) { var props = _extends({}, hoverProps, clickProps, valueRest); var _valueCoords7 = valueCoords(valueX, valueY), x = _valueCoords7[0], y = _valueCoords7[1]; var _valueToCoordinate = valueToCoordinate(x, y), cx = _valueToCoordinate[0], cy = _valueToCoordinate[1]; var off = width / 2; if (point === 'circle' || !point && round) return /*#__PURE__*/_react["default"].createElement("circle", _extends({ cx: cx, cy: cy, r: off }, props)); var d; if (point === 'diamond') d = "M " + cx + " " + (cy - off) + " L " + (cx + off) + " " + cy + " L " + cx + " " + (cy + off) + " L " + (cx - off) + " " + cy + " Z";else if (point === 'star') { var off1 = off / 3; var off2 = off1 * 2; d = "M " + cx + " " + (cy - off) + " L " + (cx - off2) + " " + (cy + off) + " L " + (cx + off) + " " + (cy - off1) + " L " + (cx - off) + " " + (cy - off1) + " L " + (cx + off2) + " " + (cy + off) + " Z"; } else if (point === 'triangle') d = "M " + cx + " " + (cy - off) + " L " + (cx + off) + " " + (cy + off) + " L " + (cx - off) + " " + (cy + off) + " Z";else if (point === 'triangleDown') d = "M " + (cx - off) + " " + (cy - off) + " L " + (cx + off) + " " + (cy - off) + " L " + cx + " " + (cy + off) + " Z"; // square else d = "M " + (cx - off) + " " + (cy - off) + " L " + (cx + off) + " " + (cy - off) + " L " + (cx + off) + " " + (cy + off) + " L " + (cx - off) + " " + (cy + off) + " Z"; return /*#__PURE__*/_react["default"].createElement("path", { d: d }); }; return /*#__PURE__*/_react["default"].createElement("g", { key: key, stroke: "none", fill: valueColor ? (0, _utils.normalizeColor)(valueColor, theme) : undefined, opacity: getOpacity(valueOpacity) }, /*#__PURE__*/_react["default"].createElement("title", null, label), renderPoint(value[0], value[1]), value[2] !== undefined && renderPoint(value[0], value[2])); }); }; var contents; if (type === 'bar') { contents = renderBars(); } else if (type === 'line') { contents = renderLine(); } else if (type === 'area') { contents = renderArea(); } else if (type === 'point') { contents = renderPoints(); } var viewBox = viewBoxBounds.join(' '); var colorName; if (!useGradient) { if (color && color.color) colorName = color.color;else if (color) colorName = color;else if (theme.chart && theme.chart.color) colorName = theme.chart.color; } var opacity; if (opacityProp === true) { opacity = theme.global.opacity.medium; } else if (opacityProp) { opacity = theme.global.opacity[opacityProp] ? theme.global.opacity[opacityProp] : opacityProp; } else if (color && color.opacity) { opacity = theme.global.opacity[color.opacity] ? theme.global.opacity[color.opacity] : color.opacity; } else opacity = undefined; var stroke; if (type !== 'point') { if (useGradient) stroke = gradientMaskColor;else stroke = (0, _utils.normalizeColor)(colorName, theme); } else stroke = 'none'; var fill; if (type === 'point' || type === 'area') { if (useGradient) fill = gradientMaskColor;else fill = (0, _utils.normalizeColor)(colorName, theme); } else fill = 'none'; var drawing = /*#__PURE__*/_react["default"].createElement("g", { stroke: stroke, strokeWidth: type !== 'point' && (type !== 'area' || !pattern) ? strokeWidth : undefined, fill: fill, strokeLinecap: round ? 'round' : 'butt', strokeLinejoin: round ? 'round' : 'miter', opacity: opacity }, contents); var defs = []; var gradientRect; if (useGradient && (horizontal && size.width || size.height)) { var uniqueGradientId = color.map(function (element) { return element.color; }).join('-'); var gradientId = uniqueGradientId + "-" + id + "-gradient"; var maskId = uniqueGradientId + "-" + id + "-mask"; defs.push(/*#__PURE__*/_react["default"].createElement("linearGradient", { key: "gradientId", id: gradientId, x1: 0, y1: 0, x2: horizontal ? 1 : 0, y2: horizontal ? 0 : 1 }, color.slice(0).sort(function (c1, c2) { return horizontal ? c1.value - c2.value : c2.value - c1.value; }).map(function (_ref0) { var value = _ref0.value, gradientColor = _ref0.color; return /*#__PURE__*/_react["default"].createElement("stop", { key: value, offset: horizontal ? (value - bounds.x.min) * scale.x / size.width : (size.height - (value - bounds.y.min) * scale.y) / size.height, stopColor: (0, _utils.normalizeColor)(gradientColor, theme) }); }))); defs.push(/*#__PURE__*/_react["default"].createElement("mask", { key: "mask", id: maskId }, drawing)); gradientRect = /*#__PURE__*/_react["default"].createElement("rect", { x: viewBoxBounds[0], y: viewBoxBounds[1], width: viewBoxBounds[2], height: viewBoxBounds[3], fill: "url(#" + gradientId + ")", mask: "url(#" + maskId + ")" }); } else if (patternId) { var content; var diagonal = pattern.match(/Diagonal/); var unit = diagonal ? strokeWidth * Math.sqrt(2) : strokeWidth; var half = unit / 2; var _double = unit * 2; var pColor = (0, _utils.normalizeColor)(colorName, theme); if (pattern === 'squares') { content = /*#__PURE__*/_react["default"].createElement("rect", { x: half, y: half, width: unit, height: unit, fill: pColor }); } else if (pattern === 'circles') { content = /*#__PURE__*/_react["default"].createElement("circle", { cx: unit, cy: unit, r: half, fill: pColor }); } else if (pattern === 'stripesHorizontal') { content = /*#__PURE__*/_react["default"].createElement("path", { d: "M 0 " + unit + " L " + _double + " " + unit, stroke: pColor, strokeWidth: strokeWidth }); } else if (pattern === 'stripesVertical') { content = /*#__PURE__*/_react["default"].createElement("path", { d: "M " + unit + " 0 L " + unit + " " + _double, stroke: pColor, strokeWidth: strokeWidth }); } else if (pattern === 'stripesDiagonalDown') { content = /*#__PURE__*/_react["default"].createElement("path", { d: "M " + half + " " + -half + " L " + (_double + half) + " " + (_double - half) + "\n M " + -half + " " + half + " L " + (_double - half) + " " + (_double + half), stroke: pColor, strokeWidth: strokeWidth }); } else if (pattern === 'stripesDiagonalUp') { content = /*#__PURE__*/_react["default"].createElement("path", { d: "M " + -half + " " + (_double - half) + " L " + (_double - half) + " " + -half + "\n M " + half + " " + (_double + half) + " L " + (_double + half) + " " + half, stroke: pColor, strokeWidth: strokeWidth }); } defs.push(/*#__PURE__*/_react["default"].createElement("pattern", { key: patternId, id: patternId, width: _double, height: _double, patternUnits: "userSpaceOnUse" }, content)); } return /*#__PURE__*/_react["default"].createElement(_StyledChart.StyledChart, _extends({ ref: containerRef, id: id, "aria-label": a11yTitle, viewBox: viewBox, preserveAspectRatio: "none", width: size === 'full' ? '100%' : size.width, height: size === 'full' ? '100%' : size.height, typeProp: type // prevent adding to DOM }, passThemeFlag, rest), defs.length && /*#__PURE__*/_react["default"].createElement("defs", null, defs), useGradient ? gradientRect : drawing); }); Chart.displayName = 'Chart'; Chart.propTypes = _propTypes.ChartPropTypes;