import { Children, createContext, useContext, useEffect, useRef, useState, } from 'react' import { Tab } from '@headlessui/react' import clsx from 'clsx' import { create } from 'zustand' import { Tag } from '@/components/Tag' const languageNames = { js: 'JavaScript', ts: 'TypeScript', javascript: 'JavaScript', typescript: 'TypeScript', php: 'PHP', python: 'Python', ruby: 'Ruby', go: 'Go', } function getPanelTitle({ title, language }) { return title ?? languageNames[language] ?? 'Code' } function ClipboardIcon(props) { return ( ) } function CopyButton({ code }) { let [copyCount, setCopyCount] = useState(0) let copied = copyCount > 0 useEffect(() => { if (copyCount > 0) { let timeout = setTimeout(() => setCopyCount(0), 1000) return () => { clearTimeout(timeout) } } }, [copyCount]) return ( ) } function CodePanelHeader({ tag, label }) { if (!tag && !label) { return null } return (
{tag && (
{tag}
)} {tag && label && ( )} {label && ( {label} )}
) } function CodePanel({ tag, label, code, children }) { let child = Children.only(children) return (
{children}
) } function CodeGroupHeader({ title, children, selectedIndex }) { let hasTabs = Children.count(children) > 1 if (!title && !hasTabs) { return null } return (
{title && (

{title}

)} {hasTabs && ( {Children.map(children, (child, childIndex) => ( {getPanelTitle(child.props)} ))} )}
) } function CodeGroupPanels({ children, ...props }) { let hasTabs = Children.count(children) > 1 if (hasTabs) { return ( {Children.map(children, (child) => ( {child} ))} ) } return {children} } function usePreventLayoutShift() { let positionRef = useRef() let rafRef = useRef() useEffect(() => { return () => { window.cancelAnimationFrame(rafRef.current) } }, []) return { positionRef, preventLayoutShift(callback) { let initialTop = positionRef.current.getBoundingClientRect().top callback() rafRef.current = window.requestAnimationFrame(() => { let newTop = positionRef.current.getBoundingClientRect().top window.scrollBy(0, newTop - initialTop) }) }, } } const usePreferredLanguageStore = create((set) => ({ preferredLanguages: [], addPreferredLanguage: (language) => set((state) => ({ preferredLanguages: [ ...state.preferredLanguages.filter( (preferredLanguage) => preferredLanguage !== language ), language, ], })), })) function useTabGroupProps(availableLanguages) { let { preferredLanguages, addPreferredLanguage } = usePreferredLanguageStore() let [selectedIndex, setSelectedIndex] = useState(0) let activeLanguage = [...availableLanguages].sort( (a, z) => preferredLanguages.indexOf(z) - preferredLanguages.indexOf(a) )[0] let languageIndex = availableLanguages.indexOf(activeLanguage) let newSelectedIndex = languageIndex === -1 ? selectedIndex : languageIndex if (newSelectedIndex !== selectedIndex) { setSelectedIndex(newSelectedIndex) } let { positionRef, preventLayoutShift } = usePreventLayoutShift() return { as: 'div', ref: positionRef, selectedIndex, onChange: (newSelectedIndex) => { preventLayoutShift(() => addPreferredLanguage(availableLanguages[newSelectedIndex]) ) }, } } const CodeGroupContext = createContext(false) export function CodeGroup({ children, title, ...props }) { let languages = Children.map(children, (child) => getPanelTitle(child.props)) let tabGroupProps = useTabGroupProps(languages) let hasTabs = Children.count(children) > 1 let Container = hasTabs ? Tab.Group : 'div' let containerProps = hasTabs ? tabGroupProps : {} let headerProps = hasTabs ? { selectedIndex: tabGroupProps.selectedIndex } : {} return ( {children} {children} ) } export function Code({ children, ...props }) { let isGrouped = useContext(CodeGroupContext) if (isGrouped) { return } return {children} } export function Pre({ children, ...props }) { let isGrouped = useContext(CodeGroupContext) if (isGrouped) { return children } return {children} }