mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-29 14:31:35 +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
|
||||
- **已迁移**: 36
|
||||
- **已迁移**: 38
|
||||
- **已重构**: 0
|
||||
- **待迁移**: 200
|
||||
- **待迁移**: 198
|
||||
|
||||
## 组件状态表
|
||||
|
||||
@ -74,6 +74,7 @@ function MyComponent() {
|
||||
| | ExpandableText | ✅ | ❌ | 可展开文本 |
|
||||
| | ThinkingEffect | ✅ | ❌ | 思考效果动画 |
|
||||
| | EmojiAvatar | ✅ | ❌ | 表情头像 |
|
||||
| | ListItem | ✅ | ❌ | 列表项 |
|
||||
| | CodeViewer | ❌ | ❌ | 代码查看器 (外部依赖) |
|
||||
| | OGCard | ❌ | ❌ | OG 卡片 |
|
||||
| | MarkdownShadowDOMRenderer | ❌ | ❌ | Markdown 渲染器 |
|
||||
@ -105,8 +106,8 @@ function MyComponent() {
|
||||
| | InfoTooltip | ✅ | ❌ | 信息提示 |
|
||||
| | HelpTooltip | ✅ | ❌ | 帮助提示 |
|
||||
| | WarnTooltip | ✅ | ❌ | 警告提示 |
|
||||
| | EditableNumber | ✅ | ❌ | 可编辑数字 |
|
||||
| | DraggableList | ❌ | ❌ | 可拖拽列表 |
|
||||
| | EditableNumber | ❌ | ❌ | 可编辑数字 |
|
||||
| | EmojiPicker | ❌ | ❌ | 表情选择器 |
|
||||
| | Selector | ✅ | ❌ | 选择器 (i18n 依赖) |
|
||||
| | ModelSelector | ❌ | ❌ | 模型选择器 (Redux 依赖) |
|
||||
|
||||
@ -48,9 +48,9 @@ When submitting PRs, please place components in the correct directory based on t
|
||||
## Migration Overview
|
||||
|
||||
- **Total Components**: 236
|
||||
- **Migrated**: 36
|
||||
- **Migrated**: 38
|
||||
- **Refactored**: 0
|
||||
- **Pending Migration**: 200
|
||||
- **Pending Migration**: 198
|
||||
|
||||
## Component Status Table
|
||||
|
||||
@ -73,6 +73,7 @@ When submitting PRs, please place components in the correct directory based on t
|
||||
| | ExpandableText | ✅ | ❌ | Expandable text |
|
||||
| | ThinkingEffect | ✅ | ❌ | Thinking effect animation |
|
||||
| | EmojiAvatar | ✅ | ❌ | Emoji avatar |
|
||||
| | ListItem | ✅ | ❌ | List item |
|
||||
| | CodeViewer | ❌ | ❌ | Code viewer (external deps) |
|
||||
| | OGCard | ❌ | ❌ | OG card |
|
||||
| | MarkdownShadowDOMRenderer | ❌ | ❌ | Markdown renderer |
|
||||
@ -104,8 +105,8 @@ When submitting PRs, please place components in the correct directory based on t
|
||||
| | InfoTooltip | ✅ | ❌ | Info tooltip |
|
||||
| | HelpTooltip | ✅ | ❌ | Help tooltip |
|
||||
| | WarnTooltip | ✅ | ❌ | Warning tooltip |
|
||||
| | EditableNumber | ✅ | ❌ | Editable number |
|
||||
| | DraggableList | ❌ | ❌ | Draggable list |
|
||||
| | EditableNumber | ❌ | ❌ | Editable number |
|
||||
| | EmojiPicker | ❌ | ❌ | Emoji picker |
|
||||
| | Selector | ✅ | ❌ | Selector (i18n 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 EmojiAvatar } from './display/EmojiAvatar'
|
||||
export { default as ExpandableText } from './display/ExpandableText'
|
||||
export { default as ListItem } from './display/ListItem'
|
||||
export { default as ThinkingEffect } from './display/ThinkingEffect'
|
||||
|
||||
// Layout Components
|
||||
@ -39,6 +40,8 @@ export { default as WebSearchIcon } from './icons/WebSearchIcon'
|
||||
export { default as WrapIcon } from './icons/WrapIcon'
|
||||
|
||||
// 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 InfoTooltip } from './interactive/InfoTooltip'
|
||||
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