Fix storage explorer footer (#35036)

* Fix storage explorer footer

* Fix

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
Terry SuttonandJoshen Lim authored and GitHub committed 2025-04-25 18:26:12 +07:00
1 parent 98ae279a2b
commit a8f193cc3c
3 files changed
+119 -127

No files matched your search

@@ -1,7 +1,8 @@
import { noop } from 'lodash'
import { observer } from 'mobx-react-lite'
import { useEffect, useRef } from 'react'
import { noop } from 'lodash'
import { cn } from 'ui'
import { CONTEXT_MENU_KEYS, STORAGE_VIEWS } from '../Storage.constants'
import type { StorageColumn, StorageItem, StorageItemWithColumn } from '../Storage.types'
import ColumnContextMenu from './ColumnContextMenu'
@@ -46,7 +47,10 @@ const FileExplorer = ({
return (
<div
ref={fileExplorerRef}
className="file-explorer flex flex-grow overflow-x-auto justify-between h-full w-full relative"
className={cn(
'file-explorer flex flex-grow overflow-x-auto justify-between h-full w-full relative',
view === STORAGE_VIEWS.LIST && 'flex-col'
)}
>
<ColumnContextMenu id={CONTEXT_MENU_KEYS.STORAGE_COLUMN} />
<ItemContextMenu id={CONTEXT_MENU_KEYS.STORAGE_ITEM} />
@@ -1,15 +1,15 @@
import { Transition } from '@headlessui/react'
import { get, noop, sum } from 'lodash'
import { Upload } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'
import { useContextMenu } from 'react-contexify'
import { Checkbox } from 'ui'
import InfiniteList from 'components/ui/InfiniteList'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { BASE_PATH } from 'lib/constants'
import { formatBytes } from 'lib/helpers'
import { Upload } from 'lucide-react'
import { useStorageExplorerStateSnapshot } from 'state/storage-explorer'
import { Checkbox, cn } from 'ui'
import {
CONTEXT_MENU_KEYS,
STORAGE_ROW_STATUS,
@@ -142,145 +142,133 @@ const FileExplorerColumn = ({
)
return (
<>
<div
ref={fileExplorerColumnRef}
className={`
${fullWidth ? 'w-full' : 'w-64 border-r border-overlay'}
${view === STORAGE_VIEWS.COLUMNS ? '' : ''}
hide-scrollbar relative flex flex-shrink-0 flex-col overflow-auto
`}
onContextMenu={displayMenu}
onDragOver={onDragOver}
onDrop={onDrop}
onClick={(event) => {
const eventTarget = get(event.target, ['className'], '')
if (typeof eventTarget === 'string' && eventTarget.includes('react-contexify')) return
onSelectColumnEmptySpace(index)
}}
>
{/* Checkbox selection for select all */}
{view === STORAGE_VIEWS.COLUMNS && (
<div
className={`sticky top-0 z-10 mb-0 flex items-center bg-table-header-light px-2.5 [[data-theme*=dark]_&]:bg-table-header-dark ${
haveSelectedItems ? 'h-10 py-3 opacity-100' : 'h-0 py-0 opacity-0'
} transition-all duration-200`}
onClick={(event) => event.stopPropagation()}
>
{columnFiles.length > 0 ? (
<>
<SelectAllCheckbox />
<p className="text-sm text-foreground-light">
Select all {columnFiles.length} files
</p>
</>
) : (
<p className="text-sm text-foreground-light">No files available for selection</p>
)}
</div>
)}
{/* List Interface Header */}
{view === STORAGE_VIEWS.LIST && (
<div
className="
sticky top-0
z-10 flex min-w-min items-center border-b border-overlay bg-surface-100 px-2.5
py-2
"
>
<div className="flex w-[40%] min-w-[250px] items-center">
<div
ref={fileExplorerColumnRef}
className={cn(
fullWidth ? 'w-full' : 'w-64 border-r border-overlay',
view === STORAGE_VIEWS.LIST && 'h-full',
'hide-scrollbar relative flex flex-shrink-0 flex-col overflow-auto'
)}
onContextMenu={displayMenu}
onDragOver={onDragOver}
onDrop={onDrop}
onClick={(event) => {
const eventTarget = get(event.target, ['className'], '')
if (typeof eventTarget === 'string' && eventTarget.includes('react-contexify')) return
onSelectColumnEmptySpace(index)
}}
>
{/* Checkbox selection for select all */}
{view === STORAGE_VIEWS.COLUMNS && (
<div
className={cn(
'sticky top-0 z-10 mb-0 flex items-center bg-table-header-light px-2.5 [[data-theme*=dark]_&]:bg-table-header-dark',
haveSelectedItems ? 'h-10 py-3 opacity-100' : 'h-0 py-0 opacity-0',
'transition-all duration-200'
)}
onClick={(event) => event.stopPropagation()}
>
{columnFiles.length > 0 ? (
<>
<SelectAllCheckbox />
<p className="text-sm">Name</p>
</div>
<p className="w-[11%] min-w-[100px] text-sm">Size</p>
<p className="w-[14%] min-w-[100px] text-sm">Type</p>
<p className="w-[15%] min-w-[160px] text-sm">Created at</p>
<p className="w-[15%] min-w-[160px] text-sm">Last modified at</p>
</div>
)}
<p className="text-sm text-foreground-light">Select all {columnFiles.length} files</p>
</>
) : (
<p className="text-sm text-foreground-light">No files available for selection</p>
)}
</div>
)}
{/* Shimmering loaders while fetching contents */}
{column.status === STORAGE_ROW_STATUS.LOADING && (
<div
className={`
{/* List Interface Header */}
{view === STORAGE_VIEWS.LIST && (
<div className="sticky top-0 py-2 z-10 flex min-w-min items-center border-b border-overlay bg-surface-100 px-2.5">
<div className="flex w-[40%] min-w-[250px] items-center">
<SelectAllCheckbox />
<p className="text-sm">Name</p>
</div>
<p className="w-[11%] min-w-[100px] text-sm">Size</p>
<p className="w-[14%] min-w-[100px] text-sm">Type</p>
<p className="w-[15%] min-w-[160px] text-sm">Created at</p>
<p className="w-[15%] min-w-[160px] text-sm">Last modified at</p>
</div>
)}
{/* Shimmering loaders while fetching contents */}
{column.status === STORAGE_ROW_STATUS.LOADING && (
<div
className={`
${fullWidth ? 'w-full' : 'w-64 border-r border-default'}
px-2 py-1 my-1 flex flex-shrink-0 flex-col space-y-2 overflow-auto
`}
>
<ShimmeringLoader />
<ShimmeringLoader />
<ShimmeringLoader />
>
<ShimmeringLoader />
<ShimmeringLoader />
<ShimmeringLoader />
</div>
)}
{/* Column Interface */}
<InfiniteList
items={columnItems}
itemProps={{
view,
columnIndex: index,
selectedItems,
openedFolders,
}}
ItemComponent={FileExplorerRow}
getItemSize={(index) => (index !== 0 && index === columnItems.length ? 85 : 37)}
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) &&
column.items.length === 0 &&
column.status !== STORAGE_ROW_STATUS.LOADING && (
<div className="h-full w-full flex flex-col items-center justify-center">
<img
alt="storage-placeholder"
src={`${BASE_PATH}/img/storage-placeholder.svg`}
className="opacity-75 pointer-events-none"
/>
<p className="text-sm my-3 opacity-75">Drop your files here</p>
<p className="w-40 text-center text-xs text-foreground-light">
Or upload them via the "Upload file" button above
</p>
</div>
)}
{/* Column Interface */}
<InfiniteList
items={columnItems}
itemProps={{
view,
columnIndex: index,
selectedItems,
openedFolders,
}}
ItemComponent={FileExplorerRow}
getItemSize={(index) => (index !== 0 && index === columnItems.length ? 85 : 37)}
hasNextPage={column.status !== STORAGE_ROW_STATUS.LOADING && column.hasMoreItems}
isLoadingNextPage={column.isLoadingMoreItems}
onLoadNextPage={() => onColumnLoadMore(index, column)}
/>
{snap.isSearching &&
itemSearchString.length > 0 &&
isEmpty &&
column.status !== STORAGE_ROW_STATUS.LOADING && (
<div className="h-full w-full flex flex-col items-center justify-center">
<p className="text-sm my-3 text-foreground">No results found in this folder</p>
<p className="w-40 text-center text-sm text-foreground-light">
Your search for "{itemSearchString}" did not return any results
</p>
</div>
)}
{/* Drag drop upload CTA for when column is empty */}
{!(snap.isSearching && itemSearchString.length > 0) &&
column.items.length === 0 &&
column.status !== STORAGE_ROW_STATUS.LOADING && (
<div className="h-full w-full flex flex-col items-center justify-center">
<img
alt="storage-placeholder"
src={`${BASE_PATH}/img/storage-placeholder.svg`}
className="opacity-75 pointer-events-none"
/>
<p className="text-sm my-3 opacity-75">Drop your files here</p>
<p className="w-40 text-center text-xs text-foreground-light">
Or upload them via the "Upload file" button above
</p>
</div>
)}
{/* Drag drop upload CTA for when column has files */}
<DragOverOverlay
isOpen={isDraggedOver}
folderIsEmpty={isEmpty}
onDragLeave={() => setIsDraggedOver(false)}
onDrop={() => setIsDraggedOver(false)}
/>
{snap.isSearching &&
itemSearchString.length > 0 &&
isEmpty &&
column.status !== STORAGE_ROW_STATUS.LOADING && (
<div className="h-full w-full flex flex-col items-center justify-center">
<p className="text-sm my-3 text-foreground">No results found in this folder</p>
<p className="w-40 text-center text-sm text-foreground-light">
Your search for "{itemSearchString}" did not return any results
</p>
</div>
)}
{/* Drag drop upload CTA for when column has files */}
<DragOverOverlay
isOpen={isDraggedOver}
folderIsEmpty={isEmpty}
onDragLeave={() => setIsDraggedOver(false)}
onDrop={() => setIsDraggedOver(false)}
/>
</div>
{/* List interface footer */}
{view === STORAGE_VIEWS.LIST && (
<div
className="
absolute bottom-0 rounded-b-md mt-auto
z-10 flex min-w-min items-center bg-panel-footer-light px-2.5 py-2 [[data-theme*=dark]_&]:bg-panel-footer-dark w-full
"
>
<div className="shrink-0 rounded-b-md z-10 flex min-w-min items-center bg-panel-footer-light px-2.5 py-2 [[data-theme*=dark]_&]:bg-panel-footer-dark w-full">
<p className="text-sm">
{formatBytes(columnItemsSize)} for {columnItems.length} items
</p>
</div>
)}
</>
</div>
)
}
@@ -1,8 +1,8 @@
import { useParams } from 'common'
import { compact, get, isEmpty, uniqBy } from 'lodash'
import { observer } from 'mobx-react-lite'
import { useEffect, useRef, useState } from 'react'
import { useParams } from 'common'
import { useProjectStorageConfigQuery } from 'data/config/project-storage-config-query'
import type { Bucket } from 'data/storage/buckets-query'
import { IS_PLATFORM } from 'lib/constants'