fix(ui): make dashboard context menu work everywhere (#85)

Move DashboardContextMenu to wrap SidebarProvider so the
right-click menu triggers across the entire dashboard
viewport including sidebar, header, and main content.

- MainContent now forwards refs and spreads props for Radix
  asChild compatibility
- FeedbackWidget hoisted above SidebarProvider so
  DashboardContextMenu can access useFeedback from wider
  scope
- NavFiles uses useFilesOptional to avoid crash when
  rendered outside FilesProvider (sidebar is in dashboard
  layout, not files route layout)

Co-authored-by: Nicholai <nicholaivogelfilms@gmail.com>
This commit is contained in:
Nicholai 2026-02-15 16:58:33 -07:00 committed by GitHub
parent 77130dea9b
commit 909af53711
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 33 additions and 24 deletions

View File

@ -51,6 +51,8 @@ export default async function DashboardLayout({
<CommandMenuProvider> <CommandMenuProvider>
<BiometricGuard> <BiometricGuard>
<DesktopShell> <DesktopShell>
<FeedbackWidget>
<DashboardContextMenu>
<SidebarProvider <SidebarProvider
defaultOpen={false} defaultOpen={false}
className="h-screen overflow-hidden" className="h-screen overflow-hidden"
@ -61,21 +63,17 @@ export default async function DashboardLayout({
} }
> >
<AppSidebar variant="inset" projects={projectList} dashboards={dashboardList} user={user} /> <AppSidebar variant="inset" projects={projectList} dashboards={dashboardList} user={user} />
<FeedbackWidget> <SidebarInset className="overflow-hidden">
<SidebarInset className="overflow-hidden"> <DesktopOfflineBanner />
<DesktopOfflineBanner /> <OfflineBanner />
<OfflineBanner /> <SiteHeader user={user} />
<SiteHeader user={user} /> <div className="flex min-h-0 flex-1 overflow-hidden">
<div className="flex min-h-0 flex-1 overflow-hidden"> <MainContent>
<DashboardContextMenu> {children}
<MainContent> </MainContent>
{children} <ChatPanelShell />
</MainContent> </div>
</DashboardContextMenu> </SidebarInset>
<ChatPanelShell />
</div>
</SidebarInset>
</FeedbackWidget>
<MobileBottomNav /> <MobileBottomNav />
<NativeShell /> <NativeShell />
<PushNotificationRegistrar /> <PushNotificationRegistrar />
@ -84,6 +82,8 @@ export default async function DashboardLayout({
</p> </p>
<Toaster position="bottom-right" /> <Toaster position="bottom-right" />
</SidebarProvider> </SidebarProvider>
</DashboardContextMenu>
</FeedbackWidget>
</DesktopShell> </DesktopShell>
</BiometricGuard> </BiometricGuard>
</CommandMenuProvider> </CommandMenuProvider>

View File

@ -6,9 +6,9 @@ import { useRenderState } from "./chat-provider"
export function MainContent({ export function MainContent({
children, children,
}: { className: classNameProp,
readonly children: React.ReactNode ...rest
}) { }: React.ComponentPropsWithRef<"div">) {
const pathname = usePathname() const pathname = usePathname()
const { spec, isRendering } = useRenderState() const { spec, isRendering } = useRenderState()
const hasRenderedUI = !!spec?.root || isRendering const hasRenderedUI = !!spec?.root || isRendering
@ -18,6 +18,7 @@ export function MainContent({
return ( return (
<div <div
{...rest}
className={cn( className={cn(
"flex flex-col overflow-x-hidden min-w-0", "flex flex-col overflow-x-hidden min-w-0",
"transition-[flex,opacity] duration-300 ease-in-out", "transition-[flex,opacity] duration-300 ease-in-out",
@ -25,7 +26,8 @@ export function MainContent({
? "flex-[0_0_0%] opacity-0 overflow-hidden pointer-events-none" ? "flex-[0_0_0%] opacity-0 overflow-hidden pointer-events-none"
: isConversations : isConversations
? "flex-1 overflow-hidden" ? "flex-1 overflow-hidden"
: "flex-1 overflow-y-auto pb-14 md:pb-0" : "flex-1 overflow-y-auto pb-14 md:pb-0",
classNameProp
)} )}
> >
<div className={cn( <div className={cn(

View File

@ -11,7 +11,7 @@ import {
import Link from "next/link" import Link from "next/link"
import { usePathname, useSearchParams } from "next/navigation" import { usePathname, useSearchParams } from "next/navigation"
import { useFiles } from "@/hooks/use-files" import { useFilesOptional } from "@/hooks/use-files"
import { StorageIndicator } from "@/components/files/storage-indicator" import { StorageIndicator } from "@/components/files/storage-indicator"
import { import {
SidebarGroup, SidebarGroup,
@ -49,7 +49,8 @@ export function NavFiles() {
const pathname = usePathname() const pathname = usePathname()
const searchParams = useSearchParams() const searchParams = useSearchParams()
const activeView = searchParams.get("view") ?? "my-files" const activeView = searchParams.get("view") ?? "my-files"
const { storageUsage } = useFiles() const files = useFilesOptional()
const storageUsage = files?.storageUsage
return ( return (
<> <>
@ -102,9 +103,11 @@ export function NavFiles() {
</SidebarMenu> </SidebarMenu>
</SidebarGroupContent> </SidebarGroupContent>
</SidebarGroup> </SidebarGroup>
<div className="mt-auto px-3 pb-3"> {storageUsage && (
<StorageIndicator usage={storageUsage} /> <div className="mt-auto px-3 pb-3">
</div> <StorageIndicator usage={storageUsage} />
</div>
)}
</> </>
) )
} }

View File

@ -733,6 +733,10 @@ export function useFiles() {
return ctx return ctx
} }
export function useFilesOptional() {
return useContext(FilesContext)
}
function sortFiles( function sortFiles(
files: FileItem[], files: FileItem[],
sortBy: SortField, sortBy: SortField,