Adopt the shared ScrollArea for app-wide scrolling
This commit is contained in:
@@ -9,6 +9,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
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 { ArrowLeft, ArrowRight, Check, Copy, Loader2, Rocket } from 'lucide-react';
|
||||||
|
|
||||||
import { useSchemaStore } from '@/stores/schemaStore';
|
import { useSchemaStore } from '@/stores/schemaStore';
|
||||||
@@ -403,13 +404,15 @@ export function BootstrapWizard() {
|
|||||||
|
|
||||||
function WizardShell({ children }: { children: React.ReactNode }) {
|
function WizardShell({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
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">
|
<header className="flex items-center px-6 py-4 border-b bg-background">
|
||||||
<DefaultLogo />
|
<DefaultLogo />
|
||||||
</header>
|
</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>
|
<div className="mx-auto max-w-3xl">{children}</div>
|
||||||
</main>
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
@@ -1831,7 +1832,8 @@ function EnumMultiSelect({ enumName, items, onChange, readOnly, schema, minItems
|
|||||||
className="h-8"
|
className="h-8"
|
||||||
/>
|
/>
|
||||||
</div>
|
</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 && (
|
{filtered.length === 0 && (
|
||||||
<p className="text-sm text-muted-foreground text-center py-2">{t('field.noMatches', 'No matches')}</p>
|
<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>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { cn } from '@/lib/utils';
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { EnterpriseUpsell } from '@/components/common/EnterpriseUpsell';
|
import { EnterpriseUpsell } from '@/components/common/EnterpriseUpsell';
|
||||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
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 { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import { useUIStore } from '@/stores/uiStore';
|
import { useUIStore } from '@/stores/uiStore';
|
||||||
import { useAccountStore } from '@/stores/accountStore';
|
import { useAccountStore } from '@/stores/accountStore';
|
||||||
@@ -342,8 +343,8 @@ export function Sidebar() {
|
|||||||
onClick={() => setSidebarOpen(false)}
|
onClick={() => setSidebarOpen(false)}
|
||||||
/>
|
/>
|
||||||
<aside className="fixed top-14 left-0 bottom-0 z-30 flex w-64 flex-col border-r bg-background">
|
<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]">
|
<ScrollArea className="flex-1">
|
||||||
<nav ref={navRef} className="flex flex-col gap-0.5 px-2">
|
<nav ref={navRef} className="flex flex-col gap-0.5 px-2 py-2">
|
||||||
{layout.items.map((item) => (
|
{layout.items.map((item) => (
|
||||||
<SidebarTopItem
|
<SidebarTopItem
|
||||||
key={'link' in item ? item.link.viewName : item.container.name}
|
key={'link' in item ? item.link.viewName : item.container.name}
|
||||||
@@ -356,7 +357,7 @@ export function Sidebar() {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</ScrollArea>
|
||||||
|
|
||||||
{layouts.length > 1 && (
|
{layouts.length > 1 && (
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { Search } from 'lucide-react';
|
|||||||
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Dialog, DialogContent } from '@/components/ui/dialog';
|
import { Dialog, DialogContent } from '@/components/ui/dialog';
|
||||||
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
|
|
||||||
const Command = React.forwardRef<
|
const Command = React.forwardRef<
|
||||||
React.ComponentRef<typeof CommandPrimitive>,
|
React.ComponentRef<typeof CommandPrimitive>,
|
||||||
@@ -61,11 +62,9 @@ const CommandList = React.forwardRef<
|
|||||||
React.ComponentRef<typeof CommandPrimitive.List>,
|
React.ComponentRef<typeof CommandPrimitive.List>,
|
||||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
|
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<CommandPrimitive.List
|
<ScrollArea viewportClassName="max-h-[300px]">
|
||||||
ref={ref}
|
<CommandPrimitive.List ref={ref} className={cn('overflow-x-hidden', className)} {...props} />
|
||||||
className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}
|
</ScrollArea>
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
));
|
));
|
||||||
CommandList.displayName = CommandPrimitive.List.displayName;
|
CommandList.displayName = CommandPrimitive.List.displayName;
|
||||||
|
|
||||||
|
|||||||
@@ -11,10 +11,17 @@ import { cn } from '@/lib/utils';
|
|||||||
|
|
||||||
const ScrollArea = React.forwardRef<
|
const ScrollArea = React.forwardRef<
|
||||||
React.ComponentRef<typeof ScrollAreaPrimitive.Root>,
|
React.ComponentRef<typeof ScrollAreaPrimitive.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & {
|
||||||
>(({ className, children, ...props }, ref) => (
|
// 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.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 />
|
<ScrollBar />
|
||||||
<ScrollAreaPrimitive.Corner />
|
<ScrollAreaPrimitive.Corner />
|
||||||
</ScrollAreaPrimitive.Root>
|
</ScrollAreaPrimitive.Root>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { setLocale } from '@/i18n';
|
|||||||
import { TopBar } from '@/components/layout/TopBar';
|
import { TopBar } from '@/components/layout/TopBar';
|
||||||
import { Sidebar } from '@/components/layout/Sidebar';
|
import { Sidebar } from '@/components/layout/Sidebar';
|
||||||
import { MainContent } from '@/components/layout/MainContent';
|
import { MainContent } from '@/components/layout/MainContent';
|
||||||
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
import { ErrorBoundary } from '@/components/layout/ErrorBoundary';
|
import { ErrorBoundary } from '@/components/layout/ErrorBoundary';
|
||||||
import { LoadingFallback } from '@/components/common/LoadingFallback';
|
import { LoadingFallback } from '@/components/common/LoadingFallback';
|
||||||
import {
|
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 (
|
return (
|
||||||
<div className="flex min-h-screen flex-col">
|
<div className="flex h-screen flex-col">
|
||||||
<TopBar />
|
<TopBar />
|
||||||
<div className="flex flex-1">
|
<div className="flex min-h-0 flex-1">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<main
|
<ScrollArea
|
||||||
className={`flex-1 overflow-auto bg-content-background p-6 transition-[margin] ${sidebarOpen ? 'md:ml-64' : ''}`}
|
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">
|
<div className="mx-auto w-full max-w-7xl">
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<MainContent viewName={viewName} id={id} section={section} />
|
<MainContent viewName={viewName} id={id} section={section} />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user