Files
Genarrative/src/components/image-editor/ImageCanvasBottomToolbarView.tsx
kdletters dfe6bafb5e 接入创作主页画布直达引导
创作工具卡片接入画布生成器直达意图

新建空白画布增加工具栏引导并在生成面板打开后关闭

暂未开放工具复用移动端欢迎弹窗提示

补充主页、项目入口和画布启动意图测试

同步创作主页入口改版文档
2026-06-30 18:13:23 +08:00

156 lines
4.7 KiB
TypeScript

import {
AppWindow,
Clapperboard,
ClipboardList,
Grid2X2,
Hand,
ImageIcon,
Megaphone,
MousePointer2,
Music,
Upload,
UserRound,
} from 'lucide-react';
import type { RefObject } from 'react';
import { EditorIconButton } from './ImageCanvasEditorPrimitives';
import type { CanvasTool } from './ImageCanvasEditorTypes';
type ToolbarOptionTool = Extract<CanvasTool, 'music' | 'spec' | 'publication'>;
type ImageCanvasBottomToolbarViewProps = {
specToolWrapRef: RefObject<HTMLSpanElement | null>;
musicToolWrapRef: RefObject<HTMLSpanElement | null>;
publicationToolWrapRef: RefObject<HTMLSpanElement | null>;
effectiveTool: CanvasTool;
highlight?: boolean;
onSwitchTool: (tool: CanvasTool) => void;
onOpenToolOptions: (tool: ToolbarOptionTool) => void;
onCloseToolOptions: (tool: ToolbarOptionTool) => void;
};
const canvasTools: Array<{
id: CanvasTool;
label: string;
icon: typeof MousePointer2;
separatorBefore?: boolean;
}> = [
{ id: 'select', label: '选择工具', icon: MousePointer2 },
{ id: 'hand', label: '抓手工具', icon: Hand },
{ id: 'upload', label: '上传工具', icon: Upload },
{
id: 'generate',
label: '生成图片',
icon: ImageIcon,
separatorBefore: true,
},
{ id: 'video', label: '生成视频', icon: Clapperboard },
{ id: 'music', label: '生成音乐', icon: Music },
{
id: 'spec',
label: '生成规范',
icon: ClipboardList,
separatorBefore: true,
},
{ id: 'character', label: '生成角色形象', icon: UserRound },
{ id: 'icon', label: '生成图标素材', icon: Grid2X2 },
{ id: 'ui-design', label: '生成UI设计图', icon: AppWindow },
{ id: 'publication', label: '宣发素材', icon: Megaphone },
];
function isToolbarOptionTool(tool: CanvasTool): tool is ToolbarOptionTool {
return tool === 'music' || tool === 'spec' || tool === 'publication';
}
function hasPersistentPressedState(tool: CanvasTool) {
return tool === 'select' || tool === 'hand';
}
export function ImageCanvasBottomToolbarView({
specToolWrapRef,
musicToolWrapRef,
publicationToolWrapRef,
effectiveTool,
highlight = false,
onSwitchTool,
onOpenToolOptions,
onCloseToolOptions,
}: ImageCanvasBottomToolbarViewProps) {
return (
<div
className={[
'image-canvas-editor__bottom-toolbar',
highlight ? 'image-canvas-editor__bottom-toolbar--guided' : '',
]
.filter(Boolean)
.join(' ')}
role="toolbar"
aria-label="AI画布工具栏"
onPointerDown={(event) => event.stopPropagation()}
>
{canvasTools.map(({ id, label, icon: Icon, separatorBefore }) => {
if (isToolbarOptionTool(id)) {
return (
<span key={id} className="image-canvas-editor__bottom-tool-group">
{separatorBefore ? (
<span
aria-hidden="true"
className="image-canvas-editor__bottom-toolbar-divider"
/>
) : null}
<span
ref={
id === 'spec'
? specToolWrapRef
: id === 'music'
? musicToolWrapRef
: publicationToolWrapRef
}
className={[
'image-canvas-editor__bottom-toolbar-option-wrap',
id === 'spec' ? 'image-canvas-editor__spec-tool-wrap' : '',
id === 'publication'
? 'image-canvas-editor__publication-tool-wrap'
: '',
]
.filter(Boolean)
.join(' ')}
onPointerEnter={() => onOpenToolOptions(id)}
onPointerLeave={() => onCloseToolOptions(id)}
onFocus={() => onOpenToolOptions(id)}
onBlur={() => onCloseToolOptions(id)}
>
<EditorIconButton
label={label}
title={label}
icon={Icon}
pressed={hasPersistentPressedState(id) && effectiveTool === id}
onClick={() => onSwitchTool(id)}
/>
</span>
</span>
);
}
return (
<span key={id} className="image-canvas-editor__bottom-tool-group">
{separatorBefore ? (
<span
aria-hidden="true"
className="image-canvas-editor__bottom-toolbar-divider"
/>
) : null}
<EditorIconButton
label={label}
title={label}
icon={Icon}
pressed={hasPersistentPressedState(id) && effectiveTool === id}
onClick={() => onSwitchTool(id)}
/>
</span>
);
})}
</div>
);
}