import * as React from "react"; import { QuestionIcon } from "@phosphor-icons/react"; import { Button, FieldLabel, PrimitiveArrowIcon, ScrollFade, Tooltip, TooltipContent, TooltipTrigger, } from "../primitives"; import { cn } from "../../lib/utils"; type ControlFieldLabelActionContextValue = { action: React.ReactNode; label: string; }; type ControlFieldLabelHelpContextValue = { help: string; label: string; }; const ControlFieldLabelActionContext = React.createContext(null); const ControlFieldLabelHelpContext = React.createContext(null); export const panelSectionSurfaceClassName = [ "flex flex-col pt-2 pb-6 first:border-t-0 data-[toolcraft-section-actions]:first:border-t", "border-t border-[color:color-mix(in_oklab,var(--border)_8%,transparent)]", "transition-colors duration-150 ease-out hover:bg-[color:color-mix(in_oklab,var(--foreground)_3%,transparent)]", ].join(" "); export function ControlSection({ children, className, ...props }: React.HTMLAttributes): React.JSX.Element { return (
{children}
); } export function ControlList({ children, className, }: { children: React.ReactNode; className?: string; }): React.JSX.Element { return (
{children}
); } export function ControlInlineGroup({ children, className, columns = 2, kind = "default", style, ...props }: React.HTMLAttributes & { columns?: number; kind?: "default" | "slider" | "toggleParameter"; }): React.JSX.Element { const gridTemplateColumns = `repeat(${Math.max( 1, Math.floor(columns), )}, minmax(0, 1fr))`; return (
{children}
); } export function ControlSectionHeader({ action, collapsed = false, collapsible = false, children, onCollapsedChange, }: { action?: React.ReactNode; collapsed?: boolean; collapsible?: boolean; children: React.ReactNode; onCollapsedChange?: (collapsed: boolean) => void; }): React.JSX.Element { const titleText = getControlSectionHeaderText(children); const collapseLabel = collapsed ? `Expand ${titleText} section` : `Collapse ${titleText} section`; const toggleCollapsed = React.useCallback(() => { if (!collapsible) { return; } onCollapsedChange?.(!collapsed); }, [collapsed, collapsible, onCollapsedChange]); function handleKeyDown(event: React.KeyboardEvent): void { if (!collapsible || (event.key !== "Enter" && event.key !== " ")) { return; } event.preventDefault(); toggleCollapsed(); } function stopHeaderToggle(event: React.SyntheticEvent): void { event.stopPropagation(); } return (
{children}
{action || collapsible ? (
{action} {collapsible ? ( } > {collapseLabel} ) : null}
) : null}
); } function getControlSectionHeaderText(children: React.ReactNode): string { const text = getReactNodeText(children).trim(); return text.length > 0 ? text : "section"; } function getReactNodeText(node: React.ReactNode): string { if (typeof node === "string" || typeof node === "number") { return String(node); } if (!React.isValidElement<{ children?: React.ReactNode }>(node)) { if (Array.isArray(node)) { return node.map(getReactNodeText).join(""); } return ""; } return getReactNodeText(node.props.children); } export function ControlItem({ allowCompoundDividers = false, children, compoundDividerPlacement = "both", flush = false, }: { allowCompoundDividers?: boolean; children: React.ReactNode; compoundDividerPlacement?: "both" | "bottom" | "top"; flush?: boolean; }): React.JSX.Element { const showCompoundTopDivider = compoundDividerPlacement === "both" || compoundDividerPlacement === "top"; const showCompoundBottomDivider = compoundDividerPlacement === "both" || compoundDividerPlacement === "bottom"; return (
{children}
); } export function PanelTitle({ children, }: { children: React.ReactNode; }): React.JSX.Element { return (

{children}

); } export type ControlFieldLabelProps = React.ComponentProps & { textClassName?: string; }; export function ControlFieldLabelActionProvider({ action, children, label, }: ControlFieldLabelActionContextValue & { children: React.ReactNode; }): React.JSX.Element { return ( {children} ); } export function ControlFieldLabelHelpProvider({ children, help, label, }: ControlFieldLabelHelpContextValue & { children: React.ReactNode; }): React.JSX.Element { return ( {children} ); } export function useControlFieldLabelAction( label: string | undefined, ): React.ReactNode { const context = React.useContext(ControlFieldLabelActionContext); return context && context.label === label ? context.action : null; } export function useControlFieldLabelHelp( label: string | undefined, ): string | null { const context = React.useContext(ControlFieldLabelHelpContext); return context && context.label === label ? context.help : null; } export function ControlFieldLabel({ children, className, textClassName, title: titleProp, ...props }: ControlFieldLabelProps): React.JSX.Element { const title = getControlFieldLabelTitle(children); const displayChildren = getControlFieldLabelDisplayChildren(children, title); const labelAction = useControlFieldLabelAction(title); const labelHelp = useControlFieldLabelHelp(title); return ( {displayChildren} {labelHelp ? ( } > {labelHelp} ) : null} {labelAction} ); } function getControlFieldLabelTitle( children: React.ReactNode, ): string | undefined { const textParts = React.Children.toArray(children).map((child) => { if (typeof child === "string" || typeof child === "number") { return String(child); } return null; }); if (!textParts.length || textParts.some((part) => part === null)) { return undefined; } const title = textParts.join("").trim(); return title || undefined; } function getControlFieldLabelDisplayChildren( children: React.ReactNode, title: string | undefined, ): React.ReactNode { if (!title || React.Children.count(children) !== 1) { return children; } const [child] = React.Children.toArray(children); if (typeof child !== "string" && typeof child !== "number") { return children; } const displayTitle = title.replace(/\s+\([^)]{1,80}\)\s*$/u, "").trim(); return displayTitle || title; }