import * as d3 from "d3";
const TOOLTIP_THRESHOLD = 30; //px
export const formatterDateDay = (timestamp) => {
    const d = new Date(timestamp.valueOf());
    const ye = new Intl.DateTimeFormat("en", { year: "numeric" }).format(d);
    const mo = new Intl.DateTimeFormat("en", { month: "short" }).format(d);
    const da = new Intl.DateTimeFormat("en", { day: "2-digit" }).format(d);
    return `${mo} ${da}, ${ye}`;
};
export const formatterDateMonth = (timestamp) => {
    const d = new Date(timestamp.valueOf());
    const ye = new Intl.DateTimeFormat("en", { year: "numeric" }).format(d);
    const mo = new Intl.DateTimeFormat("en", { month: "short" }).format(d);
    return `${mo} ${ye}`;
};
export const toHex = (s) => s
    .split("")
    .map((el) => el.charCodeAt(0).toString(16))
    .join("");
export const getTooltipClass = (graph, serie) => `id${toHex(graph)}__${toHex(serie)}`;
export 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];
};
export const getClosestAddon = ({ x, xScale, serie, additionalDataSerie, }) => {
    const pointId = getClosestPointId({
        x,
        xScale,
        serie,
    });
    return additionalDataSerie[pointId];
};
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 };
};
export const drawXAxis = ({ svg, xScale, height, margin, showGrid, ticksX, formatterX, scaleFonts, }) => {
    const xAxis = d3
        .axisBottom(xScale)
        .ticks(ticksX)
        .tickFormat(formatterX)
        .tickSizeOuter(0);
    showGrid.vertical && xAxis.tickSize(-height + margin.top + margin.bottom);
    svg
        .append("g")
        .classed(`xAxis`, true)
        .call(xAxis)
        .attr("transform", "translate(0, " + (height - margin.bottom) + ")")
        .attr("font-size", scaleFonts.fontSize)
        .attr("font-family", scaleFonts.fontFamily);
};
export const drawYAxis = ({ svg, yScaleLeft, yScaleRight, width, margin, showGrid, ticksY, formatterYLeft, formatterYRight, scaleFonts, }) => {
    if (yScaleLeft) {
        const yAxisLeft = d3
            .axisLeft(yScaleLeft)
            .ticks(ticksY)
            .tickSizeOuter(0)
            .tickFormat(formatterYLeft);
        showGrid.horizontal &&
            yAxisLeft.tickSize(-width + margin.right + margin.left);
        svg
            .append("g")
            .classed(`yAxis`, true)
            .call(yAxisLeft)
            .attr("transform", "translate(" + margin.left + ", 0)")
            .attr("font-size", scaleFonts.fontSize)
            .attr("font-family", scaleFonts.fontFamily);
    }
    if (yScaleRight) {
        const yAxisRight = d3
            .axisRight(yScaleRight)
            .tickSizeOuter(0)
            .tickFormat(formatterYRight);
        svg
            .append("g")
            .classed(`yAxis`, true)
            .call(yAxisRight)
            .attr("transform", "translate(" + (width - margin.right) + ", 0)");
    }
};
export const drawGradient = ({ svg, color, serie, 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("fill", `url(#${id})`)
        .attr("stroke", "none")
        .attr("d", d3
        .area()
        .x((d) => xScale(d[0]))
        .y0(yScale(minY))
        .y1((d) => yScale(d[1])));
};
export const drawLine = ({ svg, color, serie, xScale, yScale, lineWidth, }) => {
    svg
        .append("path")
        .datum(serie)
        .attr("fill", "none")
        .attr("stroke", color)
        .attr("stroke-width", lineWidth)
        .attr("d", d3
        .line()
        .x((d) => xScale(d[0]))
        .y((d) => yScale(d[1])));
};
export const drawFocus = ({ svg, color, }) => {
    // graph point highlight styling
    const focus = svg.append("g");
    focus
        .append("circle")
        .style("fill", color)
        .style("opacity", 0.3)
        .attr("r", 8);
    focus.append("circle").style("fill", "white").attr("r", 4);
    focus.append("circle").style("fill", color).attr("r", 3);
    focus.style("visibility", "hidden");
    return focus;
};
export const highlightPoint = (x, y) => {
    const point = d3.select(`.ui-d3linechart__highlighted-point`);
    point
        .style("opacity", 1)
        .style("left", x + "px")
        .style("top", y + "px");
};
export const showTooltips = ({ x, tooltips, xScale, width, tooltipFormatter, }) => {
    const tooltip = tooltips[0].tooltip;
    const closest = tooltips
        .reduce((acc, { point, yScale }) => {
        return [acc[0] + xScale(point[0]), acc[1] + yScale(point[1])];
    }, [0, 0])
        .map((el) => el / tooltips.length);
    tooltips.slice(1).forEach(({ tooltip }) => hideTooltip(tooltip));
    const dx = x > width / 2 && tooltip.node
        ? -(tooltip.node().offsetWidth + 18)
        : 18;
    const dy = -tooltip.node().offsetHeight / 2;
    const html = tooltips.reduce((acc, el) => acc +
        (tooltipFormatter
            ? tooltipFormatter(el)
            : `<div class="tooltip-text">
            <div class="tooltip-text__marker" style="border: 5px solid ${el.color}"></div>
            <div class="tooltip-text__title">${el.point[1]}</div>
          </div>`), "");
    tooltip
        .classed("left", x < width / 2)
        .classed("right", x >= width / 2)
        .classed("multiple", tooltips.length > 1)
        .style("opacity", 1)
        .style("left", closest[0] + dx + "px")
        .style("top", closest[1] + dy + "px")
        .html(`<div class="tooltip-group">${html}</div>`);
};
export const showFocus = ({ focus, xScale, yScale, closest, }) => {
    focus
        .style("visibility", "visible")
        .selectAll("circle")
        .attr("transform", `translate(${xScale(closest[0])}, ${yScale(closest[1])})`);
};
export const hideFocus = (focus) => {
    focus.style("visibility", "hidden");
};
export const hideTooltip = (tooltip) => {
    tooltip.style("opacity", 0);
};
export const groupTooltipPoints = (points) => {
    // sort by y coordinates on chart
    const sorted = points.sort((a, b) => a.yScale(a.point[1]) - a.yScale(b.point[1]));
    const groups = sorted.reduce((acc, current, i, arr) => {
        if (i === 0)
            return [[current]];
        const previous = arr[i - 1];
        // if tooltips are closer than threshold
        // => group them
        const yCurrent = current.yScale(current.point[1]);
        const yPrevious = previous.yScale(previous.point[1]);
        if (Math.abs(yCurrent - yPrevious) < TOOLTIP_THRESHOLD) {
            // [..., [..., previous]] => [..., [..., previous, current]]
            acc[acc.length - 1] = [...acc[acc.length - 1], current];
            return acc;
        }
        return [...acc, [current]];
    }, []);
    return groups;
};
//# sourceMappingURL=hooks.js.map