fc75fcfc90
- 新增 Resizer 组件,支持拖拽调整面板宽度 - IDE 和聊天区域宽度可通过拖拽分割线调整 - 宽度范围限制在 30%-80%,防止面板过小或过大 - 调整后的宽度保存到 localStorage,下次打开时保持
80 lines
2.3 KiB
TypeScript
80 lines
2.3 KiB
TypeScript
/**
|
|
* Resizer Component
|
|
*
|
|
* 可拖拽的分割线,用于调整面板宽度
|
|
*/
|
|
|
|
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
import { cn } from '../utils/cn.js';
|
|
|
|
interface ResizerProps {
|
|
/** 拖拽时的回调,返回拖拽的像素偏移量 */
|
|
onResize: (delta: number) => void;
|
|
/** 拖拽结束的回调 */
|
|
onResizeEnd?: () => void;
|
|
/** 方向:vertical(左右拖拽)或 horizontal(上下拖拽) */
|
|
direction?: 'vertical' | 'horizontal';
|
|
className?: string;
|
|
}
|
|
|
|
export function Resizer({
|
|
onResize,
|
|
onResizeEnd,
|
|
direction = 'vertical',
|
|
className,
|
|
}: ResizerProps) {
|
|
const [isDragging, setIsDragging] = useState(false);
|
|
const startPosRef = useRef(0);
|
|
|
|
const handleMouseDown = useCallback((e: React.MouseEvent) => {
|
|
e.preventDefault();
|
|
setIsDragging(true);
|
|
startPosRef.current = direction === 'vertical' ? e.clientX : e.clientY;
|
|
}, [direction]);
|
|
|
|
const handleMouseMove = useCallback((e: MouseEvent) => {
|
|
if (!isDragging) return;
|
|
|
|
const currentPos = direction === 'vertical' ? e.clientX : e.clientY;
|
|
const delta = currentPos - startPosRef.current;
|
|
startPosRef.current = currentPos;
|
|
onResize(delta);
|
|
}, [isDragging, direction, onResize]);
|
|
|
|
const handleMouseUp = useCallback(() => {
|
|
if (isDragging) {
|
|
setIsDragging(false);
|
|
onResizeEnd?.();
|
|
}
|
|
}, [isDragging, onResizeEnd]);
|
|
|
|
useEffect(() => {
|
|
if (isDragging) {
|
|
document.addEventListener('mousemove', handleMouseMove);
|
|
document.addEventListener('mouseup', handleMouseUp);
|
|
// 拖拽时禁止选中文本
|
|
document.body.style.userSelect = 'none';
|
|
document.body.style.cursor = direction === 'vertical' ? 'col-resize' : 'row-resize';
|
|
}
|
|
|
|
return () => {
|
|
document.removeEventListener('mousemove', handleMouseMove);
|
|
document.removeEventListener('mouseup', handleMouseUp);
|
|
document.body.style.userSelect = '';
|
|
document.body.style.cursor = '';
|
|
};
|
|
}, [isDragging, handleMouseMove, handleMouseUp, direction]);
|
|
|
|
return (
|
|
<div
|
|
onMouseDown={handleMouseDown}
|
|
className={cn(
|
|
'flex-shrink-0 bg-line hover:bg-primary-500 transition-colors',
|
|
direction === 'vertical' ? 'w-1 cursor-col-resize' : 'h-1 cursor-row-resize',
|
|
isDragging && 'bg-primary-500',
|
|
className
|
|
)}
|
|
/>
|
|
);
|
|
}
|