{"version":3,"file":"sparkline-geometry.cjs","names":[],"sources":["../../../src/components/Sparkline/sparkline-geometry.ts"],"sourcesContent":["/** A point in the sparkline's own coordinate space. */\nexport interface SparkPoint {\n    x: number;\n    y: number;\n    /** The datum this point came from. */\n    value: number;\n    /** Its position in the input series. */\n    index: number;\n}\n\nexport interface SparkGeometryOptions {\n    values: readonly number[];\n    width: number;\n    height: number;\n    /** Room reserved on every side for the end marker's ring, so it is never clipped. */\n    padding?: number;\n    /** Force the low end of the value axis. Defaults to the series minimum. */\n    min?: number;\n    /** Force the high end. Defaults to the series maximum. */\n    max?: number;\n}\n\n/**\n * Project a series onto the drawing box.\n *\n * `y` is inverted — SVG grows downward and a chart grows upward — and a flat series\n * is centred rather than pinned to an edge, which is the honest reading of \"no\n * variation\" and avoids dividing by a zero-height domain.\n *\n * Non-finite values are dropped rather than drawn: a `NaN` in the middle of a path\n * silently voids the whole `d` attribute and the sparkline disappears with no error.\n *\n * @param options - The series and the box to fit it in.\n * @returns The projected points, in input order.\n */\nexport function sparkPoints({\n    values,\n    width,\n    height,\n    padding = 2,\n    min,\n    max,\n}: SparkGeometryOptions): SparkPoint[] {\n    const usable = values\n        .map((value, index) => ({ value, index }))\n        .filter((item) => Number.isFinite(item.value));\n    if (usable.length === 0 || width <= 0 || height <= 0) return [];\n\n    const numbers = usable.map((item) => item.value);\n    const lo = min ?? Math.min(...numbers);\n    const hi = max ?? Math.max(...numbers);\n    const span = hi - lo;\n\n    const innerW = Math.max(0, width - padding * 2);\n    const innerH = Math.max(0, height - padding * 2);\n    const lastIndex = usable.length - 1;\n\n    return usable.map((item, i) => {\n        const t = lastIndex === 0 ? 0.5 : i / lastIndex;\n        const v = span === 0 ? 0.5 : (item.value - lo) / span;\n        return {\n            x: padding + t * innerW,\n            y: padding + (1 - Math.min(1, Math.max(0, v))) * innerH,\n            value: item.value,\n            index: item.index,\n        };\n    });\n}\n\n/** An SVG `d` for a polyline through the points. */\nexport function linePath(points: readonly SparkPoint[]): string {\n    if (points.length === 0) return \"\";\n    if (points.length === 1) {\n        // A lone point has no line; emit a zero-length segment so the round cap\n        // still paints a dot rather than nothing at all.\n        const [only] = points;\n        return `M ${only.x} ${only.y} L ${only.x} ${only.y}`;\n    }\n    return points.map((p, i) => `${i === 0 ? \"M\" : \"L\"} ${p.x} ${p.y}`).join(\" \");\n}\n\n/**\n * An SVG `d` for the area under the line, closed along the baseline.\n *\n * @param points - The projected points.\n * @param baselineY - Where the fill closes, in the same space as the points.\n */\nexport function areaPath(points: readonly SparkPoint[], baselineY: number): string {\n    if (points.length === 0) return \"\";\n    const line = points.map((p, i) => `${i === 0 ? \"M\" : \"L\"} ${p.x} ${p.y}`).join(\" \");\n    const first = points[0];\n    const last = points[points.length - 1];\n    return `${line} L ${last.x} ${baselineY} L ${first.x} ${baselineY} Z`;\n}\n\n/** Bar rectangles growing from the baseline, with the band's leftover left as air. */\nexport function barRects(\n    points: readonly SparkPoint[],\n    { width, baselineY, gap = 2 }: { width: number; baselineY: number; gap?: number },\n): { x: number; y: number; width: number; height: number; point: SparkPoint }[] {\n    if (points.length === 0) return [];\n    const band = width / points.length;\n    // Never fill the slot: the gap between bars is what separates them, not a stroke.\n    const barWidth = Math.max(1, Math.min(24, band - gap));\n    return points.map((point) => {\n        /*\n         * The minimum value sits on the baseline, where the raw height is 0. It still\n         * needs a visible sliver — but growing *up* from the baseline, not down: the\n         * naive `Math.max(1, …)` on height alone leaves the bar hanging one pixel\n         * below the axis it is supposed to stand on.\n         */\n        const height = Math.max(1, Math.abs(baselineY - point.y));\n        return {\n            x: point.x - barWidth / 2,\n            y: Math.min(point.y, baselineY - height),\n            width: barWidth,\n            height,\n            point,\n        };\n    });\n}\n\n/**\n * A one-sentence description of the series, for the chart's accessible name.\n *\n * A sparkline has no axes and no legend, so without this it is an unlabelled image:\n * a screen reader reaches it and reads nothing. Direction is stated in words rather\n * than implied by the shape.\n *\n * @param values - The series, in order.\n * @param format - How to render a single value.\n * @returns Text suitable for `aria-label`.\n */\nexport function describeSeries(\n    values: readonly number[],\n    format: (value: number) => string = String,\n): string {\n    const usable = values.filter((v) => Number.isFinite(v));\n    if (usable.length === 0) return \"Sem dados\";\n    if (usable.length === 1) return `Valor único: ${format(usable[0])}`;\n\n    const first = usable[0];\n    const last = usable[usable.length - 1];\n    const direction = last > first ? \"subindo\" : last < first ? \"descendo\" : \"estável\";\n    return (\n        `${usable.length} pontos, ${direction}. ` +\n        `Início ${format(first)}, fim ${format(last)}. ` +\n        `Mínimo ${format(Math.min(...usable))}, máximo ${format(Math.max(...usable))}.`\n    );\n}\n"],"mappings":"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"}