278 lines
6.4 KiB
TypeScript
278 lines
6.4 KiB
TypeScript
import React from "react";
|
|
import {
|
|
BoxSelect,
|
|
Braces,
|
|
Cable,
|
|
Download,
|
|
FileText,
|
|
FolderOpen,
|
|
Image as ImageIcon,
|
|
Link as LinkIcon,
|
|
PaintRoller,
|
|
Paperclip,
|
|
Plus,
|
|
Redo2,
|
|
Save,
|
|
Sigma,
|
|
Smile,
|
|
Sparkles,
|
|
StickyNote,
|
|
Tag,
|
|
Trash2,
|
|
Undo2,
|
|
} from "lucide-react";
|
|
|
|
type ToolbarProps = {
|
|
canBack: boolean;
|
|
canForward: boolean;
|
|
activeCount: number;
|
|
painterMode: boolean;
|
|
onUndo: () => void;
|
|
onRedo: () => void;
|
|
onPainter: () => void;
|
|
onSibling: () => void;
|
|
onChild: () => void;
|
|
onDelete: () => void;
|
|
onImage: () => void;
|
|
onIcon: () => void;
|
|
onLink: () => void;
|
|
onNote: () => void;
|
|
onTag: () => void;
|
|
onSummary: () => void;
|
|
onAssociativeLine: () => void;
|
|
onFormula: () => void;
|
|
onAttachment: () => void;
|
|
onOuterFrame: () => void;
|
|
onAi: () => void;
|
|
onImport: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
onNew: () => void;
|
|
onOpenDirectory: () => void;
|
|
onSaveAs: () => void;
|
|
onExportJson: () => void;
|
|
onExportPng: () => void;
|
|
fileInputRef: React.RefObject<HTMLInputElement>;
|
|
};
|
|
|
|
const ToolbarButton = ({
|
|
icon: Icon,
|
|
label,
|
|
onClick,
|
|
disabled = false,
|
|
active = false,
|
|
className = "",
|
|
}: {
|
|
icon: React.ElementType;
|
|
label: string;
|
|
onClick?: () => void;
|
|
disabled?: boolean;
|
|
active?: boolean;
|
|
className?: string;
|
|
}) => (
|
|
<button
|
|
type="button"
|
|
onClick={onClick}
|
|
disabled={disabled}
|
|
className={`flex flex-col items-center justify-center gap-1 px-2 py-1 transition-colors hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-40 ${
|
|
active ? "bg-blue-50 text-blue-600" : "text-gray-600"
|
|
} ${className}`}
|
|
title={label}
|
|
>
|
|
<div
|
|
className={`flex h-7 w-7 items-center justify-center rounded border shadow-sm transition-colors ${
|
|
active ? "border-blue-200 bg-white" : "border-gray-200 bg-white"
|
|
}`}
|
|
>
|
|
<Icon className="h-4 w-4" />
|
|
</div>
|
|
<span className="text-[10px] font-medium leading-tight">{label}</span>
|
|
</button>
|
|
);
|
|
|
|
const Divider = () => <div className="mx-1 h-8 w-px bg-gray-200" />;
|
|
|
|
export const MindmapToolbar = ({
|
|
canBack,
|
|
canForward,
|
|
activeCount,
|
|
painterMode,
|
|
onUndo,
|
|
onRedo,
|
|
onPainter,
|
|
onSibling,
|
|
onChild,
|
|
onDelete,
|
|
onImage,
|
|
onIcon,
|
|
onLink,
|
|
onNote,
|
|
onTag,
|
|
onSummary,
|
|
onAssociativeLine,
|
|
onFormula,
|
|
onAttachment,
|
|
onOuterFrame,
|
|
onAi,
|
|
onImport,
|
|
onNew,
|
|
onOpenDirectory,
|
|
onSaveAs,
|
|
onExportJson,
|
|
onExportPng,
|
|
fileInputRef,
|
|
}: ToolbarProps) => {
|
|
return (
|
|
<div className="flex w-full items-center justify-between gap-4 overflow-x-auto pb-2 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-gray-200">
|
|
{/* Left Section: Edit & Node Operations */}
|
|
<div className="flex shrink-0 items-center gap-1 rounded-lg border border-gray-100 bg-white px-2 py-1 shadow-sm">
|
|
<ToolbarButton
|
|
icon={Undo2}
|
|
label="回退"
|
|
onClick={onUndo}
|
|
disabled={!canBack}
|
|
/>
|
|
<ToolbarButton
|
|
icon={Redo2}
|
|
label="前进"
|
|
onClick={onRedo}
|
|
disabled={!canForward}
|
|
/>
|
|
<ToolbarButton
|
|
icon={PaintRoller}
|
|
label="格式刷"
|
|
onClick={onPainter}
|
|
active={painterMode}
|
|
/>
|
|
|
|
<Divider />
|
|
|
|
<ToolbarButton
|
|
icon={Plus} // Sibling
|
|
label="同级"
|
|
onClick={onSibling}
|
|
disabled={!activeCount}
|
|
/>
|
|
<ToolbarButton
|
|
icon={Sparkles} // Child
|
|
label="下级"
|
|
onClick={onChild}
|
|
disabled={!activeCount}
|
|
/>
|
|
<ToolbarButton
|
|
icon={Trash2}
|
|
label="删除"
|
|
onClick={onDelete}
|
|
disabled={!activeCount}
|
|
/>
|
|
|
|
<Divider />
|
|
|
|
<ToolbarButton
|
|
icon={ImageIcon}
|
|
label="图片"
|
|
onClick={onImage}
|
|
disabled={!activeCount}
|
|
/>
|
|
<ToolbarButton
|
|
icon={Smile}
|
|
label="图标"
|
|
onClick={onIcon}
|
|
disabled={!activeCount}
|
|
/>
|
|
<ToolbarButton
|
|
icon={LinkIcon}
|
|
label="链接"
|
|
onClick={onLink}
|
|
disabled={!activeCount}
|
|
/>
|
|
<ToolbarButton
|
|
icon={StickyNote}
|
|
label="备注"
|
|
onClick={onNote}
|
|
disabled={!activeCount}
|
|
/>
|
|
<ToolbarButton
|
|
icon={Tag}
|
|
label="标签"
|
|
onClick={onTag}
|
|
disabled={!activeCount}
|
|
/>
|
|
<ToolbarButton
|
|
icon={Braces}
|
|
label="概要"
|
|
onClick={onSummary}
|
|
disabled={!activeCount}
|
|
/>
|
|
<ToolbarButton
|
|
icon={Cable}
|
|
label="关联线"
|
|
onClick={onAssociativeLine}
|
|
disabled={!activeCount}
|
|
/>
|
|
<ToolbarButton
|
|
icon={Sigma}
|
|
label="公式"
|
|
onClick={onFormula}
|
|
disabled={!activeCount}
|
|
/>
|
|
<ToolbarButton
|
|
icon={Paperclip}
|
|
label="附件"
|
|
onClick={onAttachment}
|
|
disabled={!activeCount}
|
|
/>
|
|
<ToolbarButton
|
|
icon={BoxSelect}
|
|
label="外框"
|
|
onClick={onOuterFrame}
|
|
disabled={!activeCount}
|
|
/>
|
|
<ToolbarButton
|
|
icon={Sparkles} // AI
|
|
label="AI"
|
|
onClick={onAi}
|
|
/>
|
|
</div>
|
|
|
|
{/* Right Section: File & Export Actions */}
|
|
<div className="flex shrink-0 items-center gap-1 rounded-lg border border-gray-100 bg-white px-2 py-1 shadow-sm">
|
|
<ToolbarButton
|
|
icon={FolderOpen}
|
|
label="目录"
|
|
onClick={onOpenDirectory}
|
|
/>
|
|
<ToolbarButton icon={FileText} label="新建" onClick={onNew} />
|
|
<ToolbarButton
|
|
icon={FolderOpen}
|
|
label="打开"
|
|
onClick={() => fileInputRef.current?.click()}
|
|
/>
|
|
{/* Hidden Input */}
|
|
<input
|
|
ref={fileInputRef}
|
|
type="file"
|
|
accept=".json,application/json"
|
|
className="hidden"
|
|
onChange={onImport}
|
|
/>
|
|
|
|
<Divider />
|
|
|
|
<ToolbarButton
|
|
icon={Save} // Save As
|
|
label="另存为"
|
|
onClick={onSaveAs}
|
|
/>
|
|
<ToolbarButton
|
|
icon={Download} // Export
|
|
label="导出"
|
|
onClick={onExportJson}
|
|
/>
|
|
<ToolbarButton
|
|
icon={ImageIcon} // Export PNG
|
|
label="PNG"
|
|
onClick={onExportPng}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}; |