import { useBreakpoint, useFlag, useParams } from 'common' import { useRouter } from 'next/router' import { PropsWithChildren, useEffect, useState } from 'react' import { ResizablePanel, ResizablePanelGroup, SidebarProvider, usePanelRef } from 'ui' import { SkipToContent } from 'ui-patterns/SkipToContent' import { BannerStack } from '../ui/BannerStack/BannerStack' import { LayoutHeader } from './Navigation/LayoutHeader/LayoutHeader' import { MobileNavigationBar } from './Navigation/NavigationBar/MobileNavigationBar' import { MobileSheetProvider } from './Navigation/NavigationBar/MobileSheetContext' import { StudioMobileSheetNav } from './Navigation/NavigationBar/StudioMobileSheetNav' import { LayoutSidebar } from './ProjectLayout/LayoutSidebar' import { LayoutSidebarProvider, SIDEBAR_KEYS, } from './ProjectLayout/LayoutSidebar/LayoutSidebarProvider' import { ProjectContextProvider } from './ProjectLayout/ProjectContext' import { AppBannerWrapper } from '@/components/interfaces/App/AppBannerWrapper' import { GitHubConfigDriftBanner } from '@/components/interfaces/App/GitHubConfigDriftBanner' import { Sidebar } from '@/components/interfaces/Sidebar' import { useSyncScopedIntrospection } from '@/data/scoped-introspection' import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization' import { useCheckLatestDeploy } from '@/hooks/use-check-latest-deploy' import { IS_PLATFORM } from '@/lib/constants' import { useAppStateSnapshot } from '@/state/app-state' import { useSidebarManagerSnapshot } from '@/state/sidebar-manager-state' export interface DefaultLayoutProps { headerTitle?: string hideMobileMenu?: boolean } /** * Base layout for all project pages in the dashboard, rendered as the first child on all page files within a project. * * A second layout as the child to this is required, and the layout depends on which section of the dashboard the page is on. (e.g Auth - AuthLayout) * * The base layout handles rendering the following UI components: * - App banner (e.g for notices or incidents) * - Mobile navigation bar * - First level side navigation bar (e.g For navigating to Table Editor, SQL Editor, Database page, etc) */ export const DefaultLayout = ({ children, headerTitle, hideMobileMenu, }: PropsWithChildren) => { useSyncScopedIntrospection() useCheckLatestDeploy() const { ref } = useParams() const router = useRouter() const panelRef = usePanelRef() const isMobile = useBreakpoint('md') const isSidebarOverlay = useBreakpoint('xl') const appSnap = useAppStateSnapshot() const { isMaximised, activeSidebar } = useSidebarManagerSnapshot() const { lastVisitedOrganization } = useLastVisitedOrganization() const showConfigDrift = useFlag('ConfigDrift') && IS_PLATFORM const [isMounted, setIsMounted] = useState(false) const backToDashboardURL = router.pathname.startsWith('/account') ? appSnap.lastRouteBeforeVisitingAccountPage.length > 0 ? appSnap.lastRouteBeforeVisitingAccountPage : IS_PLATFORM && !!lastVisitedOrganization ? `/org/${lastVisitedOrganization}` : IS_PLATFORM ? '/organizations' : '/project/default' : undefined const contentMinSizePercentage = 50 const contentMaxSizePercentage = 70 useEffect(() => { setIsMounted(true) }, []) useEffect(() => { if (!isMounted || !panelRef.current || !activeSidebar || isMobile || isSidebarOverlay) return if (isMaximised) { panelRef.current.collapse() } else { panelRef.current.resize(`${contentMaxSizePercentage}%`) } }, [isMounted, isMaximised, panelRef, activeSidebar, isMobile, isSidebarOverlay]) // This is required to prevent layout shift when rendering resizable panels (they initially render at 50%, then shift // to whatever is specified). if (!isMounted) { return null } return (
{/* Top Banner */}
{isMobile && ( )} {showConfigDrift && ref && }
{/* Main Content Area */}
{/* Sidebar - Only show for project pages, not account pages */} {!router.pathname.startsWith('/account') && } {/* Main Content with Layout Sidebar */}
{children}
) }