Stretch sidebar hover full-width with a separated footer in square mode

This commit is contained in:
Steven RYDELL
2026-07-29 08:55:57 +02:00
parent 272502e0a3
commit 09d43f1e9b
+23 -6
View File
@@ -121,6 +121,11 @@ function AccordionCollapsible({
const sidebarItemClass = const sidebarItemClass =
'w-full justify-start gap-2 font-normal text-muted-foreground hover:bg-accent/50 hover:text-foreground'; 'w-full justify-start gap-2 font-normal text-muted-foreground hover:bg-accent/50 hover:text-foreground';
// In square mode the sidebar follows documentation conventions (better-auth):
// full-bleed rows with a barely-there hover wash instead of inset pills.
const sidebarItemSquareClass =
"[[data-radius='square']_&]:hover:bg-foreground/[0.03] [[data-radius='square']_&]:hover:text-foreground/90";
function checkLinkVisible(viewName: string): boolean { function checkLinkVisible(viewName: string): boolean {
const schema = useSchemaStore.getState().schema; const schema = useSchemaStore.getState().schema;
if (!schema) return true; if (!schema) return true;
@@ -170,6 +175,7 @@ function SidebarSubItem({ item, depth, sectionName, currentPath, navigate, editi
data-sidebar-active={isActive || undefined} data-sidebar-active={isActive || undefined}
className={cn( className={cn(
sidebarItemClass, sidebarItemClass,
sidebarItemSquareClass,
isActive && 'bg-accent text-accent-foreground hover:bg-accent', isActive && 'bg-accent text-accent-foreground hover:bg-accent',
depth > 0 && 'text-sm', depth > 0 && 'text-sm',
)} )}
@@ -197,7 +203,7 @@ function SidebarSubItem({ item, depth, sectionName, currentPath, navigate, editi
<CollapsibleTrigger asChild> <CollapsibleTrigger asChild>
<Button <Button
variant="ghost" variant="ghost"
className={cn(sidebarItemClass, 'text-sm')} className={cn(sidebarItemClass, sidebarItemSquareClass, 'text-sm')}
style={{ paddingLeft: `${(depth + 1) * 12 + 8}px` }} style={{ paddingLeft: `${(depth + 1) * 12 + 8}px` }}
> >
<ChevronDown className="h-3 w-3 shrink-0 transition-transform duration-200 [[data-state=closed]>&]:rotate-[-90deg]" /> <ChevronDown className="h-3 w-3 shrink-0 transition-transform duration-200 [[data-state=closed]>&]:rotate-[-90deg]" />
@@ -254,7 +260,11 @@ function SidebarTopItem({ item, sectionName, currentPath, navigate, edition, onU
<Button <Button
variant="ghost" variant="ghost"
data-sidebar-active={isActive || undefined} data-sidebar-active={isActive || undefined}
className={cn(sidebarItemClass, isActive && 'bg-accent text-accent-foreground hover:bg-accent')} className={cn(
sidebarItemClass,
sidebarItemSquareClass,
isActive && 'bg-accent text-accent-foreground hover:bg-accent',
)}
onClick={() => { onClick={() => {
if (isLocked) { if (isLocked) {
onUpsell(); onUpsell();
@@ -279,7 +289,7 @@ function SidebarTopItem({ item, sectionName, currentPath, navigate, edition, onU
return ( return (
<AccordionCollapsible id={name} containsActive={containsActive}> <AccordionCollapsible id={name} containsActive={containsActive}>
<CollapsibleTrigger asChild> <CollapsibleTrigger asChild>
<Button variant="ghost" className={sidebarItemClass}> <Button variant="ghost" className={cn(sidebarItemClass, sidebarItemSquareClass)}>
<LucideIcon name={icon} className="h-4 w-4 shrink-0" /> <LucideIcon name={icon} className="h-4 w-4 shrink-0" />
<span className="truncate">{name}</span> <span className="truncate">{name}</span>
<ChevronDown className="ml-auto h-3 w-3 shrink-0 transition-transform duration-200 [[data-state=closed]>&]:rotate-[-90deg]" /> <ChevronDown className="ml-auto h-3 w-3 shrink-0 transition-transform duration-200 [[data-state=closed]>&]:rotate-[-90deg]" />
@@ -368,7 +378,10 @@ export function Sidebar() {
/> />
<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">
<ScrollArea className="flex-1"> <ScrollArea className="flex-1">
<nav ref={navRef} className="flex flex-col gap-0.5 px-2 py-2"> <nav
ref={navRef}
className="flex flex-col gap-0.5 px-2 py-2 [[data-radius='square']_&]:gap-0 [[data-radius='square']_&]:px-0"
>
<AccordionLevel> <AccordionLevel>
{layout.items.map((item) => ( {layout.items.map((item) => (
<SidebarTopItem <SidebarTopItem
@@ -387,7 +400,7 @@ export function Sidebar() {
{layouts.length > 1 && ( {layouts.length > 1 && (
<TooltipProvider> <TooltipProvider>
<div className="flex items-center justify-around border-t bg-background px-2 py-2"> <div className="flex items-center justify-around border-t bg-background px-2 py-2 [[data-radius='square']_&]:px-0 [[data-radius='square']_&]:py-0">
{layouts.map((target) => { {layouts.map((target) => {
const Icon = (LucideIcons as Record<string, unknown>)[ const Icon = (LucideIcons as Record<string, unknown>)[
target.icon target.icon
@@ -406,7 +419,11 @@ export function Sidebar() {
aria-label={target.name} aria-label={target.name}
aria-current={isActive ? 'page' : undefined} aria-current={isActive ? 'page' : undefined}
onClick={() => handleSectionClick(target)} onClick={() => handleSectionClick(target)}
className={cn('h-9 w-9', isActive && 'bg-accent text-accent-foreground')} className={cn(
'h-9 w-9',
"[[data-radius='square']_&]:h-12 [[data-radius='square']_&]:flex-1 [[data-radius='square']_&]:border-r [[data-radius='square']_&]:border-border [[data-radius='square']_&]:last:border-r-0 [[data-radius='square']_&]:hover:bg-foreground/[0.03]",
isActive && 'bg-accent text-accent-foreground',
)}
> >
{Icon ? <Icon className="h-4 w-4" /> : <LucideIcons.Circle className="h-4 w-4" />} {Icon ? <Icon className="h-4 w-4" /> : <LucideIcons.Circle className="h-4 w-4" />}
</Button> </Button>