@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
JavaScript
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;