mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-10 15:49:29 +08:00
feat: update migration status and add ListItem and EditableNumber components
- Updated migration status documentation to reflect the migration of 38 components, with 198 pending. - Enhanced the component status table with new entries for ListItem and EditableNumber. - Added ListItem and EditableNumber components with their respective implementations and styles. - Updated index.ts to export the newly added components for improved accessibility.
This commit is contained in:
parent
6eb9ab30b0
commit
046ed3edef
@ -49,9 +49,9 @@ function MyComponent() {
|
|||||||
## 迁移概览
|
## 迁移概览
|
||||||
|
|
||||||
- **总组件数**: 236
|
- **总组件数**: 236
|
||||||
- **已迁移**: 36
|
- **已迁移**: 38
|
||||||
- **已重构**: 0
|
- **已重构**: 0
|
||||||
- **待迁移**: 200
|
- **待迁移**: 198
|
||||||
|
|
||||||
## 组件状态表
|
## 组件状态表
|
||||||
|
|
||||||
@ -74,6 +74,7 @@ function MyComponent() {
|
|||||||
| | ExpandableText | ✅ | ❌ | 可展开文本 |
|
| | ExpandableText | ✅ | ❌ | 可展开文本 |
|
||||||
| | ThinkingEffect | ✅ | ❌ | 思考效果动画 |
|
| | ThinkingEffect | ✅ | ❌ | 思考效果动画 |
|
||||||
| | EmojiAvatar | ✅ | ❌ | 表情头像 |
|
| | EmojiAvatar | ✅ | ❌ | 表情头像 |
|
||||||
|
| | ListItem | ✅ | ❌ | 列表项 |
|
||||||
| | CodeViewer | ❌ | ❌ | 代码查看器 (外部依赖) |
|
| | CodeViewer | ❌ | ❌ | 代码查看器 (外部依赖) |
|
||||||
| | OGCard | ❌ | ❌ | OG 卡片 |
|
| | OGCard | ❌ | ❌ | OG 卡片 |
|
||||||
| | MarkdownShadowDOMRenderer | ❌ | ❌ | Markdown 渲染器 |
|
| | MarkdownShadowDOMRenderer | ❌ | ❌ | Markdown 渲染器 |
|
||||||
@ -105,8 +106,8 @@ function MyComponent() {
|
|||||||
| | InfoTooltip | ✅ | ❌ | 信息提示 |
|
| | InfoTooltip | ✅ | ❌ | 信息提示 |
|
||||||
| | HelpTooltip | ✅ | ❌ | 帮助提示 |
|
| | HelpTooltip | ✅ | ❌ | 帮助提示 |
|
||||||
| | WarnTooltip | ✅ | ❌ | 警告提示 |
|
| | WarnTooltip | ✅ | ❌ | 警告提示 |
|
||||||
|
| | EditableNumber | ✅ | ❌ | 可编辑数字 |
|
||||||
| | DraggableList | ❌ | ❌ | 可拖拽列表 |
|
| | DraggableList | ❌ | ❌ | 可拖拽列表 |
|
||||||
| | EditableNumber | ❌ | ❌ | 可编辑数字 |
|
|
||||||
| | EmojiPicker | ❌ | ❌ | 表情选择器 |
|
| | EmojiPicker | ❌ | ❌ | 表情选择器 |
|
||||||
| | Selector | ✅ | ❌ | 选择器 (i18n 依赖) |
|
| | Selector | ✅ | ❌ | 选择器 (i18n 依赖) |
|
||||||
| | ModelSelector | ❌ | ❌ | 模型选择器 (Redux 依赖) |
|
| | ModelSelector | ❌ | ❌ | 模型选择器 (Redux 依赖) |
|
||||||
|
|||||||
@ -48,9 +48,9 @@ When submitting PRs, please place components in the correct directory based on t
|
|||||||
## Migration Overview
|
## Migration Overview
|
||||||
|
|
||||||
- **Total Components**: 236
|
- **Total Components**: 236
|
||||||
- **Migrated**: 36
|
- **Migrated**: 38
|
||||||
- **Refactored**: 0
|
- **Refactored**: 0
|
||||||
- **Pending Migration**: 200
|
- **Pending Migration**: 198
|
||||||
|
|
||||||
## Component Status Table
|
## Component Status Table
|
||||||
|
|
||||||
@ -73,6 +73,7 @@ When submitting PRs, please place components in the correct directory based on t
|
|||||||
| | ExpandableText | ✅ | ❌ | Expandable text |
|
| | ExpandableText | ✅ | ❌ | Expandable text |
|
||||||
| | ThinkingEffect | ✅ | ❌ | Thinking effect animation |
|
| | ThinkingEffect | ✅ | ❌ | Thinking effect animation |
|
||||||
| | EmojiAvatar | ✅ | ❌ | Emoji avatar |
|
| | EmojiAvatar | ✅ | ❌ | Emoji avatar |
|
||||||
|
| | ListItem | ✅ | ❌ | List item |
|
||||||
| | CodeViewer | ❌ | ❌ | Code viewer (external deps) |
|
| | CodeViewer | ❌ | ❌ | Code viewer (external deps) |
|
||||||
| | OGCard | ❌ | ❌ | OG card |
|
| | OGCard | ❌ | ❌ | OG card |
|
||||||
| | MarkdownShadowDOMRenderer | ❌ | ❌ | Markdown renderer |
|
| | MarkdownShadowDOMRenderer | ❌ | ❌ | Markdown renderer |
|
||||||
@ -104,8 +105,8 @@ When submitting PRs, please place components in the correct directory based on t
|
|||||||
| | InfoTooltip | ✅ | ❌ | Info tooltip |
|
| | InfoTooltip | ✅ | ❌ | Info tooltip |
|
||||||
| | HelpTooltip | ✅ | ❌ | Help tooltip |
|
| | HelpTooltip | ✅ | ❌ | Help tooltip |
|
||||||
| | WarnTooltip | ✅ | ❌ | Warning tooltip |
|
| | WarnTooltip | ✅ | ❌ | Warning tooltip |
|
||||||
|
| | EditableNumber | ✅ | ❌ | Editable number |
|
||||||
| | DraggableList | ❌ | ❌ | Draggable list |
|
| | DraggableList | ❌ | ❌ | Draggable list |
|
||||||
| | EditableNumber | ❌ | ❌ | Editable number |
|
|
||||||
| | EmojiPicker | ❌ | ❌ | Emoji picker |
|
| | EmojiPicker | ❌ | ❌ | Emoji picker |
|
||||||
| | Selector | ✅ | ❌ | Selector (i18n dependency) |
|
| | Selector | ✅ | ❌ | Selector (i18n dependency) |
|
||||||
| | ModelSelector | ❌ | ❌ | Model selector (Redux dependency) |
|
| | ModelSelector | ❌ | ❌ | Model selector (Redux dependency) |
|
||||||
|
|||||||
92
packages/ui/src/components/display/ListItem/index.tsx
Normal file
92
packages/ui/src/components/display/ListItem/index.tsx
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// Original path: src/renderer/src/components/ListItem/index.tsx
|
||||||
|
import { Typography } from 'antd'
|
||||||
|
import { ReactNode } from 'react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
interface ListItemProps {
|
||||||
|
active?: boolean
|
||||||
|
icon?: ReactNode
|
||||||
|
title: ReactNode
|
||||||
|
subtitle?: string
|
||||||
|
titleStyle?: React.CSSProperties
|
||||||
|
onClick?: () => void
|
||||||
|
rightContent?: ReactNode
|
||||||
|
style?: React.CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListItem = ({ active, icon, title, subtitle, titleStyle, onClick, rightContent, style }: ListItemProps) => {
|
||||||
|
return (
|
||||||
|
<ListItemContainer className={active ? 'active' : ''} onClick={onClick} style={style}>
|
||||||
|
<ListItemContent>
|
||||||
|
{icon && <IconWrapper>{icon}</IconWrapper>}
|
||||||
|
<TextContainer>
|
||||||
|
<Typography.Text style={titleStyle} ellipsis={{ expanded: false, tooltip: title }}>
|
||||||
|
{title}
|
||||||
|
</Typography.Text>
|
||||||
|
{subtitle && <SubtitleText>{subtitle}</SubtitleText>}
|
||||||
|
</TextContainer>
|
||||||
|
{rightContent && <RightContentWrapper>{rightContent}</RightContentWrapper>}
|
||||||
|
</ListItemContent>
|
||||||
|
</ListItemContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListItemContainer = styled.div`
|
||||||
|
padding: 7px 12px;
|
||||||
|
border-radius: var(--list-item-border-radius);
|
||||||
|
font-size: 13px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-background-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: var(--color-background-soft);
|
||||||
|
border: 1px solid var(--color-border-soft);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const ListItemContent = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 13px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const IconWrapper = styled.span`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-right: 8px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const TextContainer = styled.div`
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
`
|
||||||
|
|
||||||
|
const SubtitleText = styled.div`
|
||||||
|
font-size: 10px;
|
||||||
|
color: var(--color-text-soft);
|
||||||
|
margin-top: 2px;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
`
|
||||||
|
|
||||||
|
const RightContentWrapper = styled.div`
|
||||||
|
margin-left: auto;
|
||||||
|
`
|
||||||
|
|
||||||
|
export default ListItem
|
||||||
@ -15,6 +15,7 @@ export { WarnTag } from './base/WarnTag'
|
|||||||
export { default as Ellipsis } from './display/Ellipsis'
|
export { default as Ellipsis } from './display/Ellipsis'
|
||||||
export { default as EmojiAvatar } from './display/EmojiAvatar'
|
export { default as EmojiAvatar } from './display/EmojiAvatar'
|
||||||
export { default as ExpandableText } from './display/ExpandableText'
|
export { default as ExpandableText } from './display/ExpandableText'
|
||||||
|
export { default as ListItem } from './display/ListItem'
|
||||||
export { default as ThinkingEffect } from './display/ThinkingEffect'
|
export { default as ThinkingEffect } from './display/ThinkingEffect'
|
||||||
|
|
||||||
// Layout Components
|
// Layout Components
|
||||||
@ -39,6 +40,8 @@ export { default as WebSearchIcon } from './icons/WebSearchIcon'
|
|||||||
export { default as WrapIcon } from './icons/WrapIcon'
|
export { default as WrapIcon } from './icons/WrapIcon'
|
||||||
|
|
||||||
// Interactive Components
|
// Interactive Components
|
||||||
|
export type { EditableNumberProps } from './interactive/EditableNumber'
|
||||||
|
export { default as EditableNumber } from './interactive/EditableNumber'
|
||||||
export { default as HelpTooltip } from './interactive/HelpTooltip'
|
export { default as HelpTooltip } from './interactive/HelpTooltip'
|
||||||
export { default as InfoTooltip } from './interactive/InfoTooltip'
|
export { default as InfoTooltip } from './interactive/InfoTooltip'
|
||||||
export { default as Selector } from './interactive/Selector'
|
export { default as Selector } from './interactive/Selector'
|
||||||
|
|||||||
116
packages/ui/src/components/interactive/EditableNumber/index.tsx
Normal file
116
packages/ui/src/components/interactive/EditableNumber/index.tsx
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// Original path: src/renderer/src/components/EditableNumber/index.tsx
|
||||||
|
import { InputNumber } from 'antd'
|
||||||
|
import { FC, useEffect, useRef, useState } from 'react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
export interface EditableNumberProps {
|
||||||
|
value?: number | null
|
||||||
|
min?: number
|
||||||
|
max?: number
|
||||||
|
step?: number
|
||||||
|
precision?: number
|
||||||
|
placeholder?: string
|
||||||
|
disabled?: boolean
|
||||||
|
changeOnBlur?: boolean
|
||||||
|
onChange?: (value: number | null) => void
|
||||||
|
onBlur?: () => void
|
||||||
|
style?: React.CSSProperties
|
||||||
|
className?: string
|
||||||
|
size?: 'small' | 'middle' | 'large'
|
||||||
|
suffix?: string
|
||||||
|
prefix?: string
|
||||||
|
align?: 'start' | 'center' | 'end'
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditableNumber: FC<EditableNumberProps> = ({
|
||||||
|
value,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
step = 0.01,
|
||||||
|
precision,
|
||||||
|
placeholder,
|
||||||
|
disabled = false,
|
||||||
|
onChange,
|
||||||
|
onBlur,
|
||||||
|
changeOnBlur = false,
|
||||||
|
style,
|
||||||
|
className,
|
||||||
|
size = 'middle',
|
||||||
|
align = 'end'
|
||||||
|
}) => {
|
||||||
|
const [isEditing, setIsEditing] = useState(false)
|
||||||
|
const [inputValue, setInputValue] = useState(value)
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setInputValue(value)
|
||||||
|
}, [value])
|
||||||
|
|
||||||
|
const handleFocus = () => {
|
||||||
|
if (disabled) return
|
||||||
|
setIsEditing(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleInputChange = (newValue: number | null) => {
|
||||||
|
onChange?.(newValue ?? null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBlur = () => {
|
||||||
|
setIsEditing(false)
|
||||||
|
onBlur?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
handleBlur()
|
||||||
|
} else if (e.key === 'Escape') {
|
||||||
|
e.stopPropagation()
|
||||||
|
setInputValue(value)
|
||||||
|
setIsEditing(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<InputNumber
|
||||||
|
style={{ ...style, opacity: isEditing ? 1 : 0 }}
|
||||||
|
ref={inputRef}
|
||||||
|
value={inputValue}
|
||||||
|
min={min}
|
||||||
|
max={max}
|
||||||
|
step={step}
|
||||||
|
precision={precision}
|
||||||
|
size={size}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
onFocus={handleFocus}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
className={className}
|
||||||
|
controls={isEditing}
|
||||||
|
changeOnBlur={changeOnBlur}
|
||||||
|
/>
|
||||||
|
<DisplayText style={style} className={className} $align={align} $isEditing={isEditing}>
|
||||||
|
{value ?? placeholder}
|
||||||
|
</DisplayText>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
`
|
||||||
|
|
||||||
|
const DisplayText = styled.div<{
|
||||||
|
$align: 'start' | 'center' | 'end'
|
||||||
|
$isEditing: boolean
|
||||||
|
}>`
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
display: ${({ $isEditing }) => ($isEditing ? 'none' : 'flex')};
|
||||||
|
align-items: center;
|
||||||
|
justify-content: ${({ $align }) => $align};
|
||||||
|
pointer-events: none;
|
||||||
|
`
|
||||||
|
|
||||||
|
export default EditableNumber
|
||||||
Loading…
Reference in New Issue
Block a user