import { CheckSquare, FolderPlus, Square } from 'lucide-react'; import type { Dispatch, PointerEvent as ReactPointerEvent, RefObject, SetStateAction, } from 'react'; import { useMemo, useState } from 'react'; import { PlatformTextField } from '../common/PlatformTextField'; import { filterGroupedAssetsBySearch, layerMatchesSearch, layerStartsWithSearch, } from './ImageCanvasAssetLibraryModel'; import { type GroupedEditorAssetFolder, ImageCanvasAssetLibraryPanelView, type UploadFilesOptions, type UploadRequestOptions, } from './ImageCanvasAssetLibraryPanelView'; import { EditorIconButton } from './ImageCanvasEditorPrimitives'; import type { AssetMarqueeState, AssetPointerDragState, CanvasContextMenuState, CanvasLayer, EditorAsset, EditorAssetFolder, ImageContextMenuState, SidebarPanel, UploadTarget, } from './ImageCanvasEditorTypes'; import { ImageCanvasLayerPanelView } from './ImageCanvasLayerPanelView'; import { type AssetSelectionInteraction, getSelectableAssets, } from './useImageCanvasAssetSelection'; export type { GroupedEditorAssetFolder, UploadFilesOptions }; export type ImageCanvasSidebarViewProps = { activeSidebarPanel: SidebarPanel | null; assetListRef: RefObject; assetPointerDragRef: { current: AssetPointerDragState | null }; suppressAssetClickRef: { current: boolean }; assets: EditorAsset[]; groupedAssets: GroupedEditorAssetFolder[]; assetFolders: EditorAssetFolder[]; layers: CanvasLayer[]; selectedLayerId: string | null; selectedLayerIds: string[]; isAssetSelectionMode: boolean; selectedAssetIds: Set; assetMoveDropFolderId: string | null; pinnedAssetMoveFolderId: string | null; creatingFolder: boolean; newFolderName: string; renamingFolder: { folderId: string; value: string } | null; renamingAsset: { assetId: string; value: string } | null; isExportingSelectedAssets: boolean; assetMarquee: AssetMarqueeState | null; setCreatingFolder: Dispatch>; setNewFolderName: Dispatch>; setRenamingFolder: Dispatch< SetStateAction<{ folderId: string; value: string } | null> >; setRenamingAsset: Dispatch< SetStateAction<{ assetId: string; value: string } | null> >; setActiveUploadFolderId: Dispatch>; setUploadDropTarget: Dispatch>; setAssetPointerDrag: Dispatch>; setImageContextMenu: Dispatch>; setContextMenu: Dispatch>; onAssetMarqueePointerDown: (event: ReactPointerEvent) => void; onAssetMarqueePointerMove: (event: ReactPointerEvent) => void; onAssetMarqueePointerUp: (event: ReactPointerEvent) => void; onAssetMarqueePointerCancel: ( event: ReactPointerEvent, ) => void; onAssetMarqueeLostPointerCapture: ( event: ReactPointerEvent, ) => void; updateAssetMoveDropFolder: (folderId: string | null) => void; addUploadedFiles: ( files: FileList | File[], options?: UploadFilesOptions, ) => void; requestUpload: (target: UploadTarget, options?: UploadRequestOptions) => void; moveAssetToFolder: (assetId: string, folderId: string) => void; commitNewAssetFolder: () => void | Promise; 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; submitAssetToShowcase: (asset: EditorAsset) => void; toggleAssetSelected: ( assetId: string, interaction: AssetSelectionInteraction, ) => void; addAssetLayer: (asset: EditorAsset) => void; onDownloadAsset: (asset: EditorAsset) => void; toggleAllAssetsSelected: (visibleAssetIds: string[]) => void; downloadSelectedAssets: () => void | Promise; deleteSelectedAssets: (assetIds?: ReadonlySet) => void; openAssetSelectionMode: () => void; closeAssetSelectionMode: () => void; groupSelectedLayers: () => void; selectSingleLayer: (layerId: string | null) => void; resolveContextMenuPosition: ( clientX: number, clientY: number, menuKind: 'blank' | 'layer', ) => Omit; getCanvasPointFromClient: ( clientX: number, clientY: number, ) => { x: number; y: number }; }; export function ImageCanvasSidebarView({ activeSidebarPanel, assetListRef, assetPointerDragRef, suppressAssetClickRef, groupedAssets, assetFolders, layers, selectedLayerId, selectedLayerIds, isAssetSelectionMode, selectedAssetIds, assetMoveDropFolderId, pinnedAssetMoveFolderId, creatingFolder, newFolderName, renamingFolder, renamingAsset, isExportingSelectedAssets, assetMarquee, setCreatingFolder, setNewFolderName, setRenamingFolder, setRenamingAsset, setActiveUploadFolderId, setUploadDropTarget, setAssetPointerDrag, setImageContextMenu, setContextMenu, onAssetMarqueePointerDown, onAssetMarqueePointerMove, onAssetMarqueePointerUp, onAssetMarqueePointerCancel, onAssetMarqueeLostPointerCapture, updateAssetMoveDropFolder, addUploadedFiles, requestUpload, moveAssetToFolder, commitNewAssetFolder, toggleAssetFolder, startRenamingFolder, commitFolderRename, deleteAssetFolder, startRenamingAsset, commitAssetRename, deleteUploadedAsset, submitAssetToShowcase, toggleAssetSelected, addAssetLayer, onDownloadAsset, toggleAllAssetsSelected, downloadSelectedAssets, deleteSelectedAssets, openAssetSelectionMode, closeAssetSelectionMode, groupSelectedLayers, selectSingleLayer, resolveContextMenuPosition, getCanvasPointFromClient, }: ImageCanvasSidebarViewProps) { const [assetSearchQuery, setAssetSearchQuery] = useState(''); const [layerSearchQuery, setLayerSearchQuery] = useState(''); const filteredGroupedAssets = useMemo( () => filterGroupedAssetsBySearch(groupedAssets, assetSearchQuery), [assetSearchQuery, groupedAssets], ); const filteredAssetsCount = useMemo( () => filteredGroupedAssets.reduce( (totalCount, folder) => totalCount + folder.assets.length, 0, ), [filteredGroupedAssets], ); const visibleAssetIds = useMemo( () => filteredGroupedAssets.flatMap((folder) => folder.collapsed ? [] : getSelectableAssets(folder.assets).map((asset) => asset.id), ), [filteredGroupedAssets], ); const allVisibleAssetsSelected = visibleAssetIds.length > 0 && visibleAssetIds.every((assetId) => selectedAssetIds.has(assetId)); const normalizedLayerSearchQuery = layerSearchQuery.trim().toLowerCase(); const filteredLayers = useMemo( () => normalizedLayerSearchQuery ? [ ...layers.filter((layer) => layerStartsWithSearch(layer, normalizedLayerSearchQuery), ), ...layers.filter( (layer) => !layerStartsWithSearch(layer, normalizedLayerSearchQuery) && layerMatchesSearch(layer, normalizedLayerSearchQuery), ), ] : layers, [layers, normalizedLayerSearchQuery], ); if (!activeSidebarPanel) { return null; } return ( ); }