{"version":3,"file":"tour-position.cjs","names":[],"sources":["../../../src/components/Tour/tour-position.ts"],"sourcesContent":["/** Where the card sits relative to the highlighted element. */\nexport type TourPlacement = \"top\" | \"bottom\" | \"left\" | \"right\" | \"center\";\n\n/** A rectangle in viewport coordinates. */\nexport interface TourRect {\n    top: number;\n    left: number;\n    width: number;\n    height: number;\n}\n\n/** Viewport size the placement is solved against. */\nexport interface TourViewport {\n    width: number;\n    height: number;\n}\n\n/** Gap between the highlighted element and the card. */\nconst OFFSET = 12;\n\n/** Smallest margin the card keeps from the viewport edge. */\nconst MARGIN = 8;\n\n/** Clamp a value into a range, tolerating a range narrower than the value. */\nfunction clamp(value: number, min: number, max: number): number {\n    return Math.max(min, Math.min(value, Math.max(min, max)));\n}\n\n/** Does the card fit on this side of the target? */\nfunction fits(\n    placement: TourPlacement,\n    target: TourRect,\n    card: { width: number; height: number },\n    viewport: TourViewport,\n): boolean {\n    switch (placement) {\n        case \"top\":\n            return target.top - OFFSET - card.height >= MARGIN;\n        case \"bottom\":\n            return target.top + target.height + OFFSET + card.height <= viewport.height - MARGIN;\n        case \"left\":\n            return target.left - OFFSET - card.width >= MARGIN;\n        case \"right\":\n            return target.left + target.width + OFFSET + card.width <= viewport.width - MARGIN;\n        default:\n            return true;\n    }\n}\n\n/** Sides tried, in order, when the preferred one does not fit. */\nconst FALLBACKS: Record<Exclude<TourPlacement, \"center\">, TourPlacement[]> = {\n    bottom: [\"bottom\", \"top\", \"right\", \"left\"],\n    top: [\"top\", \"bottom\", \"right\", \"left\"],\n    right: [\"right\", \"left\", \"bottom\", \"top\"],\n    left: [\"left\", \"right\", \"bottom\", \"top\"],\n};\n\n/**\n * Place the tour card next to the highlighted element.\n *\n * The preferred side is tried first and the opposite one second, because flipping\n * a \"below\" card to \"above\" keeps the reading relationship with the target;\n * jumping to a side would move the card across the screen for no reason the reader\n * can see.\n *\n * Nothing fitting anywhere ends at `center`: a card pinned half off-screen is worse\n * than a card in the middle, and this happens for real whenever the target is\n * taller than the viewport.\n *\n * The result is always clamped inside the viewport, so the card cannot be pushed\n * off the edge by a target near a corner.\n *\n * @param params.target - The highlighted element's rect, viewport coordinates.\n * @param params.card - Measured card size.\n * @param params.viewport - Viewport size.\n * @param params.preferred - Placement the step asked for. Default `\"bottom\"`.\n * @returns The chosen placement and the card's top/left.\n */\nexport function placeCard({\n    target,\n    card,\n    viewport,\n    preferred = \"bottom\",\n}: {\n    target: TourRect | null;\n    card: { width: number; height: number };\n    viewport: TourViewport;\n    preferred?: TourPlacement;\n}): { placement: TourPlacement; top: number; left: number } {\n    const centered = {\n        placement: \"center\" as TourPlacement,\n        top: Math.max(MARGIN, (viewport.height - card.height) / 2),\n        left: Math.max(MARGIN, (viewport.width - card.width) / 2),\n    };\n\n    // No target — a step whose element is not on the page still has to show its\n    // message, so it is shown centered rather than dropped.\n    if (!target || preferred === \"center\") return centered;\n\n    const order = FALLBACKS[preferred as Exclude<TourPlacement, \"center\">] ?? FALLBACKS.bottom;\n    const placement = order.find((candidate) => fits(candidate, target, card, viewport));\n    if (!placement) return centered;\n\n    const horizontalCenter = target.left + target.width / 2 - card.width / 2;\n    const verticalCenter = target.top + target.height / 2 - card.height / 2;\n\n    const raw =\n        placement === \"top\"\n            ? { top: target.top - OFFSET - card.height, left: horizontalCenter }\n            : placement === \"bottom\"\n              ? { top: target.top + target.height + OFFSET, left: horizontalCenter }\n              : placement === \"left\"\n                ? { top: verticalCenter, left: target.left - OFFSET - card.width }\n                : { top: verticalCenter, left: target.left + target.width + OFFSET };\n\n    return {\n        placement,\n        top: clamp(raw.top, MARGIN, viewport.height - card.height - MARGIN),\n        left: clamp(raw.left, MARGIN, viewport.width - card.width - MARGIN),\n    };\n}\n\n/**\n * The four backdrop rectangles that surround the highlighted element.\n *\n * Four rects rather than one overlay with a `box-shadow` hole: a box-shadow is not\n * hit-testable, so the \"dimmed\" area would not block clicks — and blocking the rest\n * of the page while leaving the highlighted control usable is the whole behaviour a\n * coachmark needs. Any rect that comes out empty is dropped, which is what happens\n * when the target touches an edge.\n *\n * @param target - Highlighted rect, or `null` for a full-screen backdrop.\n * @param viewport - Viewport size.\n * @param padding - Extra space kept clear around the target.\n */\nexport function backdropRects(\n    target: TourRect | null,\n    viewport: TourViewport,\n    padding = 4,\n): TourRect[] {\n    if (!target) {\n        return [{ top: 0, left: 0, width: viewport.width, height: viewport.height }];\n    }\n    const top = Math.max(0, target.top - padding);\n    const left = Math.max(0, target.left - padding);\n    const right = Math.min(viewport.width, target.left + target.width + padding);\n    const bottom = Math.min(viewport.height, target.top + target.height + padding);\n\n    return [\n        { top: 0, left: 0, width: viewport.width, height: top },\n        { top: bottom, left: 0, width: viewport.width, height: viewport.height - bottom },\n        { top, left: 0, width: left, height: bottom - top },\n        { top, left: right, width: viewport.width - right, height: bottom - top },\n    ].filter((rect) => rect.width > 0 && rect.height > 0);\n}\n"],"mappings":"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"}