// Tremor BarChart [v1.0.0]
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
import React from "react";
import { RiArrowLeftSLine, RiArrowRightSLine } from "@remixicon/react";
import { Bar, CartesianGrid, Label, BarChart as RechartsBarChart, Legend as RechartsLegend, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis, } from "recharts";
import { AvailableChartColors, constructCategoryColors, getColorClassName, } from "../Utils/ChartColors";
import { cx } from "../Utils/Cx";
import { getYAxisDomain } from "../Utils/GetYAxisDomain";
import { useOnWindowResize } from "../Utils/UseWindowOnResize";
//#region Shape
function deepEqual(obj1, obj2) {
    if (obj1 === obj2) {
        return true;
    }
    if (typeof obj1 !== "object" ||
        typeof obj2 !== "object" ||
        obj1 === null ||
        obj2 === null) {
        return false;
    }
    const keys1 = Object.keys(obj1);
    const keys2 = Object.keys(obj2);
    if (keys1.length !== keys2.length) {
        return false;
    }
    for (const key of keys1) {
        if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) {
            return false;
        }
    }
    return true;
}
const renderShape = (props, activeBar, activeLegend, layout) => {
    const { fillOpacity, name, payload, value } = props;
    let { x, width, y, height } = props;
    if (layout === "horizontal" && height < 0) {
        y += height;
        height = Math.abs(height); // height must be a positive number
    }
    else if (layout === "vertical" && width < 0) {
        x += width;
        width = Math.abs(width); // width must be a positive number
    }
    // Radius for rounded corners at top
    const radius = Math.min(4, width / 2, height / 2);
    /*
     * Create path with rounded corners at the top only (for horizontal layout)
     * For vertical layout, round the right side
     */
    let path;
    if (layout === "horizontal") {
        // Rounded top corners for horizontal bars
        path = `
      M ${x},${y + height}
      L ${x},${y + radius}
      Q ${x},${y} ${x + radius},${y}
      L ${x + width - radius},${y}
      Q ${x + width},${y} ${x + width},${y + radius}
      L ${x + width},${y + height}
      Z
    `;
    }
    else {
        // Rounded right corners for vertical bars
        path = `
      M ${x},${y}
      L ${x + width - radius},${y}
      Q ${x + width},${y} ${x + width},${y + radius}
      L ${x + width},${y + height - radius}
      Q ${x + width},${y + height} ${x + width - radius},${y + height}
      L ${x},${y + height}
      Z
    `;
    }
    return (React.createElement("path", { d: path, opacity: activeBar || (activeLegend && activeLegend !== name)
            ? deepEqual(activeBar, Object.assign(Object.assign({}, payload), { value }))
                ? fillOpacity
                : 0.3
            : fillOpacity }));
};
const LegendItem = ({ name, color, onClick, activeLegend, }) => {
    const hasOnValueChange = Boolean(onClick);
    return (React.createElement("li", { className: cx(
        // base
        "group inline-flex flex-nowrap items-center gap-1.5 rounded-sm px-2 py-1 whitespace-nowrap transition", hasOnValueChange
            ? "cursor-pointer hover:bg-gray-100"
            : "cursor-default"), onClick: (e) => {
            e.stopPropagation();
            onClick === null || onClick === void 0 ? void 0 : onClick(name, color);
        } },
        React.createElement("span", { className: cx("size-2 shrink-0 rounded-xs", getColorClassName(color, "bg"), activeLegend && activeLegend !== name ? "opacity-40" : "opacity-100"), "aria-hidden": true }),
        React.createElement("p", { className: cx(
            // base
            "truncate text-xs whitespace-nowrap", 
            // text color
            "text-gray-700", hasOnValueChange && "group-hover:text-gray-900", activeLegend && activeLegend !== name ? "opacity-40" : "opacity-100") }, name)));
};
const ScrollButton = ({ icon, onClick, disabled, }) => {
    const Icon = icon;
    const [isPressed, setIsPressed] = React.useState(false);
    const intervalRef = React.useRef(null);
    React.useEffect(() => {
        if (isPressed) {
            intervalRef.current = setInterval(() => {
                onClick === null || onClick === void 0 ? void 0 : onClick();
            }, 300);
        }
        else {
            clearInterval(intervalRef.current);
        }
        return () => {
            return clearInterval(intervalRef.current);
        };
    }, [isPressed, onClick]);
    React.useEffect(() => {
        if (disabled) {
            clearInterval(intervalRef.current);
            setIsPressed(false);
        }
    }, [disabled]);
    return (React.createElement("button", { type: "button", className: cx(
        // base
        "group inline-flex size-5 items-center truncate rounded-sm transition", disabled
            ? "cursor-not-allowed text-gray-400"
            : "cursor-pointer text-gray-700 hover:bg-gray-100 hover:text-gray-900"), disabled: disabled, onClick: (e) => {
            e.stopPropagation();
            onClick === null || onClick === void 0 ? void 0 : onClick();
        }, onMouseDown: (e) => {
            e.stopPropagation();
            setIsPressed(true);
        }, onMouseUp: (e) => {
            e.stopPropagation();
            setIsPressed(false);
        } },
        React.createElement(Icon, { className: "size-full", "aria-hidden": "true" })));
};
const Legend = React.forwardRef((props, ref) => {
    const { categories, colors = AvailableChartColors, className, onClickLegendItem, activeLegend, enableLegendSlider = false } = props, other = __rest(props, ["categories", "colors", "className", "onClickLegendItem", "activeLegend", "enableLegendSlider"]);
    const scrollableRef = React.useRef(null);
    const scrollButtonsRef = React.useRef(null);
    const [hasScroll, setHasScroll] = React.useState(null);
    const [isKeyDowned, setIsKeyDowned] = React.useState(null);
    const intervalRef = React.useRef(null);
    const checkScroll = React.useCallback(() => {
        const scrollable = scrollableRef === null || scrollableRef === void 0 ? void 0 : scrollableRef.current;
        if (!scrollable) {
            return;
        }
        const hasLeftScroll = scrollable.scrollLeft > 0;
        const hasRightScroll = scrollable.scrollWidth - scrollable.clientWidth > scrollable.scrollLeft;
        setHasScroll({ left: hasLeftScroll, right: hasRightScroll });
    }, [setHasScroll]);
    const scrollToTest = React.useCallback((direction) => {
        var _a, _b;
        const element = scrollableRef === null || scrollableRef === void 0 ? void 0 : scrollableRef.current;
        const scrollButtons = scrollButtonsRef === null || scrollButtonsRef === void 0 ? void 0 : scrollButtonsRef.current;
        const scrollButtonsWith = (_a = scrollButtons === null || scrollButtons === void 0 ? void 0 : scrollButtons.clientWidth) !== null && _a !== void 0 ? _a : 0;
        const width = (_b = element === null || element === void 0 ? void 0 : element.clientWidth) !== null && _b !== void 0 ? _b : 0;
        if (element && enableLegendSlider) {
            element.scrollTo({
                left: direction === "left"
                    ? element.scrollLeft - width + scrollButtonsWith
                    : element.scrollLeft + width - scrollButtonsWith,
                behavior: "smooth",
            });
            setTimeout(() => {
                checkScroll();
            }, 400);
        }
    }, [enableLegendSlider, checkScroll]);
    React.useEffect(() => {
        const keyDownHandler = (key) => {
            if (key === "ArrowLeft") {
                scrollToTest("left");
            }
            else if (key === "ArrowRight") {
                scrollToTest("right");
            }
        };
        if (isKeyDowned) {
            keyDownHandler(isKeyDowned);
            intervalRef.current = setInterval(() => {
                keyDownHandler(isKeyDowned);
            }, 300);
        }
        else {
            clearInterval(intervalRef.current);
        }
        return () => {
            return clearInterval(intervalRef.current);
        };
    }, [isKeyDowned, scrollToTest]);
    const keyDown = (e) => {
        e.stopPropagation();
        if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
            e.preventDefault();
            setIsKeyDowned(e.key);
        }
    };
    const keyUp = (e) => {
        e.stopPropagation();
        setIsKeyDowned(null);
    };
    React.useEffect(() => {
        const scrollable = scrollableRef === null || scrollableRef === void 0 ? void 0 : scrollableRef.current;
        if (enableLegendSlider) {
            checkScroll();
            scrollable === null || scrollable === void 0 ? void 0 : scrollable.addEventListener("keydown", keyDown);
            scrollable === null || scrollable === void 0 ? void 0 : scrollable.addEventListener("keyup", keyUp);
        }
        return () => {
            scrollable === null || scrollable === void 0 ? void 0 : scrollable.removeEventListener("keydown", keyDown);
            scrollable === null || scrollable === void 0 ? void 0 : scrollable.removeEventListener("keyup", keyUp);
        };
    }, [checkScroll, enableLegendSlider]);
    return (React.createElement("ol", Object.assign({ ref: ref, className: cx("relative overflow-hidden", className) }, other),
        React.createElement("div", { ref: scrollableRef, tabIndex: 0, className: cx("flex h-full", enableLegendSlider
                ? (hasScroll === null || hasScroll === void 0 ? void 0 : hasScroll.right) || (hasScroll === null || hasScroll === void 0 ? void 0 : hasScroll.left)
                    ? "snap-mandatory items-center overflow-auto pr-12 pl-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
                    : ""
                : "flex-wrap") }, categories.map((category, index) => {
            return (React.createElement(LegendItem, Object.assign({ key: `item-${index}`, name: category, color: colors[index] }, (onClickLegendItem ? { onClick: onClickLegendItem } : {}), (activeLegend ? { activeLegend: activeLegend } : {}))));
        })),
        enableLegendSlider && ((hasScroll === null || hasScroll === void 0 ? void 0 : hasScroll.right) || (hasScroll === null || hasScroll === void 0 ? void 0 : hasScroll.left)) ? (React.createElement(React.Fragment, null,
            React.createElement("div", { className: cx(
                // base
                "absolute top-0 right-0 bottom-0 flex h-full items-center justify-center pr-1", 
                // background color
                "bg-white") },
                React.createElement(ScrollButton, { icon: RiArrowLeftSLine, onClick: () => {
                        setIsKeyDowned(null);
                        scrollToTest("left");
                    }, disabled: !(hasScroll === null || hasScroll === void 0 ? void 0 : hasScroll.left) }),
                React.createElement(ScrollButton, { icon: RiArrowRightSLine, onClick: () => {
                        setIsKeyDowned(null);
                        scrollToTest("right");
                    }, disabled: !(hasScroll === null || hasScroll === void 0 ? void 0 : hasScroll.right) })))) : null));
});
Legend.displayName = "Legend";
const ChartLegend = ({ payload }, categoryColors, setLegendHeight, activeLegend, onClick, enableLegendSlider, legendPosition, yAxisWidth) => {
    const legendRef = React.useRef(null);
    useOnWindowResize(() => {
        var _a;
        const calculateHeight = (height) => {
            return height ? Number(height) + 15 : 60;
        };
        setLegendHeight(calculateHeight((_a = legendRef.current) === null || _a === void 0 ? void 0 : _a.clientHeight));
    });
    const filteredPayload = payload.filter((item) => {
        return item.type !== "none";
    });
    const paddingLeft = legendPosition === "left" && yAxisWidth ? yAxisWidth - 8 : 0;
    return (React.createElement("div", { style: { paddingLeft: paddingLeft }, ref: legendRef, className: cx("flex items-center", { "justify-center": legendPosition === "center" }, {
            "justify-start": legendPosition === "left",
        }, { "justify-end": legendPosition === "right" }) },
        React.createElement(Legend, Object.assign({ categories: filteredPayload.map((entry) => {
                return entry.value;
            }), colors: filteredPayload.map((entry) => {
                return categoryColors.get(entry.value);
            }) }, (onClick ? { onClickLegendItem: onClick } : {}), (activeLegend ? { activeLegend: activeLegend } : {}), (enableLegendSlider
            ? { enableLegendSlider: enableLegendSlider }
            : {})))));
};
const ChartTooltip = ({ active, payload, label, valueFormatter, }) => {
    if (active && payload && payload.length) {
        return (React.createElement("div", { className: cx(
            // base
            "rounded-md border text-sm shadow-md", 
            // border color
            "border-gray-200", 
            // background color
            "bg-white") },
            React.createElement("div", { className: cx("border-b border-inherit px-4 py-2") },
                React.createElement("p", { className: cx(
                    // base
                    "font-medium", 
                    // text color
                    "text-gray-900") }, label)),
            React.createElement("div", { className: cx("space-y-1 px-4 py-2") }, payload.map(({ value, category, color }, index) => {
                return (React.createElement("div", { key: `id-${index}`, className: "flex items-center justify-between space-x-8" },
                    React.createElement("div", { className: "flex items-center space-x-2" },
                        React.createElement("span", { "aria-hidden": "true", className: cx("size-2 shrink-0 rounded-xs", getColorClassName(color, "bg")) }),
                        React.createElement("p", { className: cx(
                            // base
                            "text-right whitespace-nowrap", 
                            // text color
                            "text-gray-700") }, category)),
                    React.createElement("p", { className: cx(
                        // base
                        "text-right font-medium whitespace-nowrap tabular-nums", 
                        // text color
                        "text-gray-900") }, valueFormatter(value))));
            }))));
    }
    return null;
};
const BarChart = React.forwardRef((props, forwardedRef) => {
    var _a, _b, _c, _d, _e, _f;
    const { data = [], categories = [], index, colors = AvailableChartColors, valueFormatter = (value) => {
        return value.toString();
    }, startEndOnly = false, showXAxis = true, showYAxis = true, showGridLines = true, yAxisWidth = 56, intervalType = "equidistantPreserveStart", showTooltip = true, showLegend = true, autoMinValue = false, minValue, maxValue, allowDecimals = true, className, onValueChange, enableLegendSlider = false, barCategoryGap, tickGap = 5, xAxisLabel, yAxisLabel, layout = "horizontal", type = "default", legendPosition = "right", tooltipCallback, customTooltip } = props, other = __rest(props, ["data", "categories", "index", "colors", "valueFormatter", "startEndOnly", "showXAxis", "showYAxis", "showGridLines", "yAxisWidth", "intervalType", "showTooltip", "showLegend", "autoMinValue", "minValue", "maxValue", "allowDecimals", "className", "onValueChange", "enableLegendSlider", "barCategoryGap", "tickGap", "xAxisLabel", "yAxisLabel", "layout", "type", "legendPosition", "tooltipCallback", "customTooltip"]);
    const CustomTooltip = customTooltip;
    const paddingValue = (!showXAxis && !showYAxis) || (startEndOnly && !showYAxis) ? 0 : 20;
    const [legendHeight, setLegendHeight] = React.useState(60);
    const [activeLegend, setActiveLegend] = React.useState(undefined);
    const categoryColors = constructCategoryColors(categories, colors);
    const [activeBar, setActiveBar] = React.useState(undefined);
    const yAxisDomain = getYAxisDomain(autoMinValue, minValue, maxValue);
    const hasOnValueChange = Boolean(onValueChange);
    const stacked = type === "stacked" || type === "percent";
    const prevActiveRef = React.useRef(undefined);
    const prevLabelRef = React.useRef(undefined);
    function valueToPercent(value) {
        return `${(value * 100).toFixed(0)}%`;
    }
    const onBarClick = React.useCallback((data, _, event) => {
        var _a, _b, _c, _d;
        event.stopPropagation();
        if (!onValueChange) {
            return;
        }
        if (deepEqual(activeBar, Object.assign(Object.assign({}, data.payload), { value: data.value }))) {
            setActiveLegend(undefined);
            setActiveBar(undefined);
            onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(null);
        }
        else {
            setActiveLegend((_b = (_a = data.tooltipPayload) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.dataKey);
            setActiveBar(Object.assign(Object.assign({}, data.payload), { value: data.value }));
            onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(Object.assign({ eventType: "bar", categoryClicked: (_d = (_c = data.tooltipPayload) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.dataKey }, data.payload));
        }
    }, [activeBar, onValueChange, setActiveLegend, setActiveBar]);
    function onCategoryClick(dataKey) {
        if (!hasOnValueChange) {
            return;
        }
        if (dataKey === activeLegend && !activeBar) {
            setActiveLegend(undefined);
            onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(null);
        }
        else {
            setActiveLegend(dataKey);
            onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange({
                eventType: "category",
                categoryClicked: dataKey,
            });
        }
        setActiveBar(undefined);
    }
    const shapeRenderer = (props) => {
        return renderShape(props, activeBar, activeLegend, layout);
    };
    const handleChartClick = () => {
        setActiveBar(undefined);
        setActiveLegend(undefined);
        onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(null);
    };
    return (React.createElement("div", Object.assign({ ref: forwardedRef, className: cx("h-80 w-full", className), "data-tremor-id": "tremor-raw" }, other),
        React.createElement(ResponsiveContainer, null,
            React.createElement(RechartsBarChart, Object.assign({ data: data, syncId: ((_a = props.syncid) === null || _a === void 0 ? void 0 : _a.toString()) || "" }, (hasOnValueChange && (activeLegend || activeBar)
                ? {
                    onClick: handleChartClick,
                }
                : {}), { margin: {
                    bottom: xAxisLabel ? 40 : showXAxis ? 24 : 8,
                    left: yAxisLabel ? 20 : 0,
                    right: yAxisLabel ? 5 : 8,
                    top: 5,
                }, stackOffset: type === "percent" ? "expand" : "none", layout: layout, barCategoryGap: barCategoryGap !== null && barCategoryGap !== void 0 ? barCategoryGap : "10%" }),
                showGridLines ? (React.createElement(CartesianGrid, { className: cx("stroke-gray-100"), strokeDasharray: "3 3", horizontal: layout !== "vertical", vertical: layout === "vertical" })) : null,
                React.createElement(XAxis, Object.assign({ hide: !showXAxis, tick: {
                        transform: layout !== "vertical" ? "translate(0, 6)" : undefined,
                        fontSize: 10,
                        fontWeight: 500,
                    }, fill: "", stroke: "", className: cx(
                    // base
                    "tabular-nums", 
                    // text fill
                    "fill-gray-500", { "mt-4": layout !== "vertical" }), tickLine: false, axisLine: false, minTickGap: tickGap }, (layout !== "vertical"
                    ? Object.assign({ padding: {
                            left: paddingValue,
                            right: paddingValue,
                        }, dataKey: index, interval: startEndOnly ? "preserveStartEnd" : intervalType }, (startEndOnly && data.length > 0
                        ? {
                            ticks: [
                                (_b = data[0]) === null || _b === void 0 ? void 0 : _b[index],
                                (_c = data[data.length - 1]) === null || _c === void 0 ? void 0 : _c[index],
                            ].filter(Boolean),
                        }
                        : {})) : {
                    type: "number",
                    domain: yAxisDomain,
                    tickFormatter: type === "percent" ? valueToPercent : valueFormatter,
                    allowDecimals: allowDecimals,
                })), xAxisLabel && (React.createElement(Label, { position: "insideBottom", offset: -20, className: "fill-gray-800 text-sm font-medium" }, xAxisLabel))),
                React.createElement(YAxis, Object.assign({ width: yAxisWidth, hide: !showYAxis, axisLine: false, tickLine: false, fill: "", stroke: "", className: cx(
                    // base
                    "tabular-nums", 
                    // text fill
                    "fill-gray-500"), tick: {
                        transform: layout !== "vertical"
                            ? "translate(-4, 0)"
                            : "translate(0, 0)",
                        fontSize: 10,
                        fontWeight: 500,
                    } }, (layout !== "vertical"
                    ? {
                        type: "number",
                        domain: yAxisDomain,
                        tickFormatter: type === "percent" ? valueToPercent : valueFormatter,
                        allowDecimals: allowDecimals,
                    }
                    : Object.assign(Object.assign({ dataKey: index }, (startEndOnly && data.length > 0
                        ? {
                            ticks: [
                                (_d = data[0]) === null || _d === void 0 ? void 0 : _d[index],
                                (_e = data[data.length - 1]) === null || _e === void 0 ? void 0 : _e[index],
                            ].filter(Boolean),
                        }
                        : {})), { type: "category", interval: "equidistantPreserveStart" }))), yAxisLabel && (React.createElement(Label, { position: "insideLeft", style: { textAnchor: "middle" }, angle: -90, offset: -15, className: "fill-gray-800 text-sm font-medium" }, yAxisLabel))),
                React.createElement(Tooltip, Object.assign({ wrapperStyle: { outline: "none" }, isAnimationActive: true, animationDuration: 100, cursor: { fill: "#d1d5db", opacity: "0.15" }, offset: 20 }, (layout === "horizontal"
                    ? { position: { y: 0 } }
                    : { position: { x: yAxisWidth + 20 } }), { content: ({ active, payload, label }) => {
                        const cleanPayload = payload
                            ? payload.map((item) => {
                                return {
                                    category: item.dataKey,
                                    value: item.value,
                                    index: item.payload[index],
                                    color: categoryColors.get(item.dataKey),
                                    type: item.type,
                                    payload: item.payload,
                                };
                            })
                            : [];
                        if (tooltipCallback &&
                            (active !== prevActiveRef.current ||
                                label !== prevLabelRef.current)) {
                            tooltipCallback({ active, payload: cleanPayload, label });
                            prevActiveRef.current = active;
                            prevLabelRef.current = label;
                        }
                        return showTooltip && active ? (CustomTooltip ? (React.createElement(CustomTooltip, { active: active, payload: cleanPayload, label: label })) : (React.createElement(ChartTooltip, { active: active, payload: cleanPayload, label: label, valueFormatter: valueFormatter }))) : null;
                    } })),
                showLegend ? (React.createElement(RechartsLegend, { verticalAlign: "top", height: legendHeight, content: ({ payload }) => {
                        return ChartLegend({ payload }, categoryColors, setLegendHeight, activeLegend, hasOnValueChange
                            ? (clickedLegendItem) => {
                                return onCategoryClick(clickedLegendItem);
                            }
                            : undefined, enableLegendSlider, legendPosition, yAxisWidth);
                    } })) : null,
                categories.map((category) => {
                    return (React.createElement(Bar, Object.assign({ className: cx(getColorClassName(categoryColors.get(category), "fill"), onValueChange ? "cursor-pointer" : ""), key: category, name: category, type: "linear", dataKey: category }, (stacked ? { stackId: "stack" } : {}), { isAnimationActive: false, fill: "", shape: shapeRenderer, onClick: onBarClick })));
                }), (_f = props.referenceLines) === null || _f === void 0 ? void 0 :
                _f.map((refLine, refIndex) => {
                    return (React.createElement(ReferenceLine, { key: `ref-${refIndex}`, y: refLine.value, stroke: refLine.color, strokeDasharray: refLine.strokeDasharray || "4 4", strokeWidth: 1.5 }, refLine.label && (React.createElement(Label, { value: refLine.label, position: "insideTopRight", fill: refLine.color, fontSize: 11, fontWeight: 500 }))));
                })))));
});
BarChart.displayName = "BarChart";
export { BarChart };
//# sourceMappingURL=BarChart.js.map