From 1b04fd065d4a51f398062c9b9d8c881ec1ff5ba6 Mon Sep 17 00:00:00 2001 From: MyPrototypeWhat Date: Wed, 17 Sep 2025 15:45:22 +0800 Subject: [PATCH] refactor(ui): enhance CustomCollapse and ToolsCallingIcon components - Refactored CustomCollapse to utilize Accordion and AccordionItem from HeroUI, simplifying props and improving functionality. - Updated ToolsCallingIcon to accept TooltipProps for better customization. - Revised stories for CustomCollapse to reflect new prop structure and added examples for various use cases. - Cleaned up unnecessary props and improved documentation in story files. --- .../components/base/CustomCollapse/index.tsx | 90 ++----- .../icons/ToolsCallingIcon/index.tsx | 10 +- .../base/CustomCollapse.stories.tsx | 240 +++++++++--------- .../icons/SvgSpinners180Ring.stories.tsx | 21 +- .../icons/ToolsCallingIcon.stories.tsx | 45 +--- .../ModelList/ModelListGroup.tsx | 63 +++-- 6 files changed, 204 insertions(+), 265 deletions(-) diff --git a/packages/ui/src/components/base/CustomCollapse/index.tsx b/packages/ui/src/components/base/CustomCollapse/index.tsx index 89699a2cdf..d5f2261a57 100644 --- a/packages/ui/src/components/base/CustomCollapse/index.tsx +++ b/packages/ui/src/components/base/CustomCollapse/index.tsx @@ -1,88 +1,42 @@ -import { Accordion, AccordionItem } from '@heroui/react' +import { Accordion, AccordionItem, type AccordionItemProps, type AccordionProps } from '@heroui/react' import type { FC } from 'react' -import { memo, useEffect, useState } from 'react' +import { memo } from 'react' // 重新导出 HeroUI 的组件,方便直接使用 export { Accordion, AccordionItem } from '@heroui/react' interface CustomCollapseProps { - label: React.ReactNode - extra?: React.ReactNode children: React.ReactNode - destroyInactivePanel?: boolean - defaultActiveKey?: string[] - activeKey?: string[] - collapsible?: 'header' | 'icon' | 'disabled' - onChange?: (activeKeys: string | string[]) => void - style?: React.CSSProperties - classNames?: { - trigger?: string - content?: string - } - className?: string - variant?: 'light' | 'shadow' | 'bordered' | 'splitted' + accordionProps?: Omit + accordionItemProps?: Omit } -const CustomCollapse: FC = ({ - label, - extra, - children, - defaultActiveKey = ['1'], - activeKey, - collapsible, - onChange, - style, - classNames, - className = '', - variant = 'bordered' -}) => { - const [expandedKeys, setExpandedKeys] = useState>(() => { - if (activeKey !== undefined) { - return new Set(activeKey) - } - return new Set(defaultActiveKey) - }) +const CustomCollapse: FC = ({ children, accordionProps = {}, accordionItemProps = {} }) => { + // 解构 Accordion 的 props + const { + defaultExpandedKeys = ['1'], + variant = 'bordered', + className = '', + isDisabled = false, + ...restAccordionProps + } = accordionProps - useEffect(() => { - if (activeKey !== undefined) { - setExpandedKeys(new Set(activeKey)) - } - }, [activeKey]) - - const handleSelectionChange = (keys: 'all' | Set) => { - if (keys === 'all') return - - const stringKeys = Array.from(keys).map((key) => String(key)) - const newExpandedKeys = new Set(stringKeys) - - if (activeKey === undefined) { - setExpandedKeys(newExpandedKeys) - } - - onChange?.(stringKeys.length === 1 ? stringKeys[0] : stringKeys) - } - - const isDisabled = collapsible === 'disabled' + // 解构 AccordionItem 的 props + const { title = 'Collapse Panel', ...restAccordionItemProps } = accordionItemProps return ( + selectionMode="multiple" + {...restAccordionProps}> + aria-label={typeof title === 'string' ? title : 'collapse-item'} + title={title} + {...restAccordionItemProps}> {children} diff --git a/packages/ui/src/components/icons/ToolsCallingIcon/index.tsx b/packages/ui/src/components/icons/ToolsCallingIcon/index.tsx index 9282548ad5..b8faa2493a 100644 --- a/packages/ui/src/components/icons/ToolsCallingIcon/index.tsx +++ b/packages/ui/src/components/icons/ToolsCallingIcon/index.tsx @@ -1,22 +1,20 @@ // Original: src/renderer/src/components/Icons/ToolsCallingIcon.tsx -import { Tooltip } from '@heroui/react' +import { Tooltip, type TooltipProps } from '@heroui/react' import { Wrench } from 'lucide-react' import React from 'react' -import { useTranslation } from 'react-i18next' import { cn } from '../../../utils' interface ToolsCallingIconProps extends React.HTMLAttributes { className?: string iconClassName?: string + TooltipProps?: TooltipProps } -const ToolsCallingIcon = ({ className, iconClassName, ...props }: ToolsCallingIconProps) => { - const { t } = useTranslation() - +const ToolsCallingIcon = ({ className, iconClassName, TooltipProps, ...props }: ToolsCallingIconProps) => { return (
- +
diff --git a/packages/ui/stories/components/base/CustomCollapse.stories.tsx b/packages/ui/stories/components/base/CustomCollapse.stories.tsx index ec96211b30..c05dbbfc54 100644 --- a/packages/ui/stories/components/base/CustomCollapse.stories.tsx +++ b/packages/ui/stories/components/base/CustomCollapse.stories.tsx @@ -13,55 +13,17 @@ const meta: Meta = { }, tags: ['autodocs'], argTypes: { - label: { - control: 'text', - description: '面板标题' - }, - extra: { - control: false, - description: '额外内容(副标题)' - }, children: { control: false, description: '面板内容' }, - defaultActiveKey: { + accordionProps: { control: false, - description: '默认激活的面板键值' + description: 'Accordion 组件的属性' }, - activeKey: { + accordionItemProps: { control: false, - description: '当前激活的面板键值(受控模式)' - }, - onChange: { - control: false, - description: '面板状态变化回调' - }, - collapsible: { - control: 'select', - options: ['header', 'icon', 'disabled'], - description: '折叠触发方式' - }, - className: { - control: 'text', - description: '额外的 CSS 类名' - }, - variant: { - control: 'select', - options: ['light', 'shadow', 'bordered', 'splitted'], - description: 'HeroUI 样式变体' - }, - destroyInactivePanel: { - control: 'boolean', - description: '是否销毁非激活面板' - }, - style: { - control: false, - description: '自定义样式' - }, - styles: { - control: false, - description: '自定义头部和内容样式' + description: 'AccordionItem 组件的属性' } } } @@ -72,7 +34,9 @@ type Story = StoryObj // 基础用法 export const Default: Story = { args: { - label: '默认折叠面板', + accordionItemProps: { + title: '默认折叠面板' + }, children: (

这是折叠面板的内容。

@@ -85,13 +49,17 @@ export const Default: Story = { // 带副标题 export const WithSubtitle: Story = { args: { - label: '带副标题的折叠面板', - extra: 这是副标题内容, - defaultActiveKey: ['1'], + accordionProps: { + defaultExpandedKeys: ['1'] + }, + accordionItemProps: { + title: '带副标题的折叠面板', + subtitle: 这是副标题内容 + }, children: (

面板内容

-

可以在 extra 属性中设置副标题

+

可以在 subtitle 属性中设置副标题

) } @@ -100,8 +68,12 @@ export const WithSubtitle: Story = { // HeroUI 样式变体 export const VariantLight: Story = { args: { - label: 'Light 变体', - variant: 'light', + accordionProps: { + variant: 'light' + }, + accordionItemProps: { + title: 'Light 变体' + }, children: (

这是 HeroUI 的 Light 变体样式。

@@ -112,10 +84,14 @@ export const VariantLight: Story = { export const VariantShadow: Story = { args: { - label: 'Shadow 变体', - extra: '带阴影的面板样式', - variant: 'shadow', - className: 'p-2', + accordionProps: { + variant: 'shadow', + className: 'p-2' + }, + accordionItemProps: { + title: 'Shadow 变体', + subtitle: '带阴影的面板样式' + }, children: (

这是 HeroUI 的 Shadow 变体样式。

@@ -126,8 +102,12 @@ export const VariantShadow: Story = { export const VariantBordered: Story = { args: { - label: 'Bordered 变体(默认)', - variant: 'bordered', + accordionProps: { + variant: 'bordered' + }, + accordionItemProps: { + title: 'Bordered 变体(默认)' + }, children: (

这是 HeroUI 的 Bordered 变体样式。

@@ -138,8 +118,12 @@ export const VariantBordered: Story = { export const VariantSplitted: Story = { args: { - label: 'Splitted 变体', - variant: 'splitted', + accordionProps: { + variant: 'splitted' + }, + accordionItemProps: { + title: 'Splitted 变体' + }, children: (

这是 HeroUI 的 Splitted 变体样式。

@@ -151,12 +135,14 @@ export const VariantSplitted: Story = { // 富内容标题 export const RichLabel: Story = { args: { - label: ( -
- - 设置面板 -
- ), + accordionItemProps: { + title: ( +
+ + 设置面板 +
+ ) + }, children: (
@@ -181,17 +167,19 @@ export const RichLabel: Story = { // 带警告提示 export const WithWarning: Story = { args: { - label: ( -
- - 连接的设备 -
- ), - extra: ( -

- 2个问题需要立即修复 -

- ), + accordionItemProps: { + title: ( +
+ + 连接的设备 +
+ ), + subtitle: ( +

+ 2个问题需要立即修复 +

+ ) + }, children: (

检测到以下设备连接异常:

@@ -207,9 +195,13 @@ export const WithWarning: Story = { // 禁用状态 export const Disabled: Story = { args: { - label: '禁用的折叠面板', - collapsible: 'disabled', - defaultActiveKey: ['1'], + accordionProps: { + isDisabled: true, + defaultExpandedKeys: ['1'] + }, + accordionItemProps: { + title: '禁用的折叠面板' + }, children: (

这个面板被禁用了,无法操作。

@@ -221,28 +213,36 @@ export const Disabled: Story = { // 受控模式 export const ControlledMode: Story = { render: function ControlledMode() { - const [activeKey, setActiveKey] = useState(['1']) + const [selectedKeys, setSelectedKeys] = useState>(new Set(['1'])) return (
- -
setActiveKey(Array.isArray(keys) ? keys : [keys])}> + accordionProps={{ + selectedKeys, + onSelectionChange: (keys) => { + if (keys !== 'all') { + setSelectedKeys(keys as Set) + } + } + }} + accordionItemProps={{ + title: '受控的折叠面板' + }}>

这是一个受控的折叠面板

通过按钮控制展开和收起状态

-
当前状态:{activeKey.length > 0 ? '展开' : '收起'}
+
当前状态:{selectedKeys.size > 0 ? '展开' : '收起'}
) } @@ -252,17 +252,21 @@ export const ControlledMode: Story = { export const MultipleSinglePanels: Story = { render: () => (
- +

第一个面板的内容

- +

第二个面板的内容

- +

这个面板被禁用了

@@ -330,22 +334,24 @@ export const NativeAccordionMultiple: Story = { // 富内容面板 export const RichContent: Story = { args: { - label: ( -
-
- - 详细信息 + accordionItemProps: { + title: ( +
+
+ + 详细信息 +
+
e.stopPropagation()}> + + +
-
e.stopPropagation()}> - - -
-
- ), + ) + }, children: (
@@ -371,11 +377,7 @@ export const RichContent: Story = {
-