mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 09:55:06 +03:00
Fix storage explorer footer (#35036)
* Fix storage explorer footer * Fix --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
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} />
|
||||
|
||||
+112
-124
@@ -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'
|
||||
|
||||
Reference in new issue
Block a user