feat(uat-batch-17): layout polish — DocumentsHub flush-left, breadcrumb wrap fix, viewport-centered topbar search
- DocumentsHub root container gains `sm:-mx-6 sm:-mt-3 sm:-mb-6` to escape the AppShell main padding (`px-6 pt-3 pb-6`). The folder column now sits flush against the global app sidebar, reading as an extension of navigation rather than a card-inside-a-page. Mobile layout retains the AppShell padding. - Breadcrumbs: each crumb + its trailing separator now share a single `<BreadcrumbItem>` instead of being separate `<li>`s. Flex-wrap can no longer strand an orphan separator at end-of-line above a wrapped child crumb. Drops the standalone `<BreadcrumbSeparator>` usage from the consumer; the primitive is still exported for backcompat. - Topbar search visually centered against the full viewport via a `translate-x:calc(-var(--width-sidebar)/2)` shift. Grid middle slot bumped from `minmax(360px, 640px)` → `minmax(420px, 800px)` and the search wrapper from `max-w-md` → `max-w-2xl` so reps actually have room to read long results. tsc clean. 1419/1419 vitest pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -243,7 +243,12 @@ export function DocumentsHub({ portSlug }: DocumentsHubProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full">
|
// Escape the AppShell's desktop main padding (px-6 pt-3 pb-6) so the
|
||||||
|
// folder column sits flush against the global app sidebar — reads
|
||||||
|
// as an extension of navigation rather than a card-inside-a-page.
|
||||||
|
// Inner content keeps its own padding so the right pane doesn't
|
||||||
|
// run flush with the viewport edge.
|
||||||
|
<div className="h-full sm:-mx-6 sm:-mt-3 sm:-mb-6">
|
||||||
{/* Mobile: stacked, with a Sheet drawer for folders. */}
|
{/* Mobile: stacked, with a Sheet drawer for folders. */}
|
||||||
<div className="flex flex-col h-full sm:hidden">
|
<div className="flex flex-col h-full sm:hidden">
|
||||||
<FolderTreeSidebar
|
<FolderTreeSidebar
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
import { Fragment } from 'react';
|
|
||||||
import { ChevronRight } from 'lucide-react';
|
import { ChevronRight } from 'lucide-react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -11,7 +10,6 @@ import {
|
|||||||
BreadcrumbLink,
|
BreadcrumbLink,
|
||||||
BreadcrumbList,
|
BreadcrumbList,
|
||||||
BreadcrumbPage,
|
BreadcrumbPage,
|
||||||
BreadcrumbSeparator,
|
|
||||||
} from '@/components/ui/breadcrumb';
|
} from '@/components/ui/breadcrumb';
|
||||||
import { useUIStore } from '@/stores/ui-store';
|
import { useUIStore } from '@/stores/ui-store';
|
||||||
import { useBreadcrumbStore } from '@/stores/breadcrumb-store';
|
import { useBreadcrumbStore } from '@/stores/breadcrumb-store';
|
||||||
@@ -102,14 +100,17 @@ export function Breadcrumbs() {
|
|||||||
return (
|
return (
|
||||||
<Breadcrumb>
|
<Breadcrumb>
|
||||||
<BreadcrumbList className="text-sm gap-1.5">
|
<BreadcrumbList className="text-sm gap-1.5">
|
||||||
{crumbs.map((crumb, _index) => (
|
{crumbs.map((crumb) => (
|
||||||
<Fragment key={crumb.href}>
|
// Each crumb + its trailing separator share a single
|
||||||
<BreadcrumbItem>
|
// inline-flex `<li>` so flex-wrap can't strand the
|
||||||
|
// separator at end-of-line above the wrapped child crumb.
|
||||||
|
<BreadcrumbItem key={crumb.href}>
|
||||||
{crumb.isLast ? (
|
{crumb.isLast ? (
|
||||||
<BreadcrumbPage className="font-medium text-foreground truncate max-w-[160px]">
|
<BreadcrumbPage className="font-medium text-foreground truncate max-w-[160px]">
|
||||||
{crumb.label}
|
{crumb.label}
|
||||||
</BreadcrumbPage>
|
</BreadcrumbPage>
|
||||||
) : (
|
) : (
|
||||||
|
<>
|
||||||
<BreadcrumbLink asChild>
|
<BreadcrumbLink asChild>
|
||||||
<Link
|
<Link
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@@ -119,14 +120,14 @@ export function Breadcrumbs() {
|
|||||||
{crumb.label}
|
{crumb.label}
|
||||||
</Link>
|
</Link>
|
||||||
</BreadcrumbLink>
|
</BreadcrumbLink>
|
||||||
|
<ChevronRight
|
||||||
|
className="w-3 h-3 text-muted-foreground/40"
|
||||||
|
aria-hidden
|
||||||
|
role="presentation"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</BreadcrumbItem>
|
</BreadcrumbItem>
|
||||||
{!crumb.isLast && (
|
|
||||||
<BreadcrumbSeparator className="text-muted-foreground/40">
|
|
||||||
<ChevronRight className="w-3 h-3" aria-hidden />
|
|
||||||
</BreadcrumbSeparator>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
))}
|
))}
|
||||||
</BreadcrumbList>
|
</BreadcrumbList>
|
||||||
</Breadcrumb>
|
</Breadcrumb>
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export function Topbar({ ports, user }: TopbarProps) {
|
|||||||
// Three-column grid: breadcrumbs left, search center, actions right.
|
// Three-column grid: breadcrumbs left, search center, actions right.
|
||||||
// The brand logo lives in the sidebar header (per design feedback) so the
|
// The brand logo lives in the sidebar header (per design feedback) so the
|
||||||
// topbar center is dedicated to the global search bar.
|
// topbar center is dedicated to the global search bar.
|
||||||
<header className="grid h-14 grid-cols-[minmax(0,1fr)_minmax(360px,640px)_minmax(0,1fr)] items-center border-b border-border bg-background gap-3 px-4 shrink-0">
|
<header className="grid h-14 grid-cols-[minmax(0,1fr)_minmax(420px,800px)_minmax(0,1fr)] items-center border-b border-border bg-background gap-3 px-4 shrink-0">
|
||||||
{/* LEFT: optional back button + breadcrumbs / page title */}
|
{/* LEFT: optional back button + breadcrumbs / page title */}
|
||||||
<div className="min-w-0 flex items-center gap-1.5">
|
<div className="min-w-0 flex items-center gap-1.5">
|
||||||
{showBackButton && (
|
{showBackButton && (
|
||||||
@@ -74,11 +74,14 @@ export function Topbar({ ports, user }: TopbarProps) {
|
|||||||
<Breadcrumbs />
|
<Breadcrumbs />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* CENTER: global search - capped width and horizontally centered
|
{/* CENTER: global search - capped width and visually centered
|
||||||
inside its grid slot so it sits visually in the middle of the
|
against the FULL viewport (not the post-sidebar area) via a
|
||||||
topbar regardless of breadcrumb / action-row width. */}
|
translate-X that shifts left by half the sidebar width.
|
||||||
|
Without the translate the topbar's grid centers inside the
|
||||||
|
area-after-the-sidebar, so the search visually drifts right
|
||||||
|
by half the sidebar width. */}
|
||||||
<div className="flex items-center justify-center min-w-0">
|
<div className="flex items-center justify-center min-w-0">
|
||||||
<div className="w-full max-w-md mx-auto min-w-0">
|
<div className="w-full max-w-2xl mx-auto min-w-0 sm:-translate-x-[calc(var(--width-sidebar)/2)]">
|
||||||
<CommandSearch />
|
<CommandSearch />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user