"use client"; import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@headlessui/react"; import clsx from "clsx"; import { Children, type ComponentPropsWithoutRef, type ReactNode, createContext, isValidElement, useContext, useEffect, useRef, useState, } from "react"; import { create } from "zustand"; import { Tag } from "./Tag"; const languageNames: Record = { js: "JavaScript", ts: "TypeScript", javascript: "JavaScript", typescript: "TypeScript", php: "PHP", python: "Python", ruby: "Ruby", go: "Go", }; function getPanelTitle({ title, language, }: { title?: string; language?: string; }) { if (title) { return title; } if (language && language in languageNames) { return languageNames[language]; } return "Code"; } function ClipboardIcon(props: ComponentPropsWithoutRef<"svg">) { return ( ); } function CopyButton({ code }: { code: string }) { const [copyCount, setCopyCount] = useState(0); const copied = copyCount > 0; useEffect(() => { if (copyCount > 0) { const timeout = setTimeout(() => setCopyCount(0), 1000); return () => { clearTimeout(timeout); }; } }, [copyCount]); return ( ); } function CodePanelHeader({ tag, label }: { tag?: string; label?: string }) { if (!(tag || label)) { return null; } return (
{tag && (
{tag}
)} {tag && label && ( )} {label && ( {label} )}
); } function CodePanel({ children, tag, label, code, }: { children: ReactNode; tag?: string; label?: string; code?: string; }) { const child = Children.only(children); if (isValidElement(child)) { tag = child.props.tag ?? tag; label = child.props.label ?? label; code = child.props.code ?? code; } if (!code) { throw new Error( "`CodePanel` requires a `code` prop, or a child with a `code` prop.", ); } return (
                    {children}
                
); } function CodeGroupHeader({ title, children, selectedIndex, }: { title: string; children: ReactNode; selectedIndex: number; }) { const hasTabs = Children.count(children) > 1; if (!(title || hasTabs)) { return null; } return (
{title && (

{title}

)} {hasTabs && ( {Children.map(children, (child, childIndex) => ( {getPanelTitle( isValidElement(child) ? child.props : {}, )} ))} )}
); } function CodeGroupPanels({ children, ...props }: ComponentPropsWithoutRef) { const hasTabs = Children.count(children) > 1; if (hasTabs) { return ( {Children.map(children, (child) => ( {child} ))} ); } return {children}; } function usePreventLayoutShift() { const positionRef = useRef(null); const rafRef = useRef(); useEffect(() => { return () => { if (typeof rafRef.current !== "undefined") { window.cancelAnimationFrame(rafRef.current); } }; }, []); return { positionRef, preventLayoutShift(callback: () => void) { if (!positionRef.current) { return; } const initialTop = positionRef.current.getBoundingClientRect().top; callback(); rafRef.current = window.requestAnimationFrame(() => { const newTop = positionRef.current?.getBoundingClientRect().top ?? initialTop; window.scrollBy(0, newTop - initialTop); }); }, }; } const usePreferredLanguageStore = create<{ preferredLanguages: string[]; addPreferredLanguage: (language: string) => void; }>()((set) => ({ preferredLanguages: [], addPreferredLanguage: (language) => set((state) => ({ preferredLanguages: [ ...state.preferredLanguages.filter( (preferredLanguage) => preferredLanguage !== language, ), language, ], })), })); function useTabGroupProps(availableLanguages: string[]) { const { preferredLanguages, addPreferredLanguage } = usePreferredLanguageStore(); const [selectedIndex, setSelectedIndex] = useState(0); const activeLanguage = [...availableLanguages].sort( (a, z) => preferredLanguages.indexOf(z) - preferredLanguages.indexOf(a), )[0]; const languageIndex = availableLanguages.indexOf(activeLanguage); const newSelectedIndex = languageIndex === -1 ? selectedIndex : languageIndex; if (newSelectedIndex !== selectedIndex) { setSelectedIndex(newSelectedIndex); } const { positionRef, preventLayoutShift } = usePreventLayoutShift(); return { as: "div" as const, ref: positionRef, selectedIndex, onChange: (newSelectedIndex: number) => { preventLayoutShift(() => addPreferredLanguage(availableLanguages[newSelectedIndex]), ); }, }; } const CodeGroupContext = createContext(false); export function CodeGroup({ children, title, ...props }: ComponentPropsWithoutRef & { title: string }) { const languages = Children.map(children, (child) => getPanelTitle(isValidElement(child) ? child.props : {}), ) ?? []; const tabGroupProps = useTabGroupProps(languages); const hasTabs = Children.count(children) > 1; const containerClassName = "my-6 overflow-hidden rounded-2xl bg-zinc-900 shadow-md dark:ring-1 dark:ring-white/10"; const header = ( {children} ); const panels = {children}; return ( {hasTabs ? (
{header} {panels}
) : (
{header} {panels}
)}
); } export function Code({ children, ...props }: ComponentPropsWithoutRef<"code">) { const isGrouped = useContext(CodeGroupContext); if (isGrouped) { if (typeof children !== "string") { throw new Error( "`Code` children must be a string when nested inside a `CodeGroup`.", ); } return ( // biome-ignore lint/security/noDangerouslySetInnerHtml: // biome-ignore lint/style/useNamingConvention: ); } return {children}; } export function Pre({ children, ...props }: ComponentPropsWithoutRef) { const isGrouped = useContext(CodeGroupContext); if (isGrouped) { return children; } return {children}; }