Files
supabase/apps/studio/components/interfaces/ProjectAPIDocs/SecondLevelNav.StoragePicker.tsx
Gildas Garcia 243e079a2c chore: remove _Shadcn_ suffix from Command components (#46153)
## Problem

The `_Shadcn_` suffix isn't needed anymore on `Command` components

## Solution

- Remove the `_Shadcn_` suffix
- Simplify UI package exports
- Apply prettier

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Simplified command component imports and exports across the UI library
by removing internal naming aliases and adopting direct component
references. Updated the public UI package barrel export to use wildcard
re-exports for cleaner API surface.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46153?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-20 15:45:32 +02:00

140 lines
4.2 KiB
TypeScript

import { keepPreviousData } from '@tanstack/react-query'
import { useDebounce, useIntersectionObserver } from '@uidotdev/usehooks'
import { useEffect, useMemo, useRef, useState } from 'react'
import { cn, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from 'ui'
import type { ResourcePickerRenderProps } from './SecondLevelNav.Layout'
import { usePaginatedBucketsQuery } from '@/data/storage/buckets-query'
type StorageResourceListProps = ResourcePickerRenderProps & {
projectRef?: string
}
const SEARCH_DEBOUNCE_MS = 400
const useSearchQuery = () => {
const [search, setSearch] = useState('')
const debouncedSearch = useDebounce(search, SEARCH_DEBOUNCE_MS)
const searchQuery = search.length === 0 ? undefined : debouncedSearch
return {
rawQuery: search,
query: searchQuery,
setSearch,
}
}
type UseInfiniteLoadingBucketsParams = {
projectRef?: string
searchQuery?: string
rawQuery: string
}
const useInfiniteLoadingBuckets = ({
projectRef,
searchQuery,
rawQuery,
}: UseInfiniteLoadingBucketsParams) => {
const { data, isFetching, hasNextPage, fetchNextPage, isFetchingNextPage } =
usePaginatedBucketsQuery(
{ projectRef, search: searchQuery },
{
enabled: !!projectRef,
placeholderData: rawQuery.length === 0 ? keepPreviousData : undefined,
}
)
const buckets = useMemo(() => data?.pages.flatMap((page) => page) ?? [], [data])
const scrollContainerRef = useRef<HTMLDivElement | null>(null)
const [sentinelRef, entry] = useIntersectionObserver({
threshold: 1,
root: scrollContainerRef.current,
rootMargin: '0px',
})
useEffect(() => {
if (entry?.isIntersecting && hasNextPage && !isFetching) {
fetchNextPage()
}
}, [entry?.isIntersecting, fetchNextPage, hasNextPage, isFetching])
return {
isFetching,
hasNextPage,
isFetchingNextPage,
buckets,
scrollContainerRef,
sentinelRef,
}
}
export const StorageResourceList = ({
projectRef,
selectedResource,
onSelect,
closePopover,
}: StorageResourceListProps) => {
const { rawQuery, query: searchQuery, setSearch } = useSearchQuery()
const { isFetching, hasNextPage, isFetchingNextPage, buckets, scrollContainerRef, sentinelRef } =
useInfiniteLoadingBuckets({
projectRef,
searchQuery,
rawQuery,
})
const handleSelect = (value: string) => {
onSelect(value)
closePopover()
}
const showEmptyState = !isFetching && buckets.length === 0
const emptyMessage =
rawQuery.length > 0 ? 'No buckets found for this search' : 'No buckets available'
return (
<Command shouldFilter={false}>
<CommandInput
showResetIcon
value={rawQuery}
onValueChange={setSearch}
placeholder="Search buckets..."
handleReset={() => setSearch('')}
/>
<CommandList>
<CommandEmpty hidden={!showEmptyState} className="py-3 text-sm text-foreground-light">
{emptyMessage}
</CommandEmpty>
<CommandGroup>
{isFetching && buckets.length === 0 ? (
<div className="px-4 py-3 text-sm text-foreground-light">Loading buckets...</div>
) : (
<div ref={scrollContainerRef} className="max-h-72 min-h-[150px] overflow-y-auto">
{buckets.map((bucket) => {
const isActive = bucket.name === selectedResource
return (
<CommandItem
key={bucket.id}
value={bucket.name}
className={cn(
'cursor-pointer px-4',
isActive ? 'text-foreground bg-selection' : 'text-foreground-light'
)}
onSelect={() => handleSelect(bucket.name)}
>
<p className="truncate">{bucket.name}</p>
</CommandItem>
)
})}
{hasNextPage && <div ref={sentinelRef} className="h-2 w-full" />}
</div>
)}
</CommandGroup>
{isFetchingNextPage && (
<div className="px-4 py-2 text-sm text-foreground-light">Loading more buckets...</div>
)}
</CommandList>
</Command>
)
}