Files
supabase/apps/studio/components/ui/SchemaSelector.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

234 lines
8.0 KiB
TypeScript

import { PermissionAction } from '@supabase/shared-types/out/constants'
import { Check, ChevronsUpDown, Plus } from 'lucide-react'
import { ComponentPropsWithoutRef, forwardRef, useState } from 'react'
import {
Alert,
AlertDescription,
AlertTitle,
Button,
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
Popover,
PopoverContent,
PopoverTrigger,
ScrollArea,
Skeleton,
} from 'ui'
import { useSchemasQuery } from '@/data/database/schemas-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
type SchemaSelectorProps = Omit<ComponentPropsWithoutRef<'div'>, 'onSelect'> & {
disabled?: boolean
size?: 'tiny' | 'small'
showError?: boolean
selectedSchemaName?: string
placeholderLabel?: string
supportSelectAll?: boolean
excludedSchemas?: string[]
stopScrollPropagation?: boolean
onSelectSchema: (name: string) => void
onSelectCreateSchema?: () => void
align?: 'start' | 'end'
open?: boolean
onOpenChange?: (open: boolean) => void
}
export const SchemaSelector = forwardRef<HTMLDivElement, SchemaSelectorProps>(
(
{
className,
disabled = false,
size = 'tiny',
showError = true,
selectedSchemaName,
placeholderLabel = 'Choose a schema...',
supportSelectAll = false,
excludedSchemas = [],
stopScrollPropagation = false,
onSelectSchema,
onSelectCreateSchema,
align = 'start',
open: openProp,
onOpenChange,
...rest
},
ref
) => {
const [internalOpen, setInternalOpen] = useState(false)
const isControlled = openProp !== undefined
const open = isControlled ? openProp : internalOpen
const setOpen = (next: boolean) => {
if (!isControlled) setInternalOpen(next)
onOpenChange?.(next)
}
const { can: canCreateSchemas } = useAsyncCheckPermissions(
PermissionAction.TENANT_SQL_ADMIN_WRITE,
'schemas'
)
const { data: project } = useSelectedProjectQuery()
const {
data,
isPending: isSchemasLoading,
isSuccess: isSchemasSuccess,
isError: isSchemasError,
error: schemasError,
refetch: refetchSchemas,
} = useSchemasQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
})
const schemas = (data || [])
.filter((schema) => !excludedSchemas.includes(schema.name))
.sort((a, b) => a.name.localeCompare(b.name))
return (
<div ref={ref} className={className} {...rest}>
{isSchemasLoading && (
<Button
type="default"
key="schema-selector-skeleton"
className="w-full [&>span]:w-full"
size={size}
disabled
>
<Skeleton className="w-full h-3 bg-foreground-muted" />
</Button>
)}
{showError && isSchemasError && (
<Alert variant="warning" className="px-3! py-3!">
<AlertTitle className="text-xs text-amber-900">Failed to load schemas</AlertTitle>
<AlertDescription className="text-xs mb-2 wrap-break-word">
Error: {(schemasError as any)?.message}
</AlertDescription>
<Button type="default" size="tiny" onClick={() => refetchSchemas()}>
Reload schemas
</Button>
</Alert>
)}
{isSchemasSuccess && (
<Popover open={open} onOpenChange={setOpen} modal={false}>
<PopoverTrigger asChild>
<Button
size={size}
disabled={disabled}
type="default"
data-testid="schema-selector"
className={`w-full [&>span]:w-full pr-1! space-x-1`}
iconRight={
<ChevronsUpDown className="text-foreground-muted" strokeWidth={2} size={14} />
}
>
{selectedSchemaName ? (
<div className="w-full flex gap-1">
<p className="text-foreground-lighter">schema</p>
<p className="text-foreground">
{selectedSchemaName === '*' ? 'All schemas' : selectedSchemaName}
</p>
</div>
) : (
<div className="w-full flex gap-1">
<p className="text-foreground-lighter">{placeholderLabel}</p>
</div>
)}
</Button>
</PopoverTrigger>
<PopoverContent
className="p-0 min-w-[200px] pointer-events-auto"
side="bottom"
align={align}
sameWidthAsTrigger
>
<Command>
<CommandInput className="text-xs" placeholder="Find schema..." />
<CommandList
onWheel={stopScrollPropagation ? (event) => event.stopPropagation() : undefined}
>
<CommandEmpty>No schemas found</CommandEmpty>
<CommandGroup>
<ScrollArea className={(schemas || []).length > 7 ? 'h-[210px]' : ''}>
{supportSelectAll && (
<CommandItem
key="select-all"
className="cursor-pointer flex items-center justify-between space-x-2 w-full"
onSelect={() => {
onSelectSchema('*')
setOpen(false)
}}
onClick={() => {
onSelectSchema('*')
setOpen(false)
}}
>
<span>All schemas</span>
{selectedSchemaName === '*' && (
<Check className="text-brand" strokeWidth={2} size={16} />
)}
</CommandItem>
)}
{schemas.map((schema) => (
<CommandItem
key={schema.id}
className="cursor-pointer flex items-center justify-between space-x-2 w-full"
onSelect={() => {
onSelectSchema(schema.name)
setOpen(false)
}}
onClick={() => {
onSelectSchema(schema.name)
setOpen(false)
}}
>
<span>{schema.name}</span>
{selectedSchemaName === schema.name && (
<Check className="text-brand" strokeWidth={2} size={16} />
)}
</CommandItem>
))}
</ScrollArea>
</CommandGroup>
{onSelectCreateSchema !== undefined && canCreateSchemas && (
<>
<CommandSeparator />
<CommandGroup>
<CommandItem
className="cursor-pointer flex items-center gap-x-2 w-full"
onSelect={() => {
onSelectCreateSchema()
setOpen(false)
}}
onClick={() => {
onSelectCreateSchema()
setOpen(false)
}}
>
<Plus size={12} />
Create a new schema
</CommandItem>
</CommandGroup>
</>
)}
</CommandList>
</Command>
</PopoverContent>
</Popover>
)}
</div>
)
}
)
SchemaSelector.displayName = 'SchemaSelector'
export default SchemaSelector