fix(mindmap): force single-line toolbar layout with horizontal scroll
This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user