Adopt the shared ScrollArea for app-wide scrolling

This commit is contained in:
Steven RYDELL
2026-07-29 07:20:43 +02:00
parent bd16e64e51
commit c15cb22be4
6 changed files with 45 additions and 26 deletions
+6 -3
View File
@@ -9,6 +9,7 @@ import { useTranslation } from 'react-i18next';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { ScrollArea } from '@/components/ui/scroll-area';
import { ArrowLeft, ArrowRight, Check, Copy, Loader2, Rocket } from 'lucide-react';
import { useSchemaStore } from '@/stores/schemaStore';
@@ -403,13 +404,15 @@ export function BootstrapWizard() {
function WizardShell({ children }: { children: React.ReactNode }) {
return (
<div className="flex min-h-screen flex-col bg-content-background">
<div className="flex h-screen flex-col bg-content-background">
<header className="flex items-center px-6 py-4 border-b bg-background">
<DefaultLogo />
</header>
<main className="flex-1 overflow-auto p-6">
<ScrollArea role="main" className="flex-1">
<div className="p-6">
<div className="mx-auto max-w-3xl">{children}</div>
</main>
</div>
</ScrollArea>
</div>
);
}
+4 -1
View File
@@ -15,6 +15,7 @@ import { Button } from '@/components/ui/button';
import { Switch } from '@/components/ui/switch';
import { Badge } from '@/components/ui/badge';
import { Checkbox } from '@/components/ui/checkbox';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
@@ -1831,7 +1832,8 @@ function EnumMultiSelect({ enumName, items, onChange, readOnly, schema, minItems
className="h-8"
/>
</div>
<div className="max-h-60 overflow-y-auto p-2 space-y-1">
<ScrollArea viewportClassName="max-h-60">
<div className="p-2 space-y-1">
{filtered.length === 0 && (
<p className="text-sm text-muted-foreground text-center py-2">{t('field.noMatches', 'No matches')}</p>
)}
@@ -1860,6 +1862,7 @@ function EnumMultiSelect({ enumName, items, onChange, readOnly, schema, minItems
</div>
))}
</div>
</ScrollArea>
</PopoverContent>
</Popover>
</div>
+4 -3
View File
@@ -12,6 +12,7 @@ import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import { EnterpriseUpsell } from '@/components/common/EnterpriseUpsell';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { useUIStore } from '@/stores/uiStore';
import { useAccountStore } from '@/stores/accountStore';
@@ -342,8 +343,8 @@ export function Sidebar() {
onClick={() => setSidebarOpen(false)}
/>
<aside className="fixed top-14 left-0 bottom-0 z-30 flex w-64 flex-col border-r bg-background">
<div className="flex-1 overflow-y-auto py-2 [scrollbar-width:thin]">
<nav ref={navRef} className="flex flex-col gap-0.5 px-2">
<ScrollArea className="flex-1">
<nav ref={navRef} className="flex flex-col gap-0.5 px-2 py-2">
{layout.items.map((item) => (
<SidebarTopItem
key={'link' in item ? item.link.viewName : item.container.name}
@@ -356,7 +357,7 @@ export function Sidebar() {
/>
))}
</nav>
</div>
</ScrollArea>
{layouts.length > 1 && (
<TooltipProvider>
+4 -5
View File
@@ -11,6 +11,7 @@ import { Search } from 'lucide-react';
import { cn } from '@/lib/utils';
import { Dialog, DialogContent } from '@/components/ui/dialog';
import { ScrollArea } from '@/components/ui/scroll-area';
const Command = React.forwardRef<
React.ComponentRef<typeof CommandPrimitive>,
@@ -61,11 +62,9 @@ const CommandList = React.forwardRef<
React.ComponentRef<typeof CommandPrimitive.List>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
>(({ className, ...props }, ref) => (
<CommandPrimitive.List
ref={ref}
className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}
{...props}
/>
<ScrollArea viewportClassName="max-h-[300px]">
<CommandPrimitive.List ref={ref} className={cn('overflow-x-hidden', className)} {...props} />
</ScrollArea>
));
CommandList.displayName = CommandPrimitive.List.displayName;
+10 -3
View File
@@ -11,10 +11,17 @@ import { cn } from '@/lib/utils';
const ScrollArea = React.forwardRef<
React.ComponentRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
>(({ className, children, ...props }, ref) => (
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & {
// Height caps (max-h-*) must be applied to the Viewport — the actual
// scroller — because a percentage height resolves to auto when the
// Root's height is content-driven, which would break scrolling.
viewportClassName?: string;
}
>(({ className, viewportClassName, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root ref={ref} className={cn('relative overflow-hidden', className)} {...props}>
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">{children}</ScrollAreaPrimitive.Viewport>
<ScrollAreaPrimitive.Viewport className={cn('h-full w-full rounded-[inherit]', viewportClassName)}>
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
+11 -5
View File
@@ -16,6 +16,7 @@ import { setLocale } from '@/i18n';
import { TopBar } from '@/components/layout/TopBar';
import { Sidebar } from '@/components/layout/Sidebar';
import { MainContent } from '@/components/layout/MainContent';
import { ScrollArea } from '@/components/ui/scroll-area';
import { ErrorBoundary } from '@/components/layout/ErrorBoundary';
import { LoadingFallback } from '@/components/common/LoadingFallback';
import {
@@ -249,20 +250,25 @@ export default function AdminPanel() {
);
}
// The shell is viewport-height so the main area scrolls internally through
// the shared ScrollArea instead of scrolling the whole window.
return (
<div className="flex min-h-screen flex-col">
<div className="flex h-screen flex-col">
<TopBar />
<div className="flex flex-1">
<div className="flex min-h-0 flex-1">
<Sidebar />
<main
className={`flex-1 overflow-auto bg-content-background p-6 transition-[margin] ${sidebarOpen ? 'md:ml-64' : ''}`}
<ScrollArea
role="main"
className={`flex-1 bg-content-background transition-[margin] ${sidebarOpen ? 'md:ml-64' : ''}`}
>
<div className="p-6">
<div className="mx-auto w-full max-w-7xl">
<ErrorBoundary>
<MainContent viewName={viewName} id={id} section={section} />
</ErrorBoundary>
</div>
</main>
</div>
</ScrollArea>
</div>
</div>
);