125 lines
4.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useRef } from 'react';
import { LayoutGrid, Settings2, Trash2, GripVertical, X, Check } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import { Logo } from './Logo';
interface SidebarItem {
id: string;
label: string;
icon: React.ReactNode;
}
interface SidebarProps {
items: SidebarItem[];
onReorder: (newItems: SidebarItem[]) => void;
onDelete: (id: string) => void;
onItemClick: (id: string) => void;
}
export const Sidebar: React.FC<SidebarProps> = ({ items, onReorder, onDelete, onItemClick }) => {
const navigate = useNavigate();
const [isEditing, setIsEditing] = useState(false);
const dragItem = useRef<number | null>(null);
const dragOverItem = useRef<number | null>(null);
const handleDragStart = (e: React.DragEvent<HTMLDivElement>, position: number) => {
dragItem.current = position;
e.dataTransfer.effectAllowed = "move";
// Add a ghost class or style if needed
};
const handleDragEnter = (e: React.DragEvent<HTMLDivElement>, position: number) => {
dragOverItem.current = position;
};
const handleDragEnd = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
if (dragItem.current !== null && dragOverItem.current !== null) {
const copyListItems = [...items];
const dragItemContent = copyListItems[dragItem.current];
copyListItems.splice(dragItem.current, 1);
copyListItems.splice(dragOverItem.current, 0, dragItemContent);
onReorder(copyListItems);
}
dragItem.current = null;
dragOverItem.current = null;
};
return (
<aside className="w-64 bg-white border-r border-slate-200 flex-col hidden md:flex h-screen sticky top-0 select-none">
<div
className="h-16 flex items-center px-6 border-b border-slate-100 cursor-pointer hover:bg-slate-50 transition-colors"
onClick={() => navigate('/')}
>
<Logo />
</div>
<div className="flex-1 overflow-y-auto py-6 px-3 space-y-6">
<div>
<div className="flex items-center justify-between px-3 mb-2 text-slate-900 font-semibold group">
<div className="flex items-center gap-3">
<LayoutGrid size={20} />
<span></span>
</div>
<button
onClick={() => setIsEditing(!isEditing)}
className={`p-1.5 rounded-md transition-all ${isEditing ? 'bg-blue-100 text-blue-600' : 'text-slate-400 hover:text-slate-700 hover:bg-slate-100'}`}
title={isEditing ? "完成编辑" : "管理维度"}
>
{isEditing ? <Check size={16} /> : <Settings2 size={16} />}
</button>
</div>
<div className="text-xs font-medium text-slate-500 px-3 mb-3 uppercase tracking-wider">
(600519.SH)
</div>
<nav className="space-y-1">
{items.map((item, index) => (
<div
key={item.id}
draggable={isEditing}
onDragStart={(e) => handleDragStart(e, index)}
onDragEnter={(e) => handleDragEnter(e, index)}
onDragEnd={handleDragEnd}
onDragOver={(e) => e.preventDefault()}
onClick={() => !isEditing && onItemClick(item.id)}
className={`
flex items-center gap-3 px-3 py-2 rounded-lg font-medium text-sm transition-all relative group
${isEditing ? 'cursor-move hover:bg-slate-50 border border-transparent hover:border-slate-200' : 'cursor-pointer text-slate-600 hover:bg-slate-50'}
`}
>
{isEditing && (
<div className="text-slate-400">
<GripVertical size={16} />
</div>
)}
<div className={`${isEditing ? 'text-slate-500' : 'text-slate-600'}`}>
{item.icon}
</div>
<span className="flex-1">{item.label}</span>
{isEditing && (
<button
onClick={(e) => {
e.stopPropagation();
onDelete(item.id);
}}
className="p-1.5 text-slate-400 hover:text-red-500 hover:bg-red-50 rounded-md transition-colors"
>
<Trash2 size={16} />
</button>
)}
</div>
))}
</nav>
</div>
</div>
{isEditing && (
<div className="p-4 bg-blue-50/50 border-t border-blue-100 text-center text-xs text-blue-600 font-medium">
</div>
)}
</aside>
);
};