diff --git a/packages/ui/MIGRATION_STATUS.md b/packages/ui/MIGRATION_STATUS.md index 4206242b91..8c62e36bc7 100644 --- a/packages/ui/MIGRATION_STATUS.md +++ b/packages/ui/MIGRATION_STATUS.md @@ -1,152 +1,151 @@ -# UI 组件库迁移状态 +# UI Component Library Migration Status -## 使用示例 +## Usage Example ```typescript -// 从 @cherrystudio/ui 导入组件 -import { Spinner, DividerWithText, InfoTooltip, CustomTag } from '@cherrystudio/ui' +// Import components from @cherrystudio/ui +import { Spinner, DividerWithText, InfoTooltip } from '@cherrystudio/ui' -// 在组件中使用 +// Use in components function MyComponent() { return (
- - - 标签 + +
) } ``` -## 目录结构说明 +## Directory Structure ```text @packages/ui/ ├── src/ -│ ├── components/ # 组件主目录 -│ │ ├── base/ # 基础组件(按钮、输入框、标签等) -│ │ ├── display/ # 显示组件(卡片、列表、表格等) -│ │ ├── layout/ # 布局组件(容器、网格、间距等) -│ │ ├── icons/ # 图标组件 -│ │ ├── interactive/ # 交互组件(弹窗、提示、下拉等) -│ │ └── composite/ # 复合组件(多个基础组件组合而成) -│ ├── hooks/ # 自定义 React Hooks -│ └── types/ # TypeScript 类型定义 +│ ├── components/ # Main components directory +│ │ ├── base/ # Basic components (buttons, inputs, labels, etc.) +│ │ ├── display/ # Display components (cards, lists, tables, etc.) +│ │ ├── layout/ # Layout components (containers, grids, spacing, etc.) +│ │ ├── icons/ # Icon components +│ │ ├── interactive/ # Interactive components (modals, tooltips, dropdowns, etc.) +│ │ └── composite/ # Composite components (made from multiple base components) +│ ├── hooks/ # Custom React Hooks +│ └── types/ # TypeScript type definitions ``` -### 组件分类指南 +### Component Classification Guide -提交 PR 时,请根据组件功能将其放入正确的目录: +When submitting PRs, please place components in the correct directory based on their function: -- **base**: 最基础的 UI 元素,如按钮、输入框、开关、标签等 -- **display**: 用于展示内容的组件,如卡片、列表、表格、标签页等 -- **layout**: 用于页面布局的组件,如容器、网格系统、分隔符等 -- **icons**: 所有图标相关的组件 -- **interactive**: 需要用户交互的组件,如模态框、抽屉、提示框、下拉菜单等 -- **composite**: 复合组件,由多个基础组件组合而成 +- **base**: Most basic UI elements like buttons, inputs, switches, labels, etc. +- **display**: Components for displaying content like cards, lists, tables, tabs, etc. +- **layout**: Components for page layout like containers, grid systems, dividers, etc. +- **icons**: All icon-related components +- **interactive**: Components requiring user interaction like modals, drawers, tooltips, dropdowns, etc. +- **composite**: Composite components made from multiple base components -## 迁移概览 +## Migration Overview -- **总组件数**: 236 -- **已迁移**: 34 -- **已重构**: 18 -- **待迁移**: 184 +- **Total Components**: 236 +- **Migrated**: 34 +- **Refactored**: 18 +- **Pending Migration**: 184 -## 组件状态表 +## Component Status Table -| Category | Component Name | Migration Status | Refactoring Status | Description | -| --------------- | ------------------------- | ---------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **base** | | | | 基础组件 | -| | CopyButton | ✅ | ✅ | 复制按钮 | -| | CustomTag | ✅ | ✅ | 自定义标签 | -| | DividerWithText | ✅ | ✅ | 带文本的分隔线 | -| | EmojiIcon | ✅ | ✅ | 表情图标 | -| | ErrorBoundary | ✅ | ✅ | 错误边界 (通过 props 解耦) | -| | StatusTag | ✅ | ✅ | 统一状态标签(合并了 ErrorTag、SuccessTag、WarnTag、InfoTag) | -| | IndicatorLight | ✅ | ✅ | 指示灯 | -| | Spinner | ✅ | ✅ | 加载动画 | -| | TextBadge | ✅ | ✅ | 文本徽标 | -| | CustomCollapse | ✅ | ✅ | 自定义折叠面板 | -| **display** | | | | 显示组件 | -| | Ellipsis | ✅ | ✅ | 文本省略 | -| | ExpandableText | ✅ | ✅ | 可展开文本 | -| | ThinkingEffect | ✅ | ✅ | 思考效果动画 | -| | EmojiAvatar | ✅ | ✅ | 表情头像 | -| | ListItem | ✅ | ✅ | 列表项 | -| | MaxContextCount | ✅ | ✅ | 最大上下文数显示 | -| | ProviderAvatar | ✅ | ✅ | 提供者头像 | -| | CodeViewer | ❌ | ❌ | 代码查看器 (外部依赖) | -| | OGCard | ❌ | ❌ | OG 卡片 | -| | MarkdownShadowDOMRenderer | ❌ | ❌ | Markdown 渲染器 | -| | Preview/* | ❌ | ❌ | 预览组件 | -| **layout** | | | | 布局组件 | -| | HorizontalScrollContainer | ✅ | ❌ | 水平滚动容器 | -| | Scrollbar | ✅ | ❌ | 滚动条 | -| | Layout/* | ✅ | ✅ | 布局组件 | -| | Tab/* | ❌ | ❌ | 标签页 (Redux 依赖) | -| | TopView | ❌ | ❌ | 顶部视图 (window.api 依赖) | -| **icons** | | | | 图标组件 | -| | Icon | ✅ | ✅ | 图标工厂函数和预定义图标(合并了 CopyIcon、DeleteIcon、EditIcon、RefreshIcon、ResetIcon、ToolIcon、VisionIcon、WebSearchIcon、WrapIcon、UnWrapIcon、OcrIcon) | -| | FileIcons | ✅ | ❌ | 文件图标 (FileSvgIcon、FilePngIcon) | -| | ReasoningIcon | ✅ | ❌ | 推理图标 | -| | SvgSpinners180Ring | ✅ | ❌ | 旋转加载图标 | -| | ToolsCallingIcon | ✅ | ❌ | 工具调用图标 | -| **interactive** | | | | 交互组件 | -| | InfoTooltip | ✅ | ❌ | 信息提示 | -| | HelpTooltip | ✅ | ❌ | 帮助提示 | -| | WarnTooltip | ✅ | ❌ | 警告提示 | -| | EditableNumber | ✅ | ❌ | 可编辑数字 | -| | InfoPopover | ✅ | ❌ | 信息弹出框 | -| | CollapsibleSearchBar | ✅ | ❌ | 可折叠搜索栏 | -| | ImageToolButton | ✅ | ❌ | 图片工具按钮 | -| | DraggableList | ✅ | ❌ | 可拖拽列表 | -| | CodeEditor | ✅ | ❌ | 代码编辑器 | -| | EmojiPicker | ❌ | ❌ | 表情选择器 (useTheme 依赖) | -| | Selector | ✅ | ❌ | 选择器 (i18n 依赖) | -| | ModelSelector | ❌ | ❌ | 模型选择器 (Redux 依赖) | -| | LanguageSelect | ❌ | ❌ | 语言选择 | -| | TranslateButton | ❌ | ❌ | 翻译按钮 (window.api 依赖) | -| **composite** | | | | 复合组件 | -| | - | - | - | 暂无复合组件 | -| **未分类** | | | | 需要分类的组件 | -| | Popups/* (16+ 文件) | ❌ | ❌ | 弹窗组件 (业务耦合) | -| | RichEditor/* (30+ 文件) | ❌ | ❌ | 富文本编辑器 | -| | MarkdownEditor/* | ❌ | ❌ | Markdown 编辑器 | -| | MinApp/* | ❌ | ❌ | 迷你应用 (Redux 依赖) | -| | Avatar/* | ❌ | ❌ | 头像组件 | -| | ActionTools/* | ❌ | ❌ | 操作工具 | -| | CodeBlockView/* | ❌ | ❌ | 代码块视图 (window.api 依赖) | -| | ContextMenu | ❌ | ❌ | 右键菜单 (Electron API) | -| | WindowControls | ❌ | ❌ | 窗口控制 (Electron API) | -| | ErrorBoundary | ❌ | ❌ | 错误边界 (window.api 依赖) | +| Category | Component Name | Migration Status | Refactoring Status | Description | +| ----------------- | ------------------------- | ---------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **base** | | | | Base components | +| | CopyButton | ✅ | ✅ | Copy button | +| | CustomTag | ✅ | ✅ | Custom tag | +| | DividerWithText | ✅ | ✅ | Divider with text | +| | EmojiIcon | ✅ | ✅ | Emoji icon | +| | ErrorBoundary | ✅ | ✅ | Error boundary (decoupled via props) | +| | StatusTag | ✅ | ✅ | Unified status tag (merged ErrorTag, SuccessTag, WarnTag, InfoTag) | +| | IndicatorLight | ✅ | ✅ | Indicator light | +| | Spinner | ✅ | ✅ | Loading spinner | +| | TextBadge | ✅ | ✅ | Text badge | +| | CustomCollapse | ✅ | ✅ | Custom collapse panel | +| **display** | | | | Display components | +| | Ellipsis | ✅ | ✅ | Text ellipsis | +| | ExpandableText | ✅ | ✅ | Expandable text | +| | ThinkingEffect | ✅ | ✅ | Thinking effect animation | +| | EmojiAvatar | ✅ | ✅ | Emoji avatar | +| | ListItem | ✅ | ✅ | List item | +| | MaxContextCount | ✅ | ✅ | Max context count display | +| | ProviderAvatar | ✅ | ✅ | Provider avatar | +| | CodeViewer | ❌ | ❌ | Code viewer (external deps) | +| | OGCard | ❌ | ❌ | OG card | +| | MarkdownShadowDOMRenderer | ❌ | ❌ | Markdown renderer | +| | Preview/* | ❌ | ❌ | Preview components | +| **layout** | | | | Layout components | +| | HorizontalScrollContainer | ✅ | ❌ | Horizontal scroll container | +| | Scrollbar | ✅ | ❌ | Scrollbar | +| | Layout/* | ✅ | ✅ | Layout components | +| | Tab/* | ❌ | ❌ | Tab (Redux dependency) | +| | TopView | ❌ | ❌ | Top view (window.api dependency) | +| **icons** | | | | Icon components | +| | Icon | ✅ | ✅ | Icon factory function and predefined icons (merged CopyIcon, DeleteIcon, EditIcon, RefreshIcon, ResetIcon, ToolIcon, VisionIcon, WebSearchIcon, WrapIcon, UnWrapIcon, OcrIcon) | +| | FileIcons | ✅ | ❌ | File icons (FileSvgIcon, FilePngIcon) | +| | ReasoningIcon | ✅ | ❌ | Reasoning icon | +| | SvgSpinners180Ring | ✅ | ❌ | Spinner loading icon | +| | ToolsCallingIcon | ✅ | ❌ | Tools calling icon | +| **interactive** | | | | Interactive components | +| | InfoTooltip | ✅ | ❌ | Info tooltip | +| | HelpTooltip | ✅ | ❌ | Help tooltip | +| | WarnTooltip | ✅ | ❌ | Warning tooltip | +| | EditableNumber | ✅ | ❌ | Editable number | +| | InfoPopover | ✅ | ❌ | Info popover | +| | CollapsibleSearchBar | ✅ | ❌ | Collapsible search bar | +| | ImageToolButton | ✅ | ❌ | Image tool button | +| | DraggableList | ✅ | ❌ | Draggable list | +| | CodeEditor | ✅ | ❌ | Code editor | +| | EmojiPicker | ❌ | ❌ | Emoji picker (useTheme dependency) | +| | Selector | ✅ | ❌ | Selector (i18n dependency) | +| | ModelSelector | ❌ | ❌ | Model selector (Redux dependency) | +| | LanguageSelect | ❌ | ❌ | Language select | +| | TranslateButton | ❌ | ❌ | Translate button (window.api dependency) | +| **composite** | | | | Composite components | +| | - | - | - | No composite components yet | +| **Uncategorized** | | | | Components needing categorization | +| | Popups/* (16+ files) | ❌ | ❌ | Popup components (business coupled) | +| | RichEditor/* (30+ files) | ❌ | ❌ | Rich text editor | +| | MarkdownEditor/* | ❌ | ❌ | Markdown editor | +| | MinApp/* | ❌ | ❌ | Mini app (Redux dependency) | +| | Avatar/* | ❌ | ❌ | Avatar components | +| | ActionTools/* | ❌ | ❌ | Action tools | +| | CodeBlockView/* | ❌ | ❌ | Code block view (window.api dependency) | +| | ContextMenu | ❌ | ❌ | Context menu (Electron API) | +| | WindowControls | ❌ | ❌ | Window controls (Electron API) | +| | ErrorBoundary | ❌ | ❌ | Error boundary (window.api dependency) | -## 迁移步骤 +## Migration Steps -### 第一阶段:复制迁移(当前阶段) +### Phase 1: Copy Migration (Current Phase) -- 将组件原样复制到 @packages/ui -- 保留原有依赖(antd、styled-components 等) -- 在文件顶部添加原路径注释 +- Copy components as-is to @packages/ui +- Retain original dependencies (antd, styled-components, etc.) +- Add original path comment at file top -### 第二阶段:重构优化 +### Phase 2: Refactor and Optimize -- 移除 antd 依赖,替换为 HeroUI -- 移除 styled-components,替换为 Tailwind CSS -- 优化组件 API 和类型定义 +- Remove antd dependencies, replace with HeroUI +- Remove styled-components, replace with Tailwind CSS +- Optimize component APIs and type definitions -## 注意事项 +## Notes -1. **不迁移**包含以下依赖的组件(解耦后可迁移): - - window.api 调用 - - Redux(useSelector、useDispatch 等) - - 其他外部数据源 +1. **Do NOT migrate** components with these dependencies (can be migrated after decoupling): + - window.api calls + - Redux (useSelector, useDispatch, etc.) + - Other external data sources -2. **可迁移**但需要后续解耦的组件: - - 使用 i18n 的组件(将 i18n 改为 props 传入) - - 使用 antd 的组件(后续替换为 HeroUI) +2. **Can migrate** but need decoupling later: + - Components using i18n (change i18n to props) + - Components using antd (replace with HeroUI later) -3. **提交规范**: - - 每次 PR 专注于一个类别的组件 - - 确保所有迁移的组件都有导出 - - 更新此文档的迁移状态 +3. **Submission Guidelines**: + - Each PR should focus on one category of components + - Ensure all migrated components are exported + - Update migration status in this document diff --git a/packages/ui/MIGRATION_STATUS_EN.md b/packages/ui/MIGRATION_STATUS_EN.md deleted file mode 100644 index 8c62e36bc7..0000000000 --- a/packages/ui/MIGRATION_STATUS_EN.md +++ /dev/null @@ -1,151 +0,0 @@ -# UI Component Library Migration Status - -## Usage Example - -```typescript -// Import components from @cherrystudio/ui -import { Spinner, DividerWithText, InfoTooltip } from '@cherrystudio/ui' - -// Use in components -function MyComponent() { - return ( -
- - - -
- ) -} -``` - -## Directory Structure - -```text -@packages/ui/ -├── src/ -│ ├── components/ # Main components directory -│ │ ├── base/ # Basic components (buttons, inputs, labels, etc.) -│ │ ├── display/ # Display components (cards, lists, tables, etc.) -│ │ ├── layout/ # Layout components (containers, grids, spacing, etc.) -│ │ ├── icons/ # Icon components -│ │ ├── interactive/ # Interactive components (modals, tooltips, dropdowns, etc.) -│ │ └── composite/ # Composite components (made from multiple base components) -│ ├── hooks/ # Custom React Hooks -│ └── types/ # TypeScript type definitions -``` - -### Component Classification Guide - -When submitting PRs, please place components in the correct directory based on their function: - -- **base**: Most basic UI elements like buttons, inputs, switches, labels, etc. -- **display**: Components for displaying content like cards, lists, tables, tabs, etc. -- **layout**: Components for page layout like containers, grid systems, dividers, etc. -- **icons**: All icon-related components -- **interactive**: Components requiring user interaction like modals, drawers, tooltips, dropdowns, etc. -- **composite**: Composite components made from multiple base components - -## Migration Overview - -- **Total Components**: 236 -- **Migrated**: 34 -- **Refactored**: 18 -- **Pending Migration**: 184 - -## Component Status Table - -| Category | Component Name | Migration Status | Refactoring Status | Description | -| ----------------- | ------------------------- | ---------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| **base** | | | | Base components | -| | CopyButton | ✅ | ✅ | Copy button | -| | CustomTag | ✅ | ✅ | Custom tag | -| | DividerWithText | ✅ | ✅ | Divider with text | -| | EmojiIcon | ✅ | ✅ | Emoji icon | -| | ErrorBoundary | ✅ | ✅ | Error boundary (decoupled via props) | -| | StatusTag | ✅ | ✅ | Unified status tag (merged ErrorTag, SuccessTag, WarnTag, InfoTag) | -| | IndicatorLight | ✅ | ✅ | Indicator light | -| | Spinner | ✅ | ✅ | Loading spinner | -| | TextBadge | ✅ | ✅ | Text badge | -| | CustomCollapse | ✅ | ✅ | Custom collapse panel | -| **display** | | | | Display components | -| | Ellipsis | ✅ | ✅ | Text ellipsis | -| | ExpandableText | ✅ | ✅ | Expandable text | -| | ThinkingEffect | ✅ | ✅ | Thinking effect animation | -| | EmojiAvatar | ✅ | ✅ | Emoji avatar | -| | ListItem | ✅ | ✅ | List item | -| | MaxContextCount | ✅ | ✅ | Max context count display | -| | ProviderAvatar | ✅ | ✅ | Provider avatar | -| | CodeViewer | ❌ | ❌ | Code viewer (external deps) | -| | OGCard | ❌ | ❌ | OG card | -| | MarkdownShadowDOMRenderer | ❌ | ❌ | Markdown renderer | -| | Preview/* | ❌ | ❌ | Preview components | -| **layout** | | | | Layout components | -| | HorizontalScrollContainer | ✅ | ❌ | Horizontal scroll container | -| | Scrollbar | ✅ | ❌ | Scrollbar | -| | Layout/* | ✅ | ✅ | Layout components | -| | Tab/* | ❌ | ❌ | Tab (Redux dependency) | -| | TopView | ❌ | ❌ | Top view (window.api dependency) | -| **icons** | | | | Icon components | -| | Icon | ✅ | ✅ | Icon factory function and predefined icons (merged CopyIcon, DeleteIcon, EditIcon, RefreshIcon, ResetIcon, ToolIcon, VisionIcon, WebSearchIcon, WrapIcon, UnWrapIcon, OcrIcon) | -| | FileIcons | ✅ | ❌ | File icons (FileSvgIcon, FilePngIcon) | -| | ReasoningIcon | ✅ | ❌ | Reasoning icon | -| | SvgSpinners180Ring | ✅ | ❌ | Spinner loading icon | -| | ToolsCallingIcon | ✅ | ❌ | Tools calling icon | -| **interactive** | | | | Interactive components | -| | InfoTooltip | ✅ | ❌ | Info tooltip | -| | HelpTooltip | ✅ | ❌ | Help tooltip | -| | WarnTooltip | ✅ | ❌ | Warning tooltip | -| | EditableNumber | ✅ | ❌ | Editable number | -| | InfoPopover | ✅ | ❌ | Info popover | -| | CollapsibleSearchBar | ✅ | ❌ | Collapsible search bar | -| | ImageToolButton | ✅ | ❌ | Image tool button | -| | DraggableList | ✅ | ❌ | Draggable list | -| | CodeEditor | ✅ | ❌ | Code editor | -| | EmojiPicker | ❌ | ❌ | Emoji picker (useTheme dependency) | -| | Selector | ✅ | ❌ | Selector (i18n dependency) | -| | ModelSelector | ❌ | ❌ | Model selector (Redux dependency) | -| | LanguageSelect | ❌ | ❌ | Language select | -| | TranslateButton | ❌ | ❌ | Translate button (window.api dependency) | -| **composite** | | | | Composite components | -| | - | - | - | No composite components yet | -| **Uncategorized** | | | | Components needing categorization | -| | Popups/* (16+ files) | ❌ | ❌ | Popup components (business coupled) | -| | RichEditor/* (30+ files) | ❌ | ❌ | Rich text editor | -| | MarkdownEditor/* | ❌ | ❌ | Markdown editor | -| | MinApp/* | ❌ | ❌ | Mini app (Redux dependency) | -| | Avatar/* | ❌ | ❌ | Avatar components | -| | ActionTools/* | ❌ | ❌ | Action tools | -| | CodeBlockView/* | ❌ | ❌ | Code block view (window.api dependency) | -| | ContextMenu | ❌ | ❌ | Context menu (Electron API) | -| | WindowControls | ❌ | ❌ | Window controls (Electron API) | -| | ErrorBoundary | ❌ | ❌ | Error boundary (window.api dependency) | - -## Migration Steps - -### Phase 1: Copy Migration (Current Phase) - -- Copy components as-is to @packages/ui -- Retain original dependencies (antd, styled-components, etc.) -- Add original path comment at file top - -### Phase 2: Refactor and Optimize - -- Remove antd dependencies, replace with HeroUI -- Remove styled-components, replace with Tailwind CSS -- Optimize component APIs and type definitions - -## Notes - -1. **Do NOT migrate** components with these dependencies (can be migrated after decoupling): - - window.api calls - - Redux (useSelector, useDispatch, etc.) - - Other external data sources - -2. **Can migrate** but need decoupling later: - - Components using i18n (change i18n to props) - - Components using antd (replace with HeroUI later) - -3. **Submission Guidelines**: - - Each PR should focus on one category of components - - Ensure all migrated components are exported - - Update migration status in this document diff --git a/packages/ui/components.json b/packages/ui/components.json index 7f39259959..693dcaf364 100644 --- a/packages/ui/components.json +++ b/packages/ui/components.json @@ -4,7 +4,7 @@ "components": "@cherrystudio/ui/components", "hooks": "@cherrystudio/ui/hooks", "lib": "@cherrystudio/ui/lib", - "ui": "@cherrystudio/ui/components/ui", + "ui": "@cherrystudio/ui/components/primitives", "utils": "@cherrystudio/ui/utils" }, "iconLibrary": "lucide", diff --git a/packages/ui/src/components/base/Button/index.tsx b/packages/ui/src/components/base/Button/index.tsx deleted file mode 100644 index 88815307aa..0000000000 --- a/packages/ui/src/components/base/Button/index.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { ButtonProps as HeroUIButtonProps } from '@heroui/react' -import { Button as HeroUIButton } from '@heroui/react' - -export interface ButtonProps extends HeroUIButtonProps {} - -const Button = ({ ...props }: ButtonProps) => { - return -} - -Button.displayName = 'Button' - -export default Button diff --git a/packages/ui/src/components/base/CustomCollapse/index.tsx b/packages/ui/src/components/base/CustomCollapse/index.tsx deleted file mode 100644 index d5f2261a57..0000000000 --- a/packages/ui/src/components/base/CustomCollapse/index.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { Accordion, AccordionItem, type AccordionItemProps, type AccordionProps } from '@heroui/react' -import type { FC } from 'react' -import { memo } from 'react' - -// 重新导出 HeroUI 的组件,方便直接使用 -export { Accordion, AccordionItem } from '@heroui/react' - -interface CustomCollapseProps { - children: React.ReactNode - accordionProps?: Omit - accordionItemProps?: Omit -} - -const CustomCollapse: FC = ({ children, accordionProps = {}, accordionItemProps = {} }) => { - // 解构 Accordion 的 props - const { - defaultExpandedKeys = ['1'], - variant = 'bordered', - className = '', - isDisabled = false, - ...restAccordionProps - } = accordionProps - - // 解构 AccordionItem 的 props - const { title = 'Collapse Panel', ...restAccordionItemProps } = accordionItemProps - - return ( - - - {children} - - - ) -} - -export default memo(CustomCollapse) diff --git a/packages/ui/src/components/base/Selector/README.md b/packages/ui/src/components/base/Selector/README.md deleted file mode 100644 index 003a6f683e..0000000000 --- a/packages/ui/src/components/base/Selector/README.md +++ /dev/null @@ -1,333 +0,0 @@ -# Selector 组件 - -基于 HeroUI Select 封装的下拉选择组件,简化了 Set 和 Selection 的转换逻辑。 - -## 核心特性 - -- ✅ **类型安全**: 单选和多选自动推断回调类型 -- ✅ **智能转换**: 自动处理 `Set` 和原始值的转换 -- ✅ **HeroUI 风格**: 保持与 HeroUI 生态一致的 API -- ✅ **支持数字和字符串**: 泛型支持,自动识别值类型 - -## 基础用法 - -### 单选模式(默认) - -```tsx -import { Selector } from '@cherrystudio/ui' -import { useState } from 'react' - -function Example() { - const [language, setLanguage] = useState('zh-CN') - - const languageOptions = [ - { label: '中文', value: 'zh-CN' }, - { label: 'English', value: 'en-US' }, - { label: '日本語', value: 'ja-JP' } - ] - - return ( - { - // value 类型自动推断为 string - setLanguage(value) - }} - items={languageOptions} - placeholder="选择语言" - /> - ) -} -``` - -### 多选模式 - -```tsx -import { Selector } from '@cherrystudio/ui' -import { useState } from 'react' - -function Example() { - const [languages, setLanguages] = useState(['zh-CN', 'en-US']) - - const languageOptions = [ - { label: '中文', value: 'zh-CN' }, - { label: 'English', value: 'en-US' }, - { label: '日本語', value: 'ja-JP' }, - { label: 'Français', value: 'fr-FR' } - ] - - return ( - { - // values 类型自动推断为 string[] - setLanguages(values) - }} - items={languageOptions} - placeholder="选择语言" - /> - ) -} -``` - -### 数字类型值 - -```tsx -import { Selector } from '@cherrystudio/ui' - -function Example() { - const [priority, setPriority] = useState(1) - - const priorityOptions = [ - { label: '低', value: 1 }, - { label: '中', value: 2 }, - { label: '高', value: 3 } - ] - - return ( - - selectedKeys={priority} - onSelectionChange={(value) => { - // value 类型为 number - setPriority(value) - }} - items={priorityOptions} - /> - ) -} -``` - -### 禁用选项 - -```tsx -const options = [ - { label: '选项 1', value: '1' }, - { label: '选项 2 (禁用)', value: '2', disabled: true }, - { label: '选项 3', value: '3' } -] - - -``` - -### 自定义 Label - -```tsx -import { Flex } from '@cherrystudio/ui' - -const options = [ - { - label: ( - - 🇨🇳 - 中文 - - ), - value: 'zh-CN' - }, - { - label: ( - - 🇺🇸 - English - - ), - value: 'en-US' - } -] - - -``` - -## API - -### SelectorProps - -| 属性 | 类型 | 默认值 | 说明 | -|------|------|--------|------| -| `items` | `SelectorItem[]` | - | 必填,选项列表 | -| `selectedKeys` | `V` \| `V[]` | - | 受控的选中值(单选为单个值,多选为数组) | -| `onSelectionChange` | `(key: V) => void` \| `(keys: V[]) => void` | - | 选择变化回调(类型根据 selectionMode 自动推断) | -| `selectionMode` | `'single'` \| `'multiple'` | `'single'` | 选择模式 | -| `placeholder` | `string` | - | 占位文本 | -| `disabled` | `boolean` | `false` | 是否禁用 | -| `isRequired` | `boolean` | `false` | 是否必填 | -| `label` | `ReactNode` | - | 标签文本 | -| `description` | `ReactNode` | - | 描述文本 | -| `errorMessage` | `ReactNode` | - | 错误提示 | -| ...rest | `SelectProps` | - | 其他 HeroUI Select 属性 | - -### SelectorItem - -```tsx -interface SelectorItem { - label: string | ReactNode // 显示文本或自定义内容 - value: V // 选项值 - disabled?: boolean // 是否禁用 - [key: string]: any // 其他自定义属性 -} -``` - -## 类型安全 - -组件使用 TypeScript 条件类型,根据 `selectionMode` 自动推断回调类型: - -```tsx -// 单选模式 - ...} // v 类型: V -/> - -// 多选模式 - ...} // vs 类型: V[] -/> -``` - -## 与 HeroUI Select 的区别 - -| 特性 | HeroUI Select | Selector (本组件) | -|------|---------------|------------------| -| `selectedKeys` | `Set \| 'all'` | `V` \| `V[]` (自动转换) | -| `onSelectionChange` | `(keys: Selection) => void` | `(key: V) => void` \| `(keys: V[]) => void` | -| 单选回调 | 返回 `Set` (需手动提取) | 直接返回单个值 | -| 多选回调 | 返回 `Set` (需转数组) | 直接返回数组 | -| 类型推断 | 无 | 根据 selectionMode 自动推断 | - -## 最佳实践 - -### 1. 显式声明 selectionMode - -虽然单选是默认模式,但建议显式声明以提高代码可读性: - -```tsx -// ✅ 推荐 - - -// ⚠️ 可以但不够清晰 - -``` - -### 2. 使用泛型指定值类型 - -当值类型为数字或联合类型时,使用泛型获得更好的类型提示: - -```tsx -// ✅ 推荐 - selectedKeys={priority} ... /> - -// ✅ 推荐(联合类型) -type Status = 'pending' | 'approved' | 'rejected' - selectedKeys={status} ... /> -``` - -### 3. 避免在渲染时创建 items - -```tsx -// ❌ 不推荐(每次渲染都创建新数组) - - -// ✅ 推荐(在组件外或使用 useMemo) -const items = [{ label: 'A', value: '1' }] - -``` - -## 迁移指南 - -### 从 antd Select 迁移 - -```tsx -// antd Select -import { Select } from 'antd' - - - -
- - -
- - -
- -