"use client";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import { useId, useState, type ReactNode } from "react";
import { EASE_OUT, SPRING_LAYOUT } from "@/lib/ease";
import { useHoverCapable } from "@/lib/hooks/use-hover-capable";
import { cn } from "@/lib/utils";
export interface PreviewRailItem {
id: string;
label: string;
description?: ReactNode;
href: string;
target?: "_blank" | "_self" | "_parent" | "_top";
rel?: string;
}
export interface PreviewRailProps {
items: PreviewRailItem[];
orientation?: "vertical" | "horizontal";
activeId?: string;
defaultActiveId?: string;
onActiveChange?: (id: string) => void;
renderPreview?: (item: PreviewRailItem) => ReactNode;
children?: ReactNode;
className?: string;
railClassName?: string;
previewClassName?: string;
}
function DefaultPreview({ item }: { item: PreviewRailItem }) {
return (
{item.label}
{item.description ? (
{item.description}
) : null}
);
}
export function PreviewRail({
items,
orientation = "vertical",
activeId,
defaultActiveId,
onActiveChange,
renderPreview,
children,
className,
railClassName,
previewClassName,
}: PreviewRailProps) {
const uid = useId();
const reduce = useReducedMotion();
const canHover = useHoverCapable();
const [internalActiveId, setInternalActiveId] = useState(
defaultActiveId ?? items[0]?.id ?? "",
);
const [hoveredId, setHoveredId] = useState(null);
const [focusedId, setFocusedId] = useState(null);
const requestedActiveId = activeId ?? internalActiveId;
const selectedId = items.some((item) => item.id === requestedActiveId)
? requestedActiveId
: (items[0]?.id ?? "");
const displayedId = hoveredId ?? focusedId ?? "";
const displayedIndex = items.findIndex((item) => item.id === displayedId);
const rowTemplate = items.length
? `repeat(${items.length}, 1.25rem)`
: undefined;
const isHorizontal = orientation === "horizontal";
const selectItem = (id: string) => {
if (activeId === undefined) setInternalActiveId(id);
onActiveChange?.(id);
};
return (
{
if (!event.currentTarget.contains(event.relatedTarget)) {
setFocusedId(null);
}
}}
className={cn(
"isolate relative flex w-full overflow-visible",
isHorizontal
? "min-h-64 flex-col items-center justify-center"
: "min-h-80",
className,
)}
>
{items.map((item) => (
{item.id === displayedId ? (
{renderPreview ? (
renderPreview(item)
) : (
)}
) : null}
))}
{children ? (
{children}
) : null}
);
}