From ba941e971256eaca07371bbc78221cbc8573044e Mon Sep 17 00:00:00 2001 From: Francesco Sansalvadore Date: Wed, 8 Apr 2026 17:27:24 +0200 Subject: [PATCH] fix merge conflicts --- .../studio/.github/eslint-rule-baselines.json | 1 - .../interfaces/Storage/Storage.constants.ts | 6 - .../Storage/StorageExplorer/FileExplorer.tsx | 9 +- .../StorageExplorer/FileExplorerColumn.tsx | 358 +++++++++++------- .../StorageExplorer/FileExplorerRow.tsx | 21 +- .../StorageExplorer/ItemContextMenu.tsx | 117 ------ 6 files changed, 235 insertions(+), 277 deletions(-) delete mode 100644 apps/studio/components/interfaces/Storage/StorageExplorer/ItemContextMenu.tsx diff --git a/apps/studio/.github/eslint-rule-baselines.json b/apps/studio/.github/eslint-rule-baselines.json index c74dc59c540..3e0fd9338ba 100644 --- a/apps/studio/.github/eslint-rule-baselines.json +++ b/apps/studio/.github/eslint-rule-baselines.json @@ -441,7 +441,6 @@ "components/interfaces/Storage/StorageExplorer/FileExplorerColumn.tsx": 6, "components/interfaces/Storage/StorageExplorer/FileExplorerRow.tsx": 1, "components/interfaces/Storage/StorageExplorer/FileExplorerRowEditing.tsx": 2, - "components/interfaces/Storage/StorageExplorer/ItemContextMenu.tsx": 1, "components/interfaces/Storage/StorageExplorer/MoveItemsModal.tsx": 1, "components/interfaces/Storage/StorageExplorer/StorageExplorer.tsx": 1, "components/interfaces/Storage/StorageExplorer/StorageExplorer.utils.tsx": 2, diff --git a/apps/studio/components/interfaces/Storage/Storage.constants.ts b/apps/studio/components/interfaces/Storage/Storage.constants.ts index 77d33166332..c6998dfbd1b 100644 --- a/apps/studio/components/interfaces/Storage/Storage.constants.ts +++ b/apps/studio/components/interfaces/Storage/Storage.constants.ts @@ -41,12 +41,6 @@ export enum STORAGE_ROW_STATUS { EDITING = 'EDITING', } -export const CONTEXT_MENU_KEYS = { - STORAGE_COLUMN: 'STORAGE_COLUMN', - STORAGE_ITEM: 'STORAGE_ITEM', - STORAGE_FOLDER: 'STORAGE_FOLDER', -} - export const STORAGE_CLIENT_LIBRARY_MAPPINGS = { upload: ['INSERT'], download: ['SELECT'], diff --git a/apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorer.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorer.tsx index 43ea6874cec..dd30eb90007 100644 --- a/apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorer.tsx +++ b/apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorer.tsx @@ -2,12 +2,9 @@ import { noop } from 'lodash' import { useEffect, useRef } from 'react' import { cn } from 'ui' -import { CONTEXT_MENU_KEYS, STORAGE_ROW_STATUS, STORAGE_VIEWS } from '../Storage.constants' +import { STORAGE_ROW_STATUS, STORAGE_VIEWS } from '../Storage.constants' import type { StorageColumn, StorageItemWithColumn } from '../Storage.types' -import { ColumnContextMenu } from './ColumnContextMenu' import { FileExplorerColumn } from './FileExplorerColumn' -import { FolderContextMenu } from './FolderContextMenu' -import { ItemContextMenu } from './ItemContextMenu' import { useStorageExplorerStateSnapshot } from '@/state/storage-explorer' export interface FileExplorerProps { @@ -52,10 +49,6 @@ export const FileExplorer = ({ snap.view === STORAGE_VIEWS.COLUMNS && 'h-full' )} > - - - - {isLoading ? ( item.status !== STORAGE_ROW_STATUS.LOADING).length === 0 - const { show } = useContextMenu() - const displayMenu = (event: any) => { - show(event, { - id: CONTEXT_MENU_KEYS.STORAGE_COLUMN, - props: { index }, - }) + const onSelectCreateFolder = () => { + snap.addNewFolderPlaceholder(index) + } + + const onSelectAllItems = () => { + const colFiles = visibleColumnItems + .filter((item) => item.type === STORAGE_ROW_TYPES.FILE) + .map((item) => ({ ...item, columnIndex: index })) + const colFilesId = colFiles.map((item) => item.id).filter(Boolean) + const selectedFromCol = selectedItems.filter((item) => item.id && colFilesId.includes(item.id)) + + if (selectedFromCol.length === colFiles.length) { + snap.setSelectedItems( + selectedItems.filter((item) => item.id && !colFilesId.includes(item.id)) + ) + } else { + snap.setSelectedItems(uniqBy(selectedItems.concat(colFiles), 'id')) + } } const onDragOver = (event: any) => { @@ -171,133 +195,213 @@ export const FileExplorerColumn = ({ ) return ( -
{ - const eventTarget = get(event.target, ['className'], '') - if (typeof eventTarget === 'string' && eventTarget.includes('react-contexify')) return - onSelectColumnEmptySpace(index) - }} - > - {/* Checkbox selection for select all */} - {snap.view === STORAGE_VIEWS.COLUMNS && ( + +
event.stopPropagation()} + onDragOver={onDragOver} + onDrop={onDrop} + onClick={() => { + onSelectColumnEmptySpace(index) + }} > - {columnFiles.length > 0 ? ( - <> - -

Select all {columnFiles.length} files

- - ) : ( -

No files available for selection

+ {/* Checkbox selection for select all */} + {snap.view === STORAGE_VIEWS.COLUMNS && ( +
event.stopPropagation()} + > + {columnFiles.length > 0 ? ( + <> + +

+ Select all {columnFiles.length} files +

+ + ) : ( +

No files available for selection

+ )} +
)} -
- )} - {/* List Interface Header */} - {snap.view === STORAGE_VIEWS.LIST && ( -
-
- -

Name

-
-

Size

-

Type

-

Created at

-

Last modified at

-
- )} + {/* List Interface Header */} + {snap.view === STORAGE_VIEWS.LIST && ( +
+
+ +

Name

+
+

Size

+

Type

+

Created at

+

Last modified at

+
+ )} - {/* Shimmering loaders while fetching contents */} - {column.status === STORAGE_ROW_STATUS.LOADING && ( -
- - - + > + + + +
+ )} + + {/* Column Interface */} + {columnItems.length > 0 && ( + + + index !== 0 && index === columnItems.length ? 85 : 37 + } + ItemComponent={FileExplorerRow} + LoaderComponent={LoaderForIconMenuItems} + hasNextPage={column.status !== STORAGE_ROW_STATUS.LOADING && column.hasMoreItems} + isLoadingNextPage={column.isLoadingMoreItems} + onLoadNextPage={() => onColumnLoadMore(index, column)} + /> + + )} + + {/* Drag drop upload CTA for when column is empty */} + {!(snap.isSearching && itemSearchString.length > 0) && + visibleColumnItems.length === 0 && + column.status !== STORAGE_ROW_STATUS.LOADING && ( +
+ storage-placeholder +

Drop your files here

+

+ Or upload them via the "Upload file" button above +

+
+ )} + + {snap.isSearching && + itemSearchString.length > 0 && + isEmpty && + column.status !== STORAGE_ROW_STATUS.LOADING && ( +
+

No results found in this folder

+

+ Your search for "{itemSearchString}" did not return any results +

+
+ )} + + {/* Drag drop upload CTA for when column has files */} + setIsDraggedOver(false)} + onDrop={() => setIsDraggedOver(false)} + /> + + {/* List interface footer */} + {snap.view === STORAGE_VIEWS.LIST && ( +
+

+ {formatBytes(columnItemsSize)} for {columnItems.length} items +

+
+ )}
- )} - - {/* Column Interface */} - {columnItems.length > 0 && ( - (index !== 0 && index === columnItems.length ? 85 : 37)} - ItemComponent={FileExplorerRow} - LoaderComponent={LoaderForIconMenuItems} - hasNextPage={column.status !== STORAGE_ROW_STATUS.LOADING && column.hasMoreItems} - isLoadingNextPage={column.isLoadingMoreItems} - onLoadNextPage={() => onColumnLoadMore(index, column)} - /> - )} - - {/* Drag drop upload CTA for when column is empty */} - {!(snap.isSearching && itemSearchString.length > 0) && - visibleColumnItems.length === 0 && - column.status !== STORAGE_ROW_STATUS.LOADING && ( -
- storage-placeholder -

Drop your files here

-

- Or upload them via the "Upload file" button above -

-
+ + + {canUpdateStorage && ( + <> + + + New folder + + + )} - - {snap.isSearching && - itemSearchString.length > 0 && - isEmpty && - column.status !== STORAGE_ROW_STATUS.LOADING && ( -
-

No results found in this folder

-

- Your search for "{itemSearchString}" did not return any results -

-
- )} - - {/* Drag drop upload CTA for when column has files */} - setIsDraggedOver(false)} - onDrop={() => setIsDraggedOver(false)} - /> - - {/* List interface footer */} - {snap.view === STORAGE_VIEWS.LIST && ( -
-

- {formatBytes(columnItemsSize)} for {columnItems.length} items -

-
- )} - + + + Select all items + + + + + View + + + snap.setView(STORAGE_VIEWS.COLUMNS)}> + As columns + + snap.setView(STORAGE_VIEWS.LIST)}> + As list + + + + + + + Sort by + + + void snap.setSortBy(STORAGE_SORT_BY.NAME)}> + Name + + void snap.setSortBy(STORAGE_SORT_BY.CREATED_AT)} + > + Last created + + void snap.setSortBy(STORAGE_SORT_BY.UPDATED_AT)} + > + Last modified + + void snap.setSortBy(STORAGE_SORT_BY.LAST_ACCESSED_AT)} + > + Last accessed + + + + + + + Sort by order + + + void snap.setSortByOrder(STORAGE_SORT_BY_ORDER.ASC)} + > + Ascending + + void snap.setSortByOrder(STORAGE_SORT_BY_ORDER.DESC)} + > + Descending + + + +
+ ) } diff --git a/apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerRow.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerRow.tsx index 828aba47cfd..daef28642b8 100644 --- a/apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerRow.tsx +++ b/apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerRow.tsx @@ -17,7 +17,6 @@ import { Trash2, } from 'lucide-react' import type { CSSProperties } from 'react' -import { useContextMenu } from 'react-contexify' import { Checkbox, cn, @@ -36,13 +35,13 @@ import { } from 'ui' import { - CONTEXT_MENU_KEYS, STORAGE_ROW_STATUS, STORAGE_ROW_TYPES, STORAGE_VIEWS, URL_EXPIRY_DURATION, } from '../Storage.constants' import { StorageItemWithColumn, type StorageItem } from '../Storage.types' +import { useFileExplorerContextMenu } from './FileExplorerRowContextMenu' import { FileExplorerRowEditing } from './FileExplorerRowEditing' import { copyPathToFolder } from './StorageExplorer.utils' import { isPickerItemSelectable, useStorageExplorerPicker } from './StorageExplorerPickerContext' @@ -129,7 +128,7 @@ export const FileExplorerRow = ({ selectRangeItems, } = useStorageExplorerStateSnapshot() const picker = useStorageExplorerPicker() - const { show } = useContextMenu() + const ctx = useFileExplorerContextMenu() const { onCopyUrl } = useCopyUrl() const isPublic = selectedBucket.public @@ -275,18 +274,6 @@ export const FileExplorerRow = ({ const createdAt = item.created_at ? new Date(item.created_at).toLocaleString() : '-' const updatedAt = item.updated_at ? new Date(item.updated_at).toLocaleString() : '-' - const displayMenu = (event: any, rowType: STORAGE_ROW_TYPES) => { - show(event, { - id: - rowType === STORAGE_ROW_TYPES.FILE - ? CONTEXT_MENU_KEYS.STORAGE_ITEM - : CONTEXT_MENU_KEYS.STORAGE_FOLDER, - props: { - item: itemWithColumnIndex, - }, - }) - } - const nameWidth = view === STORAGE_VIEWS.LIST && item.isCorrupted ? `calc(100% - 60px)` @@ -307,9 +294,7 @@ export const FileExplorerRow = ({ onContextMenu={(event) => { event.preventDefault() event.stopPropagation() - item.type === STORAGE_ROW_TYPES.FILE - ? displayMenu(event, STORAGE_ROW_TYPES.FILE) - : displayMenu(event, STORAGE_ROW_TYPES.FOLDER) + ctx?.onRowContextMenu(event, rowOptions) }} >
{ - const { - selectedBucket, - setSelectedItemsToDelete, - setSelectedItemToRename, - setSelectedItemsToMove, - setSelectedFileCustomExpiry, - downloadFile, - } = useStorageExplorerStateSnapshot() - const { onCopyUrl } = useCopyUrl() - const isPublic = selectedBucket.public - const { can: canUpdateFiles } = useAsyncCheckPermissions(PermissionAction.STORAGE_WRITE, '*') - - const onHandleClick = async ( - event: 'copy' | 'rename' | 'move' | 'download', - item: StorageItemWithColumn, - expiresIn?: number - ) => { - if (item.isCorrupted) return - if (event === 'copy') { - if (expiresIn !== undefined && expiresIn < 0) { - setSelectedFileCustomExpiry(item) - return - } - if (item.path) { - onCopyUrl(item.path, expiresIn) - } - return - } - if (event === 'rename') { - setSelectedItemToRename(item) - return - } - if (event === 'move') { - setSelectedItemsToMove([item]) - return - } - if (event === 'download') { - await downloadFile(item) - } - } - - return ( - - {isPublic ? ( - onHandleClick('copy', props.item)}> - - Get URL - - ) : ( - - - Get URL -
- } - arrow={} - > - onHandleClick('copy', props.item, URL_EXPIRY_DURATION.WEEK)} - > - Expire in 1 week - - onHandleClick('copy', props.item, URL_EXPIRY_DURATION.MONTH)} - > - Expire in 1 month - - onHandleClick('copy', props.item, URL_EXPIRY_DURATION.YEAR)} - > - Expire in 1 year - - onHandleClick('copy', props.item, -1)}> - Custom expiry - - - )} - onHandleClick('download', props.item)}> - - Download - - {canUpdateFiles && [ - onHandleClick('rename', props.item)}> - - Rename - , - onHandleClick('move', props.item)}> - - Move - , - , - setSelectedItemsToDelete([props.item])}> - - Delete - , - ]} - - ) -}