Files
cloudflare-domain-manager/apps/web/src/components/layout/site-header.tsx
T
DenozordecandCursor 39fac7834f
quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 8s
quality / changes (push) Successful in 9s
quality / api (push) Skipped
quality / docker-check (push) Skipped
quality / web (push) Successful in 1m2s
CD / quality (push) Successful in 1m15s
CD / publish (push) Successful in 1m58s
fix(ui): не дублировать крошку Настройки при смене раздела
Co-authored-by: Cursor <[email protected]>
2026-08-20 12:28:06 +07:00

80 lines
2.7 KiB
TypeScript

import { Fragment, useMemo } from 'react'
import { Link, useMatches, useRouterState } from '@tanstack/react-router'
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@cfdm/ui/components/breadcrumb'
import { Separator } from '@cfdm/ui/components/separator'
import { SystemMonitorPopover } from '@/components/layout/system-monitor-popover'
import { AppsMenu } from '@/components/layout/apps-menu'
import { SidebarTrigger } from '@cfdm/ui/components/sidebar'
import { getBreadcrumbs } from '@/lib/breadcrumbs'
export interface RouteBreadcrumbLoaderData {
breadcrumb?: string
}
function useDynamicBreadcrumbLabels() {
const matches = useMatches()
return useMemo(() => {
const labels: Record<string, string> = {}
for (const match of matches) {
const data = match.loaderData as RouteBreadcrumbLoaderData | undefined
if (data?.breadcrumb && match.pathname) {
labels[match.pathname] = data.breadcrumb
}
}
return labels
}, [matches])
}
/** Header chrome — AppsMenu + SystemMonitor; theme in NavUser (app-shell-1). */
export function SiteHeader() {
const pathname = useRouterState({ select: (s) => s.location.pathname })
const dynamicLabels = useDynamicBreadcrumbLabels()
const crumbs = useMemo(
() => getBreadcrumbs(pathname, dynamicLabels),
[pathname, dynamicLabels],
)
return (
<header className="bg-background sticky top-0 z-10 flex h-12 shrink-0 items-center gap-2 border-b px-4 md:px-6">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 data-[orientation=vertical]:h-4" />
<Breadcrumb>
<BreadcrumbList>
{crumbs.map((crumb, index) => {
const isLast = index === crumbs.length - 1
return (
<Fragment key={`${index}-${crumb.href}`}>
{index > 0 ? (
<BreadcrumbSeparator className="hidden md:block" />
) : null}
<BreadcrumbItem
className={index === 0 && !isLast ? 'hidden md:block' : undefined}
>
{isLast ? (
<BreadcrumbPage>{crumb.label}</BreadcrumbPage>
) : (
<BreadcrumbLink render={<Link to={crumb.href} />}>
{crumb.label}
</BreadcrumbLink>
)}
</BreadcrumbItem>
</Fragment>
)
})}
</BreadcrumbList>
</Breadcrumb>
<div className="ml-auto flex items-center gap-2">
<AppsMenu />
<SystemMonitorPopover />
</div>
</header>
)
}