diff --git a/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.tsx b/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.tsx index d084a7aeb62..ec966dbe214 100644 --- a/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.tsx +++ b/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.tsx @@ -1,7 +1,13 @@ -import { AnimatePresence } from 'framer-motion' +import { useParams } from 'common' +import { AnimatePresence, motion } from 'framer-motion' +import { Home, MessageCirclePlus, NotebookText, Plus } from 'lucide-react' +import Link from 'next/link' +import { useRouter } from 'next/router' import { ComponentProps, ReactNode, useState } from 'react' +import { cn, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from 'ui' import { ProjectLayoutWithAuth } from '../ProjectLayout' +import { EditorTabs } from '../Tabs/Tabs' import { type ExplorerResourceType } from './ExplorerLayout.constants' import { ExplorerNavChats } from './ExplorerNavChats' import { ExplorerNavHome } from './ExplorerNavHome' @@ -42,7 +48,70 @@ export const ExplorerLayout = ({ browserTitle, children, title }: ExplorerLayout } > - {children} +
+
+ } + newTabButton={} + /> +
+
{children}
+
) } + +const TabClassName = + 'flex items-center justify-center min-w-(--header-height) min-h-(--header-height) hover:bg-surface-100 shrink-0 border-b' + +const HomeTabButton = () => { + const router = useRouter() + const { ref } = useParams() + const isActive = router.pathname.endsWith('/explorer') + + return ( + + + Open Explorer home + + ) +} + +const NewTabButton = () => { + return ( + + + {}} + initial={{ opacity: 0, scale: 0.8, x: -10 }} + animate={{ opacity: 1, scale: 1, x: 0 }} + transition={{ duration: 0.2 }} + > + + + + + + + New notebook + + + + New chat + + + + ) +} diff --git a/apps/studio/components/layouts/Tabs/Tabs.tsx b/apps/studio/components/layouts/Tabs/Tabs.tsx index 9ee549892b3..73b0dd1c43a 100644 --- a/apps/studio/components/layouts/Tabs/Tabs.tsx +++ b/apps/studio/components/layouts/Tabs/Tabs.tsx @@ -11,7 +11,7 @@ import { useParams } from 'common' import { AnimatePresence, motion } from 'framer-motion' import { Plus, X } from 'lucide-react' import { useRouter } from 'next/router' -import { useState } from 'react' +import { ReactNode, useState } from 'react' import { cn, ContextMenu, @@ -37,7 +37,19 @@ import { type TabCloseConfirmation, } from '@/state/tabs' -export const EditorTabs = () => { +interface EditorTabsProps { + customTabs?: ReactNode + newTabButton?: ReactNode + isCollapseButtonHidden?: boolean +} + +// [Joshen] Will be adjusting this component to support Explorer +// Will require quite a bit of cleaning up once Explorer supercedes SQL Editor +export const EditorTabs = ({ + customTabs, + newTabButton, + isCollapseButtonHidden, +}: EditorTabsProps) => { const { ref, id } = useParams() const router = useRouter() const { setLastVisitedSnippet, setLastVisitedTable } = useDashboardHistory() @@ -175,7 +187,10 @@ export const EditorTabs = () => { value={hasNewTab ? 'new' : (tabs.activeTab ?? undefined)} onValueChange={handleTabChange} > - + {!isCollapseButtonHidden && } + + {customTabs} + { )} - {!hasNewTab && ( - - router.push( - `/project/${router.query.ref}/${editor === 'table' ? 'editor' : 'sql'}/new?skip=true` - ) - } - initial={{ opacity: 0, scale: 0.8, x: -10 }} - animate={{ opacity: 1, scale: 1, x: 0 }} - transition={{ duration: 0.2 }} - > - - - )} + {!hasNewTab && + (newTabButton ?? ( + + router.push( + `/project/${router.query.ref}/${editor === 'table' ? 'editor' : 'sql'}/new?skip=true` + ) + } + initial={{ opacity: 0, scale: 0.8, x: -10 }} + animate={{ opacity: 1, scale: 1, x: 0 }} + transition={{ duration: 0.2 }} + > + + + ))}
diff --git a/apps/studio/components/layouts/editors/EditorsLayout.hooks.ts b/apps/studio/components/layouts/editors/EditorsLayout.hooks.ts index 1495cc2b374..3b0f0cdb705 100644 --- a/apps/studio/components/layouts/editors/EditorsLayout.hooks.ts +++ b/apps/studio/components/layouts/editors/EditorsLayout.hooks.ts @@ -1,13 +1,15 @@ import { useParams } from 'common' import { usePathname } from 'next/navigation' -export function useEditorType(): 'table' | 'sql' | undefined { +export type EditorType = 'table' | 'sql' | 'explorer' | undefined + +export function useEditorType(): EditorType { const pathname = usePathname() const { ref } = useParams() - return pathname?.includes(`/project/${ref}/editor`) - ? 'table' - : pathname?.includes(`/project/${ref}/sql`) - ? 'sql' - : undefined + if (pathname?.includes(`/project/${ref}/editor`)) return 'table' + if (pathname?.includes(`/project/${ref}/sql`)) return 'sql' + if (pathname?.includes(`/project/${ref}/explorer`)) return 'explorer' + + return undefined } diff --git a/apps/studio/state/tabs.tsx b/apps/studio/state/tabs.tsx index 85c1dc9d56a..dc8ba7e00fe 100644 --- a/apps/studio/state/tabs.tsx +++ b/apps/studio/state/tabs.tsx @@ -13,11 +13,13 @@ import { proxy, subscribe, useSnapshot } from 'valtio' import { buildTableEditorUrl } from '@/components/grid/SupabaseGrid.utils' import type { SqlSnippetSource } from '@/components/interfaces/SQLEditor/querySource' -import { ENTITY_TYPE } from '@/data/entity-types/entity-type-constants' +import type { EditorType } from '@/components/layouts/editors/EditorsLayout.hooks' +import type { ENTITY_TYPE } from '@/data/entity-types/entity-type-constants' export const editorEntityTypes = { table: ['r', 'v', 'm', 'f', 'p'], sql: ['sql'], + explorer: ['notebook'], } export type TabType = ENTITY_TYPE | 'sql' @@ -469,7 +471,7 @@ export function createTabsState(projectRef: string) { }: { id: string router: NextRouter - editor?: 'sql' | 'table' + editor?: EditorType onClose?: (id: string) => void onClearDashboardHistory: () => void }) => {