UNPKG

@trap_stevo/legendarybuilderproreact-ui

Version:

The legendary UI & utility API that makes your application a legendary application. ~ Created by Steven Compton

510 lines 23.3 kB
import _extends from "@babel/runtime/helpers/extends"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray"; import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } import React, { useState, useEffect, useRef, useMemo } from "react"; import { motion, AnimatePresence } from "framer-motion"; function HUDBarChart(_ref) { var _ref$toolTipContainer = _ref.toolTipContainerAnimationTransitionConfigurationSettings, toolTipContainerAnimationTransitionConfigurationSettings = _ref$toolTipContainer === void 0 ? {} : _ref$toolTipContainer, _ref$toolTipContainer2 = _ref.toolTipContainerInitialAnimationConfigurationSettings, toolTipContainerInitialAnimationConfigurationSettings = _ref$toolTipContainer2 === void 0 ? {} : _ref$toolTipContainer2, _ref$toolTipContainer3 = _ref.toolTipContainerExitAnimationConfigurationSettings, toolTipContainerExitAnimationConfigurationSettings = _ref$toolTipContainer3 === void 0 ? {} : _ref$toolTipContainer3, _ref$toolTipContainer4 = _ref.toolTipContainerAnimationConfigurationSettings, toolTipContainerAnimationConfigurationSettings = _ref$toolTipContainer4 === void 0 ? {} : _ref$toolTipContainer4, _ref$legendAnimationT = _ref.legendAnimationTransitionConfigurationSettings, legendAnimationTransitionConfigurationSettings = _ref$legendAnimationT === void 0 ? {} : _ref$legendAnimationT, _ref$legendSeriesColo = _ref.legendSeriesColorDisplayConfigurationSettings, legendSeriesColorDisplayConfigurationSettings = _ref$legendSeriesColo === void 0 ? {} : _ref$legendSeriesColo, _ref$barChartContentC = _ref.barChartContentContainerConfigurationSettings, barChartContentContainerConfigurationSettings = _ref$barChartContentC === void 0 ? {} : _ref$barChartContentC, _ref$barAnimationTran = _ref.barAnimationTransitionConfigurationSettings, barAnimationTransitionConfigurationSettings = _ref$barAnimationTran === void 0 ? {} : _ref$barAnimationTran, _ref$legendInitialAni = _ref.legendInitialAnimationConfigurationSettings, legendInitialAnimationConfigurationSettings = _ref$legendInitialAni === void 0 ? {} : _ref$legendInitialAni, _ref$barInitialAnimat = _ref.barInitialAnimationConfigurationSettings, barInitialAnimationConfigurationSettings = _ref$barInitialAnimat === void 0 ? {} : _ref$barInitialAnimat, _ref$toolTipContainer5 = _ref.toolTipContainerConfigurationSettings, toolTipContainerConfigurationSettings = _ref$toolTipContainer5 === void 0 ? {} : _ref$toolTipContainer5, _ref$legendAnimationC = _ref.legendAnimationConfigurationSettings, legendAnimationConfigurationSettings = _ref$legendAnimationC === void 0 ? {} : _ref$legendAnimationC, _ref$legendContainerC = _ref.legendContainerConfigurationSettings, legendContainerConfigurationSettings = _ref$legendContainerC === void 0 ? {} : _ref$legendContainerC, _ref$barAnimationConf = _ref.barAnimationConfigurationSettings, barAnimationConfigurationSettings = _ref$barAnimationConf === void 0 ? {} : _ref$barAnimationConf, _ref$legendLabelConfi = _ref.legendLabelConfigurationSettings, legendLabelConfigurationSettings = _ref$legendLabelConfi === void 0 ? {} : _ref$legendLabelConfi, _ref$barChartConfigur = _ref.barChartConfigurationSettings, barChartConfigurationSettings = _ref$barChartConfigur === void 0 ? {} : _ref$barChartConfigur, _ref$legendConfigurat = _ref.legendConfigurationSettings, legendConfigurationSettings = _ref$legendConfigurat === void 0 ? {} : _ref$legendConfigurat, _ref$barChartContentC2 = _ref.barChartContentContainerConfigurations, barChartContentContainerConfigurations = _ref$barChartContentC2 === void 0 ? {} : _ref$barChartContentC2, _ref$legendSeriesColo2 = _ref.legendSeriesColorDisplayConfigurations, legendSeriesColorDisplayConfigurations = _ref$legendSeriesColo2 === void 0 ? {} : _ref$legendSeriesColo2, _ref$axisLabelContain = _ref.axisLabelContainerConfigurations, axisLabelContainerConfigurations = _ref$axisLabelContain === void 0 ? {} : _ref$axisLabelContain, _ref$axisTickContaine = _ref.axisTickContainerConfigurations, axisTickContainerConfigurations = _ref$axisTickContaine === void 0 ? {} : _ref$axisTickContaine, _ref$barLabelContaine = _ref.barLabelContainerConfigurations, barLabelContainerConfigurations = _ref$barLabelContaine === void 0 ? {} : _ref$barLabelContaine, _ref$toolTipContainer6 = _ref.toolTipContainerConfigurations, toolTipContainerConfigurations = _ref$toolTipContainer6 === void 0 ? {} : _ref$toolTipContainer6, _ref$legendContainerC2 = _ref.legendContainerConfigurations, legendContainerConfigurations = _ref$legendContainerC2 === void 0 ? {} : _ref$legendContainerC2, _ref$barContainerConf = _ref.barContainerConfigurations, barContainerConfigurations = _ref$barContainerConf === void 0 ? {} : _ref$barContainerConf, _ref$axisTickLineConf = _ref.axisTickLineConfigurations, axisTickLineConfigurations = _ref$axisTickLineConf === void 0 ? {} : _ref$axisTickLineConf, _ref$noDataLabelConfi = _ref.noDataLabelConfigurations, noDataLabelConfigurations = _ref$noDataLabelConfi === void 0 ? {} : _ref$noDataLabelConfi, _ref$legendLabelConfi2 = _ref.legendLabelConfigurations, legendLabelConfigurations = _ref$legendLabelConfi2 === void 0 ? {} : _ref$legendLabelConfi2, _ref$axisTickConfigur = _ref.axisTickConfigurations, axisTickConfigurations = _ref$axisTickConfigur === void 0 ? {} : _ref$axisTickConfigur, _ref$barChartConfigur2 = _ref.barChartConfigurations, barChartConfigurations = _ref$barChartConfigur2 === void 0 ? {} : _ref$barChartConfigur2, _ref$legendConfigurat2 = _ref.legendConfigurations, legendConfigurations = _ref$legendConfigurat2 === void 0 ? {} : _ref$legendConfigurat2, _ref$barConfiguration = _ref.barConfigurations, barConfigurations = _ref$barConfiguration === void 0 ? {} : _ref$barConfiguration, _ref$getColor = _ref.getColor, getColor = _ref$getColor === void 0 ? function (i, j) { return "hsl(".concat(i * 60 + j * 20, ", 70%, 60%)"); } : _ref$getColor, _ref$renderTooltip = _ref.renderTooltip, renderTooltip = _ref$renderTooltip === void 0 ? function (_ref2) { var label = _ref2.label, value = _ref2.value; return /*#__PURE__*/React.createElement("div", null, label, ": ", value); } : _ref$renderTooltip, _ref$renderLegendLabe = _ref.renderLegendLabel, renderLegendLabel = _ref$renderLegendLabe === void 0 ? function (datum, index) { return datum.label; } : _ref$renderLegendLabe, _ref$renderAxisLabel = _ref.renderAxisLabel, renderAxisLabel = _ref$renderAxisLabel === void 0 ? function (datum) { return datum.label; } : _ref$renderAxisLabel, _ref$renderBarLabel = _ref.renderBarLabel, renderBarLabel = _ref$renderBarLabel === void 0 ? function (_ref3) { var value = _ref3.value; return value; } : _ref$renderBarLabel, _ref$renderAxisTick = _ref.renderAxisTick, renderAxisTick = _ref$renderAxisTick === void 0 ? function (val) { return val; } : _ref$renderAxisTick, _ref$onBarClick = _ref.onBarClick, onBarClick = _ref$onBarClick === void 0 ? function () {} : _ref$onBarClick, _ref$noDataContent = _ref.noDataContent, noDataContent = _ref$noDataContent === void 0 ? null : _ref$noDataContent, _ref$tooltipHeightPos = _ref.tooltipHeightPositioningDetectionFactor, tooltipHeightPositioningDetectionFactor = _ref$tooltipHeightPos === void 0 ? 80 : _ref$tooltipHeightPos, _ref$tooltipWidthPosi = _ref.tooltipWidthPositioningDetectionFactor, tooltipWidthPositioningDetectionFactor = _ref$tooltipWidthPosi === void 0 ? 100 : _ref$tooltipWidthPosi, _ref$orientation = _ref.orientation, orientation = _ref$orientation === void 0 ? "vertical" : _ref$orientation, _ref$layoutMode = _ref.layoutMode, layoutMode = _ref$layoutMode === void 0 ? "single" : _ref$layoutMode, _ref$chartPadding = _ref.chartPadding, chartPadding = _ref$chartPadding === void 0 ? { top: 20, right: 20, bottom: 60, left: 60 } : _ref$chartPadding, _ref$barThicknessFact = _ref.barThicknessFactor, barThicknessFactor = _ref$barThicknessFact === void 0 ? 20 : _ref$barThicknessFact, _ref$barFontSize = _ref.barFontSize, barFontSize = _ref$barFontSize === void 0 ? 12 : _ref$barFontSize, _ref$tickCount = _ref.tickCount, tickCount = _ref$tickCount === void 0 ? 5 : _ref$tickCount, _ref$groupGap = _ref.groupGap, groupGap = _ref$groupGap === void 0 ? 12 : _ref$groupGap, _ref$barRadius = _ref.barRadius, barRadius = _ref$barRadius === void 0 ? 6 : _ref$barRadius, _ref$barGap = _ref.barGap, barGap = _ref$barGap === void 0 ? 8 : _ref$barGap, _ref$data = _ref.data, data = _ref$data === void 0 ? [] : _ref$data; var _useState = useState(data.map(function (d) { return d.label; })), _useState2 = _slicedToArray(_useState, 2), visibleGroups = _useState2[0], setVisibleGroups = _useState2[1]; var _useState3 = useState({ height: 400, width: 800 }), _useState4 = _slicedToArray(_useState3, 2), size = _useState4[0], setSize = _useState4[1]; var _useState5 = useState({ x: 0, y: 0 }), _useState6 = _slicedToArray(_useState5, 2), tooltipPos = _useState6[0], setTooltipPos = _useState6[1]; var _useState7 = useState(null), _useState8 = _slicedToArray(_useState7, 2), hovered = _useState8[0], setHovered = _useState8[1]; var tooltipHovered = useRef(false); var containerRef = useRef(null); var tooltipRef = useRef(null); var chartH = size.height - chartPadding.top - chartPadding.bottom; var chartW = size.width - chartPadding.left - chartPadding.right; var filteredData = useMemo(function () { return data.filter(function (d) { return visibleGroups.includes(d.label); }); }, [data, visibleGroups]); var maxValue = useMemo(function () { if (!filteredData.length) { return 1; } if (layoutMode === "single") { return Math.max.apply(Math, _toConsumableArray(filteredData.map(function (d) { return d.value; })).concat([1])); } if (layoutMode === "grouped") { return Math.max.apply(Math, _toConsumableArray(filteredData.flatMap(function (d) { return d.values; })).concat([1])); } if (layoutMode === "stacked") { return Math.max.apply(Math, _toConsumableArray(filteredData.map(function (d) { return d.values.reduce(function (a, b) { return a + b; }, 0); })).concat([1])); } return 1; }, [filteredData, layoutMode]); var groupWidths = useMemo(function () { return filteredData.map(function (d) { var _d$values; var count = layoutMode === "grouped" ? ((_d$values = d.values) === null || _d$values === void 0 ? void 0 : _d$values.length) || 1 : 1; return count * barThicknessFactor + (count - 1) * barGap; }); }, [filteredData, layoutMode, barThicknessFactor, barGap]); var totalGroupWidth = useMemo(function () { return groupWidths.reduce(function (a, b) { return a + b; }, 0) + groupGap * (filteredData.length - 1); }, [groupWidths, groupGap, filteredData]); var scaleX = chartW / totalGroupWidth; var scaledGroupWidths = useMemo(function () { return groupWidths.map(function (w) { return w * scaleX; }); }, [groupWidths, scaleX]); var groupXPositions = useMemo(function () { var positions = []; var runningX = chartPadding.left; for (var i = 0; i < scaledGroupWidths.length; i++) { positions.push(runningX); runningX += scaledGroupWidths[i] + groupGap * scaleX; } return positions; }, [scaledGroupWidths, groupGap, scaleX, chartPadding.left]); var groupCenters = useMemo(function () { return groupXPositions.map(function (x, i) { return x + scaledGroupWidths[i] / 2; }); }, [groupXPositions, scaledGroupWidths]); var scaledBarThickness = barThicknessFactor * scaleX; var empty = !filteredData.length; var handleHover = function handleHover(e, i, j) { if (tooltipHovered.current) { return; } var rect = containerRef.current.getBoundingClientRect(); var x = e.clientX - rect.left; var y = e.clientY - rect.top; var tooltipHeight = tooltipHeightPositioningDetectionFactor; var tooltipWidth = tooltipWidthPositioningDetectionFactor; var adjustedX = x + 12; var adjustedY = y - 12; if (x + tooltipWidth > rect.width) { adjustedX = x - tooltipWidth - 12; } if (y - tooltipHeight < 0) { adjustedY = y + 12; } setTooltipPos({ x: adjustedX, y: adjustedY }); setHovered({ i: i, j: j }); }; var clearHover = function clearHover() { var timer = setTimeout(function () { clearTimeout(timer); if (!tooltipHovered.current) setHovered(null); }, 60); }; var toggleGroup = function toggleGroup(label) { setVisibleGroups(function (prev) { return prev.includes(label) ? prev.filter(function (l) { return l !== label; }) : [].concat(_toConsumableArray(prev), [label]); }); }; useEffect(function () { var observer = new ResizeObserver(function (_ref4) { var _ref5 = _slicedToArray(_ref4, 1), entry = _ref5[0]; var _entry$contentRect = entry.contentRect, width = _entry$contentRect.width, height = _entry$contentRect.height; setSize({ width: width, height: height }); }); if (containerRef.current) { observer.observe(containerRef.current); } return function () { return observer.disconnect(); }; }, []); useEffect(function () { function handleMouseMove(e) { if (!tooltipRef.current) { return; } var rect = tooltipRef.current.getBoundingClientRect(); var inside = e.clientX >= rect.left - 12 && e.clientX <= rect.right + 12 && e.clientY >= rect.top - 12 && e.clientY <= rect.bottom + 12; tooltipHovered.current = inside; if (!inside && !hovered) { setHovered(null); } } window.addEventListener("mousemove", handleMouseMove); return function () { return window.removeEventListener("mousemove", handleMouseMove); }; }, [hovered]); return /*#__PURE__*/React.createElement("div", _extends({ style: _objectSpread({ display: "flex", alignItems: "stretch", overflow: "hidden", borderRadius: "16px", background: "radial-gradient(circle at 30% 20%, #12151c, #0d1117)", minHeight: 300, height: "auto", width: "100%" }, barChartConfigurationSettings) }, barChartConfigurations), /*#__PURE__*/React.createElement("div", _extends({ style: _objectSpread({ display: "flex", flexDirection: "column", flexShrink: 0, overflowY: "auto", maxHeight: "calc(100vh - 100px)", minWidth: "130px", gap: "10px", padding: "20px" }, legendContainerConfigurationSettings) }, legendContainerConfigurations), data.map(function (d, i) { var visible = visibleGroups.includes(d.label); return /*#__PURE__*/React.createElement(motion.div, _extends({ key: i, initial: _objectSpread({ opacity: 0, x: -10 }, legendInitialAnimationConfigurationSettings), animate: _objectSpread({ opacity: 1, x: 0 }, legendAnimationConfigurationSettings), transition: _objectSpread({ delay: i * 0.04 }, legendAnimationTransitionConfigurationSettings), style: _objectSpread({ display: "flex", alignItems: "center", cursor: "pointer", transition: "all 0.2s ease", border: visible ? "1px solid rgba(119, 119, 119, 0.169)" : "1px solid transparent", borderRadius: "8px", backgroundColor: visible ? "rgba(255, 255, 255, 0.0169)" : "transparent", color: visible ? "rgba(238, 238, 238, 1)" : "rgba(119, 119, 119, 1)", padding: visible ? "6px 10px" : "1px 2px" }, legendConfigurationSettings), onClick: function onClick() { return toggleGroup(d.label); } }, legendConfigurations), /*#__PURE__*/React.createElement("div", _extends({ style: _objectSpread({ transition: "all 0.2s ease", filter: visible ? "none" : "grayscale(1)", borderRadius: 3, background: getColor(i, 0), height: 12, width: 12, opacity: visible ? 1 : 0.4, marginRight: 8 }, legendSeriesColorDisplayConfigurationSettings) }, legendSeriesColorDisplayConfigurations)), /*#__PURE__*/React.createElement("span", _extends({ style: _objectSpread({ fontSize: 13 }, legendLabelConfigurationSettings) }, legendLabelConfigurations), renderLegendLabel(d, i))); })), /*#__PURE__*/React.createElement("div", _extends({ ref: containerRef, style: _objectSpread({ position: "relative", flexGrow: 1, aspectRatio: "2 / 1" }, barChartContentContainerConfigurationSettings) }, barChartContentContainerConfigurations), /*#__PURE__*/React.createElement("svg", { width: "100%", height: "100%", viewBox: "0 0 ".concat(size.width, " ").concat(size.height) }, empty ? /*#__PURE__*/React.createElement(React.Fragment, null, noDataContent ? noDataContent : /*#__PURE__*/React.createElement("text", _extends({ dominantBaseline: "middle", textAnchor: "middle", fontStyle: "italic", fontSize: 18, fill: "#888", y: "50%", x: "50%" }, noDataLabelConfigurations), "No Data")) : /*#__PURE__*/React.createElement(React.Fragment, null, Array.from({ length: tickCount }).map(function (_, i) { var ratio = i / (tickCount - 1); var y = chartPadding.top + chartH - ratio * chartH; var val = Math.round(maxValue * ratio); return /*#__PURE__*/React.createElement("g", _extends({ key: "tick-".concat(i) }, axisTickContainerConfigurations), /*#__PURE__*/React.createElement("line", _extends({ stroke: "rgba(255, 255, 255, 0.05)", x2: size.width - chartPadding.right, x1: chartPadding.left, y2: y, y1: y }, axisTickLineConfigurations)), /*#__PURE__*/React.createElement("text", _extends({ textAnchor: "end", fill: "#44f2ff", fontSize: 10, x: chartPadding.left - 10, y: y + 4 }, axisTickConfigurations), renderAxisTick(val, i))); }), filteredData.map(function (datum, i) { var group = layoutMode === "single" ? [datum.value] : datum.values; var groupX = groupXPositions[i]; var stackedOffset = 0; return group.map(function (val, j) { var barHeight = val / maxValue * chartH; var barX = groupX + (layoutMode === "grouped" ? j * (scaledBarThickness + barGap * scaleX) : 0); var barY = layoutMode === "stacked" ? chartPadding.top + chartH - barHeight - stackedOffset : chartPadding.top + chartH - barHeight; if (layoutMode === "stacked") { stackedOffset += barHeight; } var barCenterX = barX + scaledBarThickness / 2; var insideBar = barHeight > barFontSize + 6; return /*#__PURE__*/React.createElement("g", _extends({ key: "bar-".concat(i, "-").concat(j) }, barContainerConfigurations), /*#__PURE__*/React.createElement(motion.rect, _extends({ initial: _objectSpread({ height: 0, y: chartPadding.top + chartH }, barInitialAnimationConfigurationSettings), animate: _objectSpread({ height: barHeight, y: 0 }, barAnimationConfigurationSettings), transition: _objectSpread({ type: "spring", stiffness: 220, damping: 24 }, barAnimationTransitionConfigurationSettings), fill: getColor(i, j), height: barHeight, width: scaledBarThickness, rx: barRadius, ry: barRadius, x: barX, y: barY, onMouseMove: function onMouseMove(e) { return handleHover(e, i, j); }, onMouseLeave: clearHover, onClick: function onClick() { return onBarClick(datum, i, j); } }, barConfigurations)), /*#__PURE__*/React.createElement("text", _extends({ fill: insideBar ? "#fff" : "#ccc", fontSize: barFontSize, textAnchor: "middle", x: barCenterX, y: insideBar ? barY + barHeight / 2 + barFontSize / 2 - 2 : barY - 4 }, barLabelContainerConfigurations), renderBarLabel({ label: datum.label, value: val }, i, j))); }); }), filteredData.map(function (datum, i) { var center = groupCenters[i]; return /*#__PURE__*/React.createElement("text", _extends({ key: "xlabel-".concat(i), textAnchor: "middle", fontSize: 12, fill: "#ccc", y: size.height - chartPadding.bottom + 20, x: center }, axisLabelContainerConfigurations), renderAxisLabel(datum, i)); }))), /*#__PURE__*/React.createElement(AnimatePresence, null, hovered && /*#__PURE__*/React.createElement(motion.div, _extends({ ref: tooltipRef, initial: _objectSpread({ opacity: 0, scale: 0.9 }, toolTipContainerInitialAnimationConfigurationSettings), animate: _objectSpread({ opacity: 1, scale: 1 }, toolTipContainerAnimationConfigurationSettings), exit: _objectSpread({ opacity: 0, scale: 0.9 }, toolTipContainerExitAnimationConfigurationSettings), transition: _objectSpread({ type: "spring", stiffness: 300, damping: 20 }, toolTipContainerAnimationTransitionConfigurationSettings), style: _objectSpread({ position: "absolute", pointerEvents: "auto", transform: "translate(-50%, -100%)", fontSize: "13px", border: "1px solid #ddd", boxShadow: "0 4px 14px rgba(0,0,0,0.15)", borderRadius: "8px", background: "rgba(255,255,255,0.95)", color: "#222", zIndex: 999, padding: "8px 12px", left: tooltipPos.x, top: tooltipPos.y }, toolTipContainerConfigurationSettings) }, toolTipContainerConfigurations), renderTooltip({ label: filteredData[hovered.i].label, value: layoutMode === "single" ? filteredData[hovered.i].value : filteredData[hovered.i].values[hovered.j] }))))); } export default HUDBarChart;