import * as d3 from "d3";
export const isDefined = (x) => typeof x !== "undefined";
export const drawXAxis = ({ svg, xScale, yScale, height, showGrid, scaleFonts, yOffset = 0, }) => {
    // @ts-expect-error
    const xAxis = d3.axisBottom(xScale).tickSize(0);
    showGrid.vertical && xAxis.tickSize(-height);
    svg
        .append("g")
        .classed(`xAxis`, true)
        .call(xAxis)
        .attr("transform", "translate(0, " + (yScale(0) - yOffset) + ")")
        .attr("font-size", scaleFonts?.fontSize ?? "14px")
        .attr("font-family", scaleFonts?.fontFamily ?? "Regular");
};
export const getExtents = (series) => {
    const xList = [];
    const yList = [];
    series.forEach(({ serie }) => {
        const x = d3.extent(serie, (d) => d[0]);
        const y = d3.extent(serie, (d) => d[1]);
        x[0] && x[1] && xList.push(...x);
        y[0] && y[1] && yList.push(...y);
    });
    const [minX, maxX] = d3.extent(xList);
    const [minY, maxY] = d3.extent(yList);
    return { minX, maxX, minY, maxY };
};
// TODO refactor for different scale types if possible
export const drawBars = ({ svg, chartPayload: { id, color, serie, animationSettings }, xScale, yScale, barPadding, formatterX, }) => {
    const rect = svg.selectAll(`rect.${id}`).data(serie);
    const [minY, maxY] = d3.extent(serie.map((el) => el[1]));
    if (!isDefined(minY) || !isDefined(maxY))
        return;
    rect.exit().remove();
    rect
        .enter()
        .append("rect")
        .attr("rx", 2)
        .attr("class", `${id}`)
        .attr("x", (d) => xScale(formatterX(d)) ?? 0)
        .attr("y", () => yScale(0))
        .attr("height", 0)
        .attr("width", () => xScale.bandwidth() - barPadding)
        .attr("fill", color);
    svg
        .selectAll(`rect.${id}`)
        .transition()
        .duration(800)
        // @ts-expect-error
        .attr("y", ([_, y]) => (y > 0 ? yScale(y) : yScale(0)))
        .attr("height", 
    // @ts-expect-error
    ([_, y]) => Math.abs(yScale(0) - yScale(y)))
        .delay((_, i) => i * 100);
};
function tweenDash() {
    // @ts-expect-error
    const l = this.getTotalLength();
    const i = d3.interpolateString("0," + l, l + "," + l);
    return function (t) {
        return i(t);
    };
}
function transition(duration) {
    return function (path) {
        path
            .transition()
            .duration(duration)
            .attrTween("stroke-dasharray", tweenDash);
    };
}
export const drawLine = ({ nonce, svg, chartPayload: { serie, id, color, yOffset, animationSettings }, xScale, yScale, strokeWidth, xOffset = 0, formatterX, }) => {
    const localNonce = nonce.current;
    setTimeout(() => {
        if (localNonce !== nonce.current)
            return;
        svg
            .append("path")
            .datum(serie)
            .attr("fill", "none")
            .attr("class", `${id}`)
            .attr("stroke", color)
            .attr("stroke-width", strokeWidth)
            .attr("d", d3
            .line()
            .x((d) => 
        // @ts-expect-error
        (xScale(formatterX(d)) ?? 0) + xOffset)
            .y(([_, y]) => yScale(y) - (yOffset ?? 0)))
            .call(transition(animationSettings?.lineDurationMs ?? 0));
    }, animationSettings?.lineDelayMs ?? 0);
};
export const drawGradient = ({ svg, chartPayload: { serie, color, animationSettings }, xScale, yScale, }) => {
    const id = `d3area${Math.random()}`;
    const [minY] = d3.extent(serie, (d) => d[1]);
    const gradient = svg
        .append("defs")
        .append("linearGradient")
        .attr("id", id)
        .attr("x1", "0%")
        .attr("x2", "0%")
        .attr("y1", "0%")
        .attr("y2", "100%");
    gradient
        .append("stop")
        .attr("offset", "0%")
        .style("stop-color", color)
        .style("stop-opacity", 0.4);
    gradient
        .append("stop")
        .attr("offset", "100%")
        .style("stop-color", color)
        .style("stop-opacity", 0);
    svg
        .append("path")
        .datum(serie)
        .attr("d", d3
        .area()
        .x(([x]) => xScale(x))
        .y0(([_, y]) => yScale(y))
        .y1(([_, y]) => yScale(y)))
        .attr("fill", `url(#${id})`)
        .attr("stroke", "none")
        .attr("opacity", 0)
        .transition()
        .duration(animationSettings?.gradientDurationMs ?? 0)
        .delay(animationSettings?.gradientDelayMs ?? 0)
        .attr("d", d3
        .area()
        .x(([x]) => xScale(x))
        .y0(yScale(minY))
        .y1(([_, y]) => yScale(y)))
        .attr("opacity", 1);
};
const bisectLeft = d3.bisector((d) => d[0]).left;
const getClosestPointId = ({ x, xScale, serie, }) => {
    const x0 = xScale.invert(x);
    const i = bisectLeft(serie, x0, 1, serie.length - 1);
    const d0 = serie[i - 1];
    const d1 = serie[i];
    const result = typeof d1 !== "undefined" && x0 - d0[0] > d1[0] - x0 ? i : i - 1;
    return result;
};
export const getClosest = ({ x, xScale, serie, }) => {
    const pointId = getClosestPointId({
        x,
        xScale,
        serie,
    });
    return serie[pointId];
};
//# sourceMappingURL=d3.js.map