import { useState, type ReactNode } from "react"; import { useSearchParams } from "next/navigation"; // Selects the active tab from the `?tab=` param, falling back when it names a // tab this viewer can't see. The tab list is usually derived from identity or // entitlement data that resolves after the first render, so the param has to // be re-checked every render rather than validated once in a useState // initializer — until the viewer picks a tab, which wins from then on. export function useTabParam( tabs: { value: T }[], fallback: T, ): [T, (tab: T) => void] { let requested = useSearchParams().get("tab"); let [chosen, setChosen] = useState(null); let tab = chosen ?? (tabs.some((t) => t.value === requested) ? (requested as T) : fallback); return [tab, setChosen]; } export function Tabs(props: { value: T; onChange: (value: T, e?: React.MouseEvent) => void; options: { value: T; label: ReactNode }[]; className?: string; optionClassName?: string; selectedOptionClassName?: string; pill?: boolean; }) { return (
{props.options.map((option) => { const selected = props.value === option.value; let pillStyles = `text-sm font-bold px-1 ${ selected ? `text-accent-2 bg-accent-1 rounded-md ${props.selectedOptionClassName || ""}` : "text-tertiary bg-transparent" }`; let tabStyles = `text-sm font-bold pb-0.5 px-1 border-b-3 ${ selected ? `text-accent-contrast border-accent-contrast ${props.selectedOptionClassName || ""}` : "text-tertiary border-transparent" } `; return ( ); })}
{!props.pill &&
}
); }