feat(组件): 新增 Tooltip 组件并优化输入区交互体验
- 新增通用 Tooltip 组件,支持上下左右四个方向显示 - ChatInput 组件使用 Tooltip 替代原生 title 属性 - ChatInput 添加附件图标改为 Paperclip,更直观 - ToolsDropdown 工具按钮添加 Tooltip 提示 - 优化按钮 cursor 样式,提升交互体验
This commit is contained in:
parent
3b0683faf9
commit
f50766b742
@ -1,10 +1,11 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useRef } from 'react';
|
import { useState, useRef } from 'react';
|
||||||
import { Plus, ArrowUp, Upload } from 'lucide-react';
|
import { Paperclip, ArrowUp, Upload } from 'lucide-react';
|
||||||
import { ModelSelector } from './ModelSelector';
|
import { ModelSelector } from './ModelSelector';
|
||||||
import { ToolsDropdown } from './ToolsDropdown';
|
import { ToolsDropdown } from './ToolsDropdown';
|
||||||
import { FilePreviewList } from './FilePreviewList';
|
import { FilePreviewList } from './FilePreviewList';
|
||||||
|
import { Tooltip } from '@/components/ui/Tooltip';
|
||||||
import { useFileUpload } from '@/hooks/useFileUpload';
|
import { useFileUpload } from '@/hooks/useFileUpload';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { Model, Tool } from '@/types';
|
import type { Model, Tool } from '@/types';
|
||||||
@ -136,17 +137,18 @@ export function ChatInput({
|
|||||||
{/* 左侧按钮 */}
|
{/* 左侧按钮 */}
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
{/* 添加附件 */}
|
{/* 添加附件 */}
|
||||||
|
<Tooltip content="添加附件">
|
||||||
<button
|
<button
|
||||||
onClick={openFileDialog}
|
onClick={openFileDialog}
|
||||||
className={cn(
|
className={cn(
|
||||||
'w-8 h-8 flex items-center justify-center rounded-lg transition-colors',
|
'w-8 h-8 flex items-center justify-center rounded-lg transition-colors cursor-pointer',
|
||||||
'text-[var(--color-text-tertiary)] hover:bg-[var(--color-bg-hover)] hover:text-[var(--color-text-secondary)]',
|
'text-[var(--color-text-tertiary)] hover:bg-[var(--color-bg-hover)] hover:text-[var(--color-text-secondary)]',
|
||||||
files.length > 0 && 'text-[var(--color-primary)]'
|
files.length > 0 && 'text-[var(--color-primary)]'
|
||||||
)}
|
)}
|
||||||
title="添加附件"
|
|
||||||
>
|
>
|
||||||
<Plus size={20} />
|
<Paperclip size={20} />
|
||||||
</button>
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
{/* 隐藏的文件输入 */}
|
{/* 隐藏的文件输入 */}
|
||||||
<input
|
<input
|
||||||
@ -176,19 +178,20 @@ export function ChatInput({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 发送按钮 */}
|
{/* 发送按钮 */}
|
||||||
|
<Tooltip content="发送消息">
|
||||||
<button
|
<button
|
||||||
onClick={handleSend}
|
onClick={handleSend}
|
||||||
disabled={!message.trim() && files.length === 0}
|
disabled={!message.trim() && files.length === 0}
|
||||||
className={cn(
|
className={cn(
|
||||||
'w-[38px] h-[38px] flex items-center justify-center bg-[var(--color-primary)] text-white rounded-xl transition-all duration-150',
|
'w-[38px] h-[38px] flex items-center justify-center bg-[var(--color-primary)] text-white rounded-xl transition-all duration-150 cursor-pointer',
|
||||||
message.trim() || files.length > 0
|
message.trim() || files.length > 0
|
||||||
? 'hover:bg-[var(--color-primary-hover)] hover:-translate-y-0.5'
|
? 'hover:bg-[var(--color-primary-hover)] hover:-translate-y-0.5'
|
||||||
: 'opacity-50 cursor-not-allowed'
|
: 'opacity-50 cursor-not-allowed'
|
||||||
)}
|
)}
|
||||||
title="Send message"
|
|
||||||
>
|
>
|
||||||
<ArrowUp size={18} />
|
<ArrowUp size={18} />
|
||||||
</button>
|
</button>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
import { useState, useRef, useEffect } from 'react';
|
import { useState, useRef, useEffect } from 'react';
|
||||||
import { Wrench, Search, Terminal, Globe, Check } from 'lucide-react';
|
import { Wrench, Search, Terminal, Globe, Check } from 'lucide-react';
|
||||||
import { Toggle } from '@/components/ui/Toggle';
|
import { Toggle } from '@/components/ui/Toggle';
|
||||||
|
import { Tooltip } from '@/components/ui/Tooltip';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { Tool } from '@/types';
|
import type { Tool } from '@/types';
|
||||||
|
|
||||||
@ -41,20 +42,21 @@ export function ToolsDropdown({ tools, onToolToggle, onEnableAllToggle }: ToolsD
|
|||||||
<div className="relative" ref={dropdownRef}>
|
<div className="relative" ref={dropdownRef}>
|
||||||
{/* 触发按钮 */}
|
{/* 触发按钮 */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
|
<Tooltip content="工具" position="top">
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsOpen(!isOpen)}
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
className={cn(
|
className={cn(
|
||||||
'w-8 h-8 flex items-center justify-center rounded-lg transition-colors',
|
'w-8 h-8 flex items-center justify-center rounded-lg transition-colors cursor-pointer',
|
||||||
enabledCount > 0
|
enabledCount > 0
|
||||||
? 'text-[var(--color-primary)] bg-[var(--color-primary-light)]'
|
? 'text-[var(--color-primary)] bg-[var(--color-primary-light)]'
|
||||||
: 'text-[var(--color-text-tertiary)] hover:bg-[var(--color-bg-hover)] hover:text-[var(--color-text-secondary)]'
|
: 'text-[var(--color-text-tertiary)] hover:bg-[var(--color-bg-hover)] hover:text-[var(--color-text-secondary)]'
|
||||||
)}
|
)}
|
||||||
title="Tools"
|
|
||||||
>
|
>
|
||||||
<Wrench size={20} />
|
<Wrench size={20} />
|
||||||
</button>
|
</button>
|
||||||
|
</Tooltip>
|
||||||
{enabledCount > 0 && (
|
{enabledCount > 0 && (
|
||||||
<span className="absolute -top-0.5 -right-0.5 min-w-4 h-4 bg-[var(--color-primary)] text-white text-[10px] font-semibold rounded-full flex items-center justify-center px-1">
|
<span className="absolute -top-0.5 -right-0.5 min-w-4 h-4 bg-[var(--color-primary)] text-white text-[10px] font-semibold rounded-full flex items-center justify-center px-1 pointer-events-none">
|
||||||
{enabledCount}
|
{enabledCount}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
55
src/components/ui/Tooltip.tsx
Normal file
55
src/components/ui/Tooltip.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
interface TooltipProps {
|
||||||
|
children: ReactNode;
|
||||||
|
content: string;
|
||||||
|
position?: 'top' | 'bottom' | 'left' | 'right';
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tooltip 组件
|
||||||
|
* 鼠标悬停时显示提示文字
|
||||||
|
*/
|
||||||
|
export function Tooltip({ children, content, position = 'top', className }: TooltipProps) {
|
||||||
|
const positionStyles = {
|
||||||
|
top: 'bottom-full left-1/2 -translate-x-1/2 mb-2',
|
||||||
|
bottom: 'top-full left-1/2 -translate-x-1/2 mt-2',
|
||||||
|
left: 'right-full top-1/2 -translate-y-1/2 mr-2',
|
||||||
|
right: 'left-full top-1/2 -translate-y-1/2 ml-2',
|
||||||
|
};
|
||||||
|
|
||||||
|
const arrowStyles = {
|
||||||
|
top: 'top-full left-1/2 -translate-x-1/2 border-t-gray-800 border-x-transparent border-b-transparent',
|
||||||
|
bottom: 'bottom-full left-1/2 -translate-x-1/2 border-b-gray-800 border-x-transparent border-t-transparent',
|
||||||
|
left: 'left-full top-1/2 -translate-y-1/2 border-l-gray-800 border-y-transparent border-r-transparent',
|
||||||
|
right: 'right-full top-1/2 -translate-y-1/2 border-r-gray-800 border-y-transparent border-l-transparent',
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn('relative group/tooltip inline-flex', className)}>
|
||||||
|
{children}
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'absolute z-50 px-2.5 py-1.5 text-xs font-medium text-white bg-gray-800 rounded-md',
|
||||||
|
'opacity-0 invisible group-hover/tooltip:opacity-100 group-hover/tooltip:visible',
|
||||||
|
'transition-all duration-200 whitespace-nowrap pointer-events-none',
|
||||||
|
'shadow-lg',
|
||||||
|
positionStyles[position]
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
{/* 小三角箭头 */}
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'absolute w-0 h-0 border-4',
|
||||||
|
arrowStyles[position]
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user