mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-15 02:42:45 +08:00
feat: 新增 EnhancedSelect 组件,为 A2UI Dropdown 添加 Combobox 模式
- 创建 enhanced-select 组件(搜索、分组、描述、键盘导航、Glassmorphism 样式) - 扩展 DropdownComponentSchema 支持 searchable/clearable/size/label/error 等字段 - A2UIDropdown 渲染器自动检测增强特性,按需切换标准 Select 与 Combobox 模式 - 所有新字段 optional,完全向后兼容
This commit is contained in:
@@ -62,16 +62,31 @@ export const ButtonComponentSchema = z.object({
|
||||
}),
|
||||
});
|
||||
|
||||
/** Dropdown/Select component */
|
||||
/** Dropdown option schema (enhanced with description, icon, disabled, group) */
|
||||
export const DropdownOptionSchema = z.object({
|
||||
label: TextContentSchema,
|
||||
value: z.string(),
|
||||
description: TextContentSchema.optional(),
|
||||
icon: z.string().optional(),
|
||||
disabled: BooleanContentSchema.optional(),
|
||||
group: z.string().optional(),
|
||||
});
|
||||
|
||||
/** Dropdown/Select component (enhanced with search, form integration, styling) */
|
||||
export const DropdownComponentSchema = z.object({
|
||||
Dropdown: z.object({
|
||||
options: z.array(z.object({
|
||||
label: TextContentSchema,
|
||||
value: z.string(),
|
||||
})),
|
||||
options: z.array(DropdownOptionSchema),
|
||||
selectedValue: TextContentSchema.optional(),
|
||||
onChange: ActionSchema,
|
||||
placeholder: z.string().optional(),
|
||||
// Enhanced features
|
||||
searchable: z.boolean().optional(),
|
||||
clearable: z.boolean().optional(),
|
||||
size: z.enum(['sm', 'default', 'lg']).optional(),
|
||||
// Form integration
|
||||
label: TextContentSchema.optional(),
|
||||
required: z.boolean().optional(),
|
||||
error: TextContentSchema.optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// ========================================
|
||||
// A2UI Dropdown Component Renderer
|
||||
// ========================================
|
||||
// Maps A2UI Dropdown component to shadcn/ui Select
|
||||
// Renders as EnhancedSelect (Combobox) when searchable,
|
||||
// or standard shadcn/ui Select otherwise.
|
||||
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import { useState, useCallback, useEffect, useMemo } from 'react';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -11,25 +12,31 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/Select';
|
||||
import { EnhancedSelect } from '@/components/ui/enhanced-select';
|
||||
import type { EnhancedSelectOption } from '@/components/ui/enhanced-select';
|
||||
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
||||
import { resolveTextContent } from '../A2UIRenderer';
|
||||
import { resolveTextContent, resolveLiteralOrBinding } from '../A2UIRenderer';
|
||||
import type { DropdownComponent } from '../../core/A2UITypes';
|
||||
|
||||
interface A2UIDropdownProps {
|
||||
component: DropdownComponent;
|
||||
state: Record<string, unknown>;
|
||||
onAction: (actionId: string, params: Record<string, unknown>) => void | Promise<void>;
|
||||
resolveBinding: (binding: { path: string }) => unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* A2UI Dropdown Component Renderer
|
||||
* Using shadcn/ui Select with options array mapping to SelectItem
|
||||
* Auto-selects rendering mode based on schema fields:
|
||||
* - searchable/description/icon/group → EnhancedSelect (Combobox)
|
||||
* - basic options only → shadcn/ui Select
|
||||
*/
|
||||
export const A2UIDropdown: ComponentRenderer = ({ component, state, onAction, resolveBinding }) => {
|
||||
const dropdownComp = component as DropdownComponent;
|
||||
const { Dropdown: dropdownConfig } = dropdownComp;
|
||||
|
||||
// Detect if enhanced features are requested
|
||||
const hasEnhancedFeatures = dropdownConfig.searchable ||
|
||||
dropdownConfig.clearable ||
|
||||
dropdownConfig.label ||
|
||||
dropdownConfig.error ||
|
||||
dropdownConfig.size ||
|
||||
dropdownConfig.options.some((opt: any) => opt.description || opt.icon || opt.group || opt.disabled);
|
||||
|
||||
// Resolve initial selected value from binding
|
||||
const getInitialValue = (): string => {
|
||||
if (!dropdownConfig.selectedValue) return '';
|
||||
@@ -56,13 +63,55 @@ export const A2UIDropdown: ComponentRenderer = ({ component, state, onAction, re
|
||||
});
|
||||
}, [dropdownConfig.onChange, onAction]);
|
||||
|
||||
// Resolve enhanced options (always computed to satisfy hooks rules)
|
||||
const resolvedOptions: EnhancedSelectOption[] = useMemo(() =>
|
||||
dropdownConfig.options.map((opt: any) => ({
|
||||
label: resolveTextContent(opt.label, resolveBinding),
|
||||
value: opt.value,
|
||||
description: opt.description ? resolveTextContent(opt.description, resolveBinding) : undefined,
|
||||
icon: opt.icon || undefined,
|
||||
disabled: opt.disabled
|
||||
? Boolean(resolveLiteralOrBinding(opt.disabled, resolveBinding))
|
||||
: false,
|
||||
group: opt.group || undefined,
|
||||
})),
|
||||
[dropdownConfig.options, resolveBinding]
|
||||
);
|
||||
|
||||
const resolvedLabel = dropdownConfig.label
|
||||
? resolveTextContent(dropdownConfig.label, resolveBinding)
|
||||
: undefined;
|
||||
|
||||
const resolvedError = dropdownConfig.error
|
||||
? resolveTextContent(dropdownConfig.error, resolveBinding)
|
||||
: undefined;
|
||||
|
||||
// ========== Enhanced Mode (Combobox) ==========
|
||||
if (hasEnhancedFeatures) {
|
||||
return (
|
||||
<EnhancedSelect
|
||||
options={resolvedOptions}
|
||||
value={selectedValue}
|
||||
onChange={handleChange}
|
||||
placeholder={dropdownConfig.placeholder}
|
||||
searchable={dropdownConfig.searchable || false}
|
||||
clearable={dropdownConfig.clearable || false}
|
||||
size={dropdownConfig.size}
|
||||
label={resolvedLabel}
|
||||
required={dropdownConfig.required || false}
|
||||
error={resolvedError}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// ========== Standard Mode (Radix Select) ==========
|
||||
return (
|
||||
<Select value={selectedValue} onValueChange={handleChange}>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder={dropdownConfig.placeholder} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{dropdownConfig.options.map((option) => {
|
||||
{dropdownConfig.options.map((option: any) => {
|
||||
const label = resolveTextContent(option.label, resolveBinding);
|
||||
return (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
|
||||
Reference in New Issue
Block a user