import type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from 'react'; import { PlatformFieldLabel } from './PlatformFieldLabel'; type PlatformSubpanelStaticElement = 'section' | 'div' | 'article' | 'aside'; type PlatformSubpanelElement = PlatformSubpanelStaticElement | 'button'; type PlatformSubpanelPadding = | 'tight' | 'row' | 'xs' | 'sm' | 'md' | 'lg' | 'none'; type PlatformSubpanelRadius = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; type PlatformSubpanelSurface = | 'platform' | 'flat' | 'soft' | 'dark' | 'darkSky' | 'darkEmerald' | 'darkAmber' | 'darkRose' | 'danger'; type PlatformSubpanelTitleVariant = 'section' | 'strong'; type PlatformSubpanelBaseProps = { as?: PlatformSubpanelElement; title?: ReactNode; titleVariant?: PlatformSubpanelTitleVariant; actions?: ReactNode; children: ReactNode; interactive?: boolean; padding?: PlatformSubpanelPadding; radius?: PlatformSubpanelRadius; surface?: PlatformSubpanelSurface; className?: string; headerClassName?: string; titleClassName?: string; actionsClassName?: string; bodyClassName?: string; }; type PlatformSubpanelStaticProps = PlatformSubpanelBaseProps & Omit, 'children' | 'className' | 'title'> & { as?: PlatformSubpanelStaticElement; }; type PlatformSubpanelButtonProps = PlatformSubpanelBaseProps & Omit, 'children' | 'title'> & { as: 'button'; }; type PlatformSubpanelProps = | PlatformSubpanelStaticProps | PlatformSubpanelButtonProps; const PLATFORM_SUBPANEL_PADDING_CLASS: Record = { tight: 'p-2', row: 'px-3 py-2', xs: 'px-3 py-2.5', sm: 'p-3', md: 'p-4', lg: 'p-4 sm:p-5', none: 'p-0', }; const PLATFORM_SUBPANEL_RADIUS_CLASS: Record = { xs: 'rounded-xl', sm: 'rounded-[1rem]', md: 'rounded-[1.25rem]', lg: 'rounded-[1.35rem]', xl: 'rounded-[1.5rem]', }; const PLATFORM_SUBPANEL_SURFACE_CLASS: Record = { platform: 'platform-subpanel', flat: 'border border-[var(--platform-subpanel-border)] bg-white/72', soft: 'border border-[var(--platform-subpanel-border)] bg-white/68', dark: 'border border-white/10 bg-black/25 text-zinc-100', darkSky: 'border border-sky-400/18 bg-sky-500/8 text-sky-50', darkEmerald: 'border border-emerald-400/18 bg-emerald-500/8 text-emerald-100/85', darkAmber: 'border border-amber-300/18 bg-amber-500/8 text-amber-50', darkRose: 'border border-rose-300/18 bg-rose-500/8 text-rose-50', danger: 'border border-[var(--platform-button-danger-border)] bg-[var(--platform-button-danger-fill)]', }; const PLATFORM_SUBPANEL_INTERACTIVE_CLASS = 'text-left transition hover:bg-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--platform-warm-border)] disabled:cursor-not-allowed disabled:opacity-55'; function renderSubpanelTitle({ title, titleClassName, titleVariant, }: { title: ReactNode; titleClassName?: string; titleVariant: PlatformSubpanelTitleVariant; }) { if (titleVariant === 'strong') { return ( {title} ); } return ( {title} ); } /** * 平台白底子面板。 * 统一承接结果页和创作工作台里的 subpanel 外壳、标题行和右侧动作区。 */ export function PlatformSubpanel({ as: Component = 'section', title, titleVariant = 'section', actions, children, interactive = false, padding = 'md', radius = 'md', surface = 'platform', className, headerClassName, titleClassName, actionsClassName, bodyClassName, ...elementProps }: PlatformSubpanelProps) { const hasHeader = Boolean(title) || Boolean(actions); const subpanelClassName = [ PLATFORM_SUBPANEL_SURFACE_CLASS[surface], PLATFORM_SUBPANEL_RADIUS_CLASS[radius], PLATFORM_SUBPANEL_PADDING_CLASS[padding], interactive ? PLATFORM_SUBPANEL_INTERACTIVE_CLASS : null, className, ] .filter(Boolean) .join(' '); const content = ( <> {hasHeader ? ( {title ? renderSubpanelTitle({ title, titleClassName, titleVariant, }) : null} {actions ? ( {actions} ) : null} ) : null} {bodyClassName ? ( {children} ) : ( children )} > ); if (Component === 'button') { const { type = 'button', ...buttonProps } = elementProps as Omit< ButtonHTMLAttributes, 'children' | 'title' >; return ( {content} ); } const StaticComponent = Component; const staticProps = elementProps as Omit< HTMLAttributes, 'children' | 'className' | 'title' >; return ( {content} ); }