"use client"; import { Button } from "@prototype/components/ui/button"; import { Label } from "@prototype/components/ui/label"; import { OptionSelect } from "@prototype/components/ui/option-select"; import { ScrollArea } from "@prototype/components/ui/scroll-area"; import { Slider } from "@prototype/components/ui/slider"; import { Switch } from "@prototype/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@prototype/components/ui/tabs"; import { ToggleGroup, ToggleGroupItem } from "@prototype/components/ui/toggle-group"; import { getPrototypeShareCommand, getPrototypeSlugFromPathname, } from "@prototype/lib/prototypes/share-command"; import { cn } from "@prototype/lib/utils"; import { ArrowLeft, Copy, Info, SlidersHorizontal, Terminal, } from "lucide-react"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { Children, isValidElement, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, type ReactElement, type ReactNode, } from "react"; import { toast } from "sonner"; import { usePrototypeReviewOptional } from "@prototype/lib/prototypes/prototype-review-context"; import { PrototypeReferenceDocs } from "./prototype-reference-docs"; type PrototypeControlsTabProps = { value: string; label: string; children: ReactNode; }; type PrototypeControlsFieldProps = { label: string; htmlFor?: string; description?: string; children: ReactNode; className?: string; }; type PrototypeControlsToggleProps = { label: string; checked: boolean; onCheckedChange: (checked: boolean) => void; disabled?: boolean; id?: string; }; type PrototypeControlsSelectProps = { label: string; value: T; onValueChange: (value: T) => void; options: { value: T; label: string }[]; disabled?: boolean; }; type PrototypeControlsSliderProps = { label: string; value: number[]; onValueChange: (value: number[]) => void; min?: number; max?: number; step?: number; formatValue?: (value: number) => string; disabled?: boolean; }; type PrototypeControlsDropdownProps = { label: string; value: T; onValueChange: (value: T) => void; options: { value: T; label: string }[]; disabled?: boolean; }; type PrototypeControlsToggleGroupProps = { label: string; type?: "single" | "multiple"; value?: T | T[]; onValueChange?: (value: T | T[]) => void; options: { value: T; label: string }[]; disabled?: boolean; }; function PrototypeControlsTab(_props: PrototypeControlsTabProps) { return null; } PrototypeControlsTab.displayName = "PrototypeControlsTab"; function isPrototypeControlsTab( child: ReactNode, ): child is ReactElement { return ( isValidElement(child) && (child.type as { displayName?: string }).displayName === "PrototypeControlsTab" ); } function PrototypeControlsField({ label, htmlFor, description, children, className, }: PrototypeControlsFieldProps) { return (
{description ? (

{description}

) : null} {children}
); } function PrototypeControlsToggle({ label, checked, onCheckedChange, disabled, id, }: PrototypeControlsToggleProps) { const generatedId = useId(); const fieldId = id ?? generatedId; return (
); } function PrototypeControlsSelect({ label, value, onValueChange, options, disabled, }: PrototypeControlsSelectProps) { return ( ); } function PrototypeControlsSlider({ label, value, onValueChange, min = 0, max = 100, step = 1, formatValue = (nextValue) => String(nextValue), disabled, }: PrototypeControlsSliderProps) { const currentValue = value[0] ?? min; return (
{formatValue(currentValue)}
); } function PrototypeControlsDropdown({ label, value, onValueChange, options, disabled, }: PrototypeControlsDropdownProps) { return ( ); } function PrototypeControlsToggleGroup({ label, type = "single", value, onValueChange, options, disabled, }: PrototypeControlsToggleGroupProps) { const items = options.map((option) => ( {option.label} )); return ( {type === "multiple" ? ( { onValueChange?.(nextValue as T[]); }} disabled={disabled} className="w-full" > {items} ) : ( { onValueChange?.(nextValue as T); }} disabled={disabled} className="w-full" > {items} )} ); } type PrototypeShareCommandButtonProps = { command: string; }; export function PrototypeShareCommandButton({ command, }: PrototypeShareCommandButtonProps) { return ( ); } type PrototypeControlsProps = { children: ReactNode; defaultOpen?: boolean; className?: string; slug?: string; }; type PrototypeControlsPanelProps = { children: ReactNode; tabs: Array<{ value: string; label: string; content: ReactNode }>; hasTabs: boolean; }; function PrototypeControlsPanel({ children, tabs, hasTabs, }: PrototypeControlsPanelProps) { const defaultTab = tabs[0]?.value; return hasTabs ? ( {tabs.length > 1 ? (
{tabs.map((tab) => ( {tab.label} ))}
) : null} {tabs.map((tab) => (
{tab.content}
))}
) : (
{children}
); } type PrototypeControlsActionsProps = { shareCommand: string | null; slug?: string | null; className?: string; }; export function PrototypeGalleryBackButton({ className }: { className?: string }) { return ( ); } export function PrototypeControlsActions({ shareCommand, slug, className, }: PrototypeControlsActionsProps) { return (
{shareCommand ? ( ) : null} {slug ? : null}
); } function PrototypeControlsRoot({ children, defaultOpen = false, className, slug: slugProp, }: PrototypeControlsProps) { const pathname = usePathname(); const review = usePrototypeReviewOptional(); const [tweaksOpen, setTweaksOpen] = useState(defaultOpen); const [settingsOpen, setSettingsOpen] = useState(false); const slug = slugProp ?? getPrototypeSlugFromPathname(pathname); const shareCommand = slug ? getPrototypeShareCommand(slug) : null; const tabs = useMemo(() => { return Children.toArray(children) .filter(isPrototypeControlsTab) .map((child) => ({ value: child.props.value, label: child.props.label, content: child.props.children, })); }, [children]); const hasTabs = tabs.length > 0; const setTweaksContent = review?.setTweaksContent; const setTweaksContentRef = useRef(setTweaksContent); setTweaksContentRef.current = setTweaksContent; useLayoutEffect(() => { setTweaksContentRef.current?.(children); }, [children]); useEffect(() => { return () => setTweaksContentRef.current?.(null); }, []); if (review) { return null; } const toggleTweaks = () => { setTweaksOpen((current) => { const next = !current; if (next) setSettingsOpen(false); return next; }); }; const toggleSettings = () => { setSettingsOpen((current) => { const next = !current; if (next) setTweaksOpen(false); return next; }); }; return (
{children}
); } export const PrototypeControls = Object.assign(PrototypeControlsRoot, { Tab: PrototypeControlsTab, Field: PrototypeControlsField, Toggle: PrototypeControlsToggle, Select: PrototypeControlsSelect, Slider: PrototypeControlsSlider, Dropdown: PrototypeControlsDropdown, ToggleGroup: PrototypeControlsToggleGroup, });