UNPKG

react-horizontal-heatmap

Version:

A lightweight React component for rendering a horizontal heatmap. Perfect for timelines, activity charts, or health status indicators. Fully customizable colors, box size, and spacing.

132 lines 4.2 kB
// src/HorizontalHeatmap.tsx import { useState, useRef } from "react"; import { Popover, ArrowContainer } from "react-tiny-popover"; import { jsx, jsxs } from "react/jsx-runtime"; var defaultColors = [ "rgb(36, 193, 154)", // greenish "rgb(251, 191, 36)", // yellowish "rgb(248, 113, 113)" // reddish ]; var HorizontalHeatmap = ({ data, boxSize = 40, gap = 5, colors = defaultColors, emptyMessage = "No items available" }) => { const [openIndex, setOpenIndex] = useState(null); const hideTimeoutRef = useRef(null); const getColor = (value) => { const max = Math.max(...data.map((d) => d.value), 1); const index = Math.floor(value / max * (colors.length - 1)); return colors[index]; }; const clearHideTimeout = () => { if (hideTimeoutRef.current) { clearTimeout(hideTimeoutRef.current); hideTimeoutRef.current = null; } }; const startHideTimeout = (callback) => { clearHideTimeout(); hideTimeoutRef.current = setTimeout(callback, 150); }; return /* @__PURE__ */ jsx("div", { style: { display: "flex", gap }, children: data.map((d, i) => /* @__PURE__ */ jsx( Popover, { isOpen: openIndex === i, positions: ["bottom"], padding: 10, reposition: true, onClickOutside: () => setOpenIndex(null), content: ({ position, childRect, popoverRect }) => /* @__PURE__ */ jsx( ArrowContainer, { position, childRect, popoverRect, arrowColor: "#f5f5f5", arrowSize: 10, arrowStyle: { opacity: 1 }, className: "popover-arrow-container", children: /* @__PURE__ */ jsxs( "div", { style: { backgroundColor: "#f5f5f5", color: "#333", padding: 12, borderRadius: 6, minWidth: 180, fontFamily: "Arial, sans-serif", boxShadow: "0 4px 12px rgba(0,0,0,0.1)" }, onMouseEnter: clearHideTimeout, onMouseLeave: () => startHideTimeout(() => setOpenIndex(null)), children: [ /* @__PURE__ */ jsx("div", { style: { fontWeight: "bold", marginBottom: 6 }, children: d.time }), d.items.length > 0 ? /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0 }, children: d.items.map((item, idx) => /* @__PURE__ */ jsxs( "li", { style: { display: "flex", alignItems: "center", marginBottom: 4 }, children: [ /* @__PURE__ */ jsx("span", { style: { marginRight: 6 }, children: item.icon }), /* @__PURE__ */ jsx( "a", { href: item.link, target: "_blank", rel: "noopener noreferrer", style: { color: "#1976d2", textDecoration: "none" }, children: item.text } ) ] }, idx )) }) : /* @__PURE__ */ jsx("div", { style: { fontStyle: "italic", opacity: 0.6 }, children: emptyMessage }) ] } ) } ), children: /* @__PURE__ */ jsx( "div", { style: { width: boxSize, height: boxSize * 16 / 9, backgroundColor: getColor(d.value), borderRadius: 4, cursor: "pointer", transition: "transform 0.15s ease-in-out" }, onMouseEnter: () => { clearHideTimeout(); setOpenIndex(i); }, onMouseLeave: () => startHideTimeout(() => setOpenIndex(null)) } ) }, i )) }); }; export { HorizontalHeatmap }; //# sourceMappingURL=index.mjs.map