Files
Genarrative/src/components/image-editor/ImageCanvasSidebarView.tsx
T
kdletters d8b935317d 拆分编辑器前端画布视图
抽出素材栏、生成器、舞台工具栏和画布世界视图

补充各拆分视图的聚焦测试

更新 TRACKING.md 记录第三十四阶段验证
2026-06-17 17:48:12 +08:00

274 lines
9.5 KiB
TypeScript

import {
CheckSquare,
FolderPlus,
Square,
} from 'lucide-react';
import type {
Dispatch,
PointerEvent as ReactPointerEvent,
RefObject,
SetStateAction,
} from 'react';
import {
ImageCanvasAssetLibraryPanelView,
type GroupedEditorAssetFolder,
type UploadFilesOptions,
} from './ImageCanvasAssetLibraryPanelView';
import { EditorIconButton } from './ImageCanvasEditorPrimitives';
import type {
AssetMarqueeState,
AssetPointerDragState,
CanvasContextMenuState,
CanvasLayer,
EditorAsset,
EditorAssetFolder,
ImageContextMenuState,
SidebarPanel,
UploadTarget,
} from './ImageCanvasEditorTypes';
import { ImageCanvasLayerPanelView } from './ImageCanvasLayerPanelView';
export type { GroupedEditorAssetFolder, UploadFilesOptions };
export type ImageCanvasSidebarViewProps = {
activeSidebarPanel: SidebarPanel | null;
assetListRef: RefObject<HTMLDivElement | null>;
assetPointerDragRef: { current: AssetPointerDragState | null };
suppressAssetClickRef: { current: boolean };
assets: EditorAsset[];
groupedAssets: GroupedEditorAssetFolder[];
assetFolders: EditorAssetFolder[];
layers: CanvasLayer[];
selectedLayerId: string | null;
selectedLayerIds: string[];
isAssetSelectionMode: boolean;
selectedAssetIds: Set<string>;
assetMoveDropFolderId: string | null;
pinnedAssetMoveFolderId: string | null;
creatingFolder: boolean;
newFolderName: string;
renamingFolder: { folderId: string; value: string } | null;
renamingAsset: { assetId: string; value: string } | null;
allSelectableAssetsSelected: boolean;
assetMarquee: AssetMarqueeState | null;
setIsAssetSelectionMode: Dispatch<SetStateAction<boolean>>;
setCreatingFolder: Dispatch<SetStateAction<boolean>>;
setNewFolderName: Dispatch<SetStateAction<string>>;
setRenamingFolder: Dispatch<
SetStateAction<{ folderId: string; value: string } | null>
>;
setRenamingAsset: Dispatch<
SetStateAction<{ assetId: string; value: string } | null>
>;
setActiveUploadFolderId: Dispatch<SetStateAction<string>>;
setUploadDropTarget: Dispatch<SetStateAction<'canvas' | 'assets' | null>>;
setAssetPointerDrag: Dispatch<SetStateAction<AssetPointerDragState | null>>;
setSelectedAssetIds: Dispatch<SetStateAction<Set<string>>>;
setImageContextMenu: Dispatch<SetStateAction<ImageContextMenuState | null>>;
setContextMenu: Dispatch<SetStateAction<CanvasContextMenuState | null>>;
onAssetMarqueePointerDown: (
event: ReactPointerEvent<HTMLDivElement>,
) => void;
onAssetMarqueePointerMove: (
event: ReactPointerEvent<HTMLDivElement>,
) => void;
onAssetMarqueePointerUp: (
event: ReactPointerEvent<HTMLDivElement>,
) => void;
updateAssetMoveDropFolder: (folderId: string | null) => void;
addUploadedFiles: (files: FileList | File[], options?: UploadFilesOptions) => void;
requestUpload: (target: UploadTarget) => void;
moveAssetToFolder: (assetId: string, folderId: string) => void;
commitNewAssetFolder: () => void | Promise<void>;
toggleAssetFolder: (folderId: string) => void;
startRenamingFolder: (folder: EditorAssetFolder) => void;
commitFolderRename: (folder: EditorAssetFolder) => void;
deleteAssetFolder: (folder: EditorAssetFolder) => void;
startRenamingAsset: (asset: EditorAsset) => void;
commitAssetRename: (asset: EditorAsset) => void;
deleteUploadedAsset: (asset: EditorAsset) => void;
toggleAssetSelected: (assetId: string) => void;
addAssetLayer: (asset: EditorAsset) => void;
toggleAllAssetsSelected: () => void;
deleteSelectedAssets: () => void;
closeAssetSelectionMode: () => void;
groupSelectedLayers: () => void;
selectSingleLayer: (layerId: string | null) => void;
resolveContextMenuPosition: (
clientX: number,
clientY: number,
menuKind: 'blank' | 'layer',
) => Omit<CanvasContextMenuState, 'kind' | 'layerId' | 'canvasPoint'>;
getCanvasPointFromClient: (
clientX: number,
clientY: number,
) => { x: number; y: number };
};
export function ImageCanvasSidebarView({
activeSidebarPanel,
assetListRef,
assetPointerDragRef,
suppressAssetClickRef,
assets,
groupedAssets,
assetFolders,
layers,
selectedLayerId,
selectedLayerIds,
isAssetSelectionMode,
selectedAssetIds,
assetMoveDropFolderId,
pinnedAssetMoveFolderId,
creatingFolder,
newFolderName,
renamingFolder,
renamingAsset,
allSelectableAssetsSelected,
assetMarquee,
setIsAssetSelectionMode,
setCreatingFolder,
setNewFolderName,
setRenamingFolder,
setRenamingAsset,
setActiveUploadFolderId,
setUploadDropTarget,
setAssetPointerDrag,
setSelectedAssetIds,
setImageContextMenu,
setContextMenu,
onAssetMarqueePointerDown,
onAssetMarqueePointerMove,
onAssetMarqueePointerUp,
updateAssetMoveDropFolder,
addUploadedFiles,
requestUpload,
moveAssetToFolder,
commitNewAssetFolder,
toggleAssetFolder,
startRenamingFolder,
commitFolderRename,
deleteAssetFolder,
startRenamingAsset,
commitAssetRename,
deleteUploadedAsset,
toggleAssetSelected,
addAssetLayer,
toggleAllAssetsSelected,
deleteSelectedAssets,
closeAssetSelectionMode,
groupSelectedLayers,
selectSingleLayer,
resolveContextMenuPosition,
getCanvasPointFromClient,
}: ImageCanvasSidebarViewProps) {
if (!activeSidebarPanel) {
return null;
}
return (
<aside className="image-canvas-editor__sidebar" aria-label="图片资源栏">
<div className="image-canvas-editor__sidebar-header">
<div className="min-w-0">
<h2 className="image-canvas-editor__sidebar-title">
{activeSidebarPanel === 'assets' ? '素材' : '图层'}
</h2>
<div className="image-canvas-editor__sidebar-count">
{activeSidebarPanel === 'assets' ? assets.length : layers.length}
</div>
</div>
{activeSidebarPanel === 'assets' ? (
<div className="image-canvas-editor__sidebar-header-actions">
<EditorIconButton
className="image-canvas-editor__icon-button"
label="素材选择模式"
title="选择"
icon={isAssetSelectionMode ? CheckSquare : Square}
pressed={isAssetSelectionMode}
onClick={() =>
setIsAssetSelectionMode((currentMode) => !currentMode)
}
/>
<EditorIconButton
className="image-canvas-editor__icon-button"
label="新建素材文件夹"
title="新建文件夹"
icon={FolderPlus}
onClick={() => setCreatingFolder(true)}
/>
</div>
) : (
<EditorIconButton
className="image-canvas-editor__icon-button"
label="图层打组"
title="打组"
icon={FolderPlus}
disabled={!selectedLayerId && selectedLayerIds.length === 0}
onClick={groupSelectedLayers}
/>
)}
</div>
{activeSidebarPanel === 'assets' ? (
<ImageCanvasAssetLibraryPanelView
assetListRef={assetListRef}
assetPointerDragRef={assetPointerDragRef}
suppressAssetClickRef={suppressAssetClickRef}
groupedAssets={groupedAssets}
assetFolders={assetFolders}
isAssetSelectionMode={isAssetSelectionMode}
selectedAssetIds={selectedAssetIds}
assetMoveDropFolderId={assetMoveDropFolderId}
pinnedAssetMoveFolderId={pinnedAssetMoveFolderId}
creatingFolder={creatingFolder}
newFolderName={newFolderName}
renamingFolder={renamingFolder}
renamingAsset={renamingAsset}
allSelectableAssetsSelected={allSelectableAssetsSelected}
assetMarquee={assetMarquee}
setCreatingFolder={setCreatingFolder}
setNewFolderName={setNewFolderName}
setRenamingFolder={setRenamingFolder}
setRenamingAsset={setRenamingAsset}
setActiveUploadFolderId={setActiveUploadFolderId}
setUploadDropTarget={setUploadDropTarget}
setAssetPointerDrag={setAssetPointerDrag}
setSelectedAssetIds={setSelectedAssetIds}
onAssetMarqueePointerDown={onAssetMarqueePointerDown}
onAssetMarqueePointerMove={onAssetMarqueePointerMove}
onAssetMarqueePointerUp={onAssetMarqueePointerUp}
updateAssetMoveDropFolder={updateAssetMoveDropFolder}
addUploadedFiles={addUploadedFiles}
requestUpload={requestUpload}
moveAssetToFolder={moveAssetToFolder}
commitNewAssetFolder={commitNewAssetFolder}
toggleAssetFolder={toggleAssetFolder}
startRenamingFolder={startRenamingFolder}
commitFolderRename={commitFolderRename}
deleteAssetFolder={deleteAssetFolder}
startRenamingAsset={startRenamingAsset}
commitAssetRename={commitAssetRename}
deleteUploadedAsset={deleteUploadedAsset}
toggleAssetSelected={toggleAssetSelected}
addAssetLayer={addAssetLayer}
toggleAllAssetsSelected={toggleAllAssetsSelected}
deleteSelectedAssets={deleteSelectedAssets}
closeAssetSelectionMode={closeAssetSelectionMode}
/>
) : (
<ImageCanvasLayerPanelView
layers={layers}
selectedLayerId={selectedLayerId}
selectedLayerIds={selectedLayerIds}
setImageContextMenu={setImageContextMenu}
setContextMenu={setContextMenu}
selectSingleLayer={selectSingleLayer}
resolveContextMenuPosition={resolveContextMenuPosition}
getCanvasPointFromClient={getCanvasPointFromClient}
/>
)}
</aside>
);
}