'use client' import React, { useState, useRef, useEffect, useLayoutEffect, useCallback } from 'react' import { createPortal } from 'react-dom' import { AppIcon } from '@/components/ui/icons' import type { ModelCapabilityOption, CapabilityFieldDefinition } from './config-modals/ModelCapabilityDropdown' import type { CapabilityValue } from '@/lib/model-config-contract' export interface ModelDropdownTestProps { models: ModelCapabilityOption[] value: string | undefined onModelChange: (modelKey: string) => void capabilityFields: CapabilityFieldDefinition[] capabilityOverrides: Record onCapabilityChange: (field: string, rawValue: string, sample: CapabilityValue) => void placeholder?: string } const VIEWPORT_EDGE_GAP = 8 const DEFAULT_MAX_HEIGHT = 400 function useDropdown(isOpen: boolean, setIsOpen: (val: boolean) => void, alignRight: boolean = false) { const triggerRef = useRef(null) const panelRef = useRef(null) const [panelStyle, setPanelStyle] = useState({}) const updatePosition = useCallback(() => { if (!triggerRef.current) return const rect = triggerRef.current.getBoundingClientRect() const viewportHeight = window.innerHeight || document.documentElement.clientHeight const spaceBelow = viewportHeight - rect.bottom - VIEWPORT_EDGE_GAP const spaceAbove = rect.top - VIEWPORT_EDGE_GAP let openUpward = false let currentMaxHeight = DEFAULT_MAX_HEIGHT if (spaceBelow < 250 && spaceAbove > spaceBelow) { openUpward = true currentMaxHeight = Math.min(DEFAULT_MAX_HEIGHT, spaceAbove) } else { currentMaxHeight = Math.min(DEFAULT_MAX_HEIGHT, spaceBelow) } const width = Math.max(rect.width, 240) let left = rect.left if (alignRight) { left = rect.right - width } setPanelStyle({ position: 'fixed', left, width, maxHeight: currentMaxHeight, ...(openUpward ? { bottom: viewportHeight - rect.top + 6 } : { top: rect.bottom + 6 }), zIndex: 9999 }) }, [alignRight]) useEffect(() => { function handleClickOutside(e: MouseEvent) { const target = e.target as Node if (triggerRef.current?.contains(target)) return if (panelRef.current?.contains(target)) return setIsOpen(false) } document.addEventListener('mousedown', handleClickOutside) return () => document.removeEventListener('mousedown', handleClickOutside) }, [setIsOpen]) useLayoutEffect(() => { if (!isOpen) return updatePosition() window.addEventListener('resize', updatePosition) window.addEventListener('scroll', updatePosition, true) return () => { window.removeEventListener('resize', updatePosition) window.removeEventListener('scroll', updatePosition, true) } }, [isOpen, updatePosition]) return { triggerRef, panelRef, panelStyle } } function resolveParamSummary(fields: CapabilityFieldDefinition[], overrides: Record) { return fields.map(def => { const val = overrides[def.field] !== undefined ? String(overrides[def.field]) : String(def.options[0] || '') if (def.field === 'duration') return `${val}s` return val }).filter(Boolean).join(' · ') } // ============================================================================ // V6: The Split Toolbar (Deconstructed Controls) // Breaks the monolithic dropdown into two separate context actions. No massive popover. // ============================================================================ export function ModelInnovativeV6(props: ModelDropdownTestProps) { const [modelOpen, setModelOpen] = useState(false) const [paramOpen, setParamOpen] = useState(false) const { triggerRef: modelTrigger, panelRef: modelPanel, panelStyle: modelStyle } = useDropdown(modelOpen, setModelOpen) const { triggerRef: paramTrigger, panelRef: paramPanel, panelStyle: paramStyle } = useDropdown(paramOpen, setParamOpen, true) const activeModel = props.models.find(m => m.value === props.value) const summary = resolveParamSummary(props.capabilityFields, props.capabilityOverrides) return (
{/* Left Button: Model Selection */} {/* Divider */}
{/* Right Button: Param Configuration */} {/* Portals */} {modelOpen && createPortal(
{props.models.map(m => ( ))}
, document.body )} {paramOpen && props.capabilityFields.length > 0 && createPortal(
{props.capabilityFields.map(field => { const val = props.capabilityOverrides[field.field] !== undefined ? String(props.capabilityOverrides[field.field]) : String(field.options[0] || '') return (
{field.label || field.field}
{field.options.map(opt => { const s = String(opt) const active = s === val return ( ) })}
) })}
, document.body )}
) } // ============================================================================ // V7: The Inline Canvas Expandable (No Overlays, Document Flow) // Pushes content down naturally. Perfect for form wizards. // ============================================================================ export function ModelInnovativeV7(props: ModelDropdownTestProps) { const [isExpanded, setIsExpanded] = useState(false) const activeModel = props.models.find(m => m.value === props.value) return (
1. 选择模型
{props.models.map(m => { const active = m.value === props.value return ( ) })}
{props.capabilityFields.length > 0 && ( <>
2. 参数微调
{props.capabilityFields.map(field => { const val = props.capabilityOverrides[field.field] !== undefined ? String(props.capabilityOverrides[field.field]) : String(field.options[0] || '') return (
{field.label || field.field}
{field.options.map(opt => { const s = String(opt) const active = s === val return ( ) })}
) })}
)}
) } // ============================================================================ // V8: The Pro Centered Modal (Context Shift) // Clicking opens a spacious, distraction-free modal dialog. Left-right layout. // ============================================================================ export function ModelInnovativeV8(props: ModelDropdownTestProps) { const [isOpen, setIsOpen] = useState(false) const activeModel = props.models.find(m => m.value === props.value) return ( <> {isOpen && createPortal(
setIsOpen(false)} />
{/* Left: Models List */}

模型库

包含 {props.models.length} 项
{props.models.map(m => { const active = m.value === props.value return ( ) })}
{/* Right: Params Configuration */}

参数设置

{props.capabilityFields.length === 0 ? (

当前模型无可用参数

) : ( props.capabilityFields.map(field => { const val = props.capabilityOverrides[field.field] !== undefined ? String(props.capabilityOverrides[field.field]) : String(field.options[0] || '') return (
{field.label || field.field}
{field.options.map(opt => { const s = String(opt) const active = s === val return ( ) })}
) }) )}
, document.body )} ) } // ============================================================================ // V9: The Drill-Down Popover (Nested Navigation) // Click Model -> Popover shows Model List -> Click "Params" -> View shifts sideways inside popover. // ============================================================================ export function ModelInnovativeV9(props: ModelDropdownTestProps) { const [isOpen, setIsOpen] = useState(false) const [view, setView] = useState<'models' | 'params'>('models') const { triggerRef, panelRef, panelStyle } = useDropdown(isOpen, setIsOpen) const activeModel = props.models.find(m => m.value === props.value) // When re-opening, reset view useEffect(() => { if (isOpen) setView('models') }, [isOpen]) return ( <> {isOpen && createPortal(
{/* Page 1: Models */}
选择主要模型
{props.models.map(m => { const active = m.value === props.value return (
{active && props.capabilityFields.length > 0 && ( )}
) })}
{/* Page 2: Params */}
参数配置
{props.capabilityFields.map(field => { const val = props.capabilityOverrides[field.field] !== undefined ? String(props.capabilityOverrides[field.field]) : String(field.options[0] || '') return (
{field.label || field.field}
{field.options.map(opt => { const s = String(opt) const active = s === val return ( ) })}
) })}
, document.body )} ) } // ============================================================================ // V10: Bottom Sheet Drawer (Mobile-inspired / Context Menu Bottom) // Triggers a drawer anchored to the bottom of the screen. Very tactile. // ============================================================================ export function ModelInnovativeV10(props: ModelDropdownTestProps) { const [isOpen, setIsOpen] = useState(false) const activeModel = props.models.find(m => m.value === props.value) return ( <> {isOpen && createPortal(
setIsOpen(false)} />

配置生成偏好

{/* Left: Models horizontally scrollable block */}

核心模型选择

{props.models.map(m => { const active = m.value === props.value return ( ) })}
{/* Right: Vertical params list */}

参数微调

{props.capabilityFields.length === 0 ? (
自动最佳配置应用中
) : (
{props.capabilityFields.map(field => { const val = props.capabilityOverrides[field.field] !== undefined ? String(props.capabilityOverrides[field.field]) : String(field.options[0] || '') return (
{field.label || field.field}
{field.options.map(opt => { const s = String(opt) const active = s === val return ( ) })}
) })}
)}
, document.body )} ) }