import { useSortable } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' import { AnimatePresence, motion } from 'framer-motion' import { X } from 'lucide-react' import { useMemo } from 'react' import { cn, TabsTrigger_Shadcn_ } from 'ui' import { useEditorType } from '../editors/EditorsLayout.hooks' import { EntityTypeIcon } from '@/components/ui/EntityTypeIcon' import { useQuerySchemaState } from '@/hooks/misc/useSchemaQueryState' import { useTabsStateSnapshot, type Tab } from '@/state/tabs' /** * Individual draggable tab component that handles: * - Drag * - Drop functionality * - Dynamic schema name display * - Tab label animations * - Close button interactions */ export const SortableTab = ({ tab, index, openTabs, onClose, }: { tab: Tab index: number openTabs: Tab[] onClose: (id: string) => void }) => { const editor = useEditorType() const tabs = useTabsStateSnapshot() const { selectedSchema: currentSchema } = useQuerySchemaState() const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: tab.id, }) const style = { transform: CSS.Transform.toString(transform), transition, zIndex: isDragging ? 1 : 0, } // Update schema visibility check to include URL param comparison const shouldShowSchema = useMemo(() => { // For both table and schema tabs, show schema if: // Any tab has a different schema than the current schema parameter return openTabs.some((t) => editor === 'table' && t.metadata?.schema !== currentSchema) }, [openTabs, currentSchema, editor]) // Create a motion version of TabsTrigger while preserving all functionality // const MotionTabsTrigger = motion(TabsTrigger_Shadcn_) return ( { // Middle click closes tab if (e.button === 1) { e.preventDefault() onClose(tab.id) } }} onDoubleClick={() => tabs.makeTabPermanent(tab.id)} className={cn( 'flex items-center gap-2 pl-3 pr-2.5 text-xs', 'bg-dash-sidebar/50 dark:bg-surface-100/50', 'data-[state=active]:bg-dash-sidebar dark:data-[state=active]:bg-surface-100', 'border-b border-default', 'data-[state=active]:border-b-background-dash-sidebar dark:data-[state=active]:border-b-background-surface-100', 'relative group h-full', 'hover:bg-surface-300 dark:hover:bg-surface-100', tab.isPreview && 'italic font-light' // Optional: style preview tabs differently )} {...listeners} >
{shouldShowSchema && ( {tab?.metadata?.schema}. )} {tab.label || 'Untitled'}
{ e.preventDefault() e.stopPropagation() }} className="p-0.5 ml-1 opacity-0 group-hover:opacity-100 hover:bg-200 rounded-xs cursor-pointer" onMouseDown={(e) => { e.preventDefault() e.stopPropagation() }} onPointerDown={(e) => { e.preventDefault() e.stopPropagation() onClose(tab.id) }} >
{index < openTabs.length && (
)} ) }