Files
supabase/apps/studio/components/layouts/PageLayout/PageHeader.tsx
Ivan Vasilov 308cd791a2 chore: Prep work for migrating to Tailwind v4 (#45285)
This PR preps the monorepo for a migration to Tailwind v4:
- Bump all Tailwind dependencies and libraries to the latest possible
version, while still compatible with Tailwind 3.
- Cleans up obsolete Tailwind 3 specific options and configs.
- Cleans up unused CSS files and fixes the CSS imports.
- Migrates all `important` uses in `@apply` lines to using the `!`
prefix.
- Move `typography.css` to the `config` package and import it from the
apps.
- Migrated all occurrences of `flex-grow`, `flex-shrink`,
`overflow-clip` and `overflow-ellipsis` since they're deprecated and
will be removed in Tailwind 4.
- Make the default theme object typesafe in the `ui` package.
- Migrate all `bg-opacity`, `border-opacity`, `ring-opacity` and
`divider-opacity` to the new format where they're declared as part of
the property color.
- Bump and unify all imports of `postcss` dependency.
2026-04-28 11:33:53 +02:00

136 lines
4.9 KiB
TypeScript

import { useParams } from 'common'
import { ChevronLeft } from 'lucide-react'
import Link from 'next/link'
import { Fragment, ReactNode } from 'react'
import { cn } from 'ui'
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage as BreadcrumbPageItem,
BreadcrumbSeparator,
} from 'ui/src/components/shadcn/ui/breadcrumb'
import { ScaffoldDescription, ScaffoldTitle } from '../Scaffold'
interface PageHeaderProps {
title?: string | ReactNode
subtitle?: string | ReactNode
icon?: ReactNode
breadcrumbs?: Array<{
label?: string
href?: string
element?: ReactNode
}>
primaryActions?: ReactNode
secondaryActions?: ReactNode
className?: string
isCompact?: boolean
}
export const PageHeader = ({
title,
subtitle,
icon,
breadcrumbs = [],
primaryActions,
secondaryActions,
className,
isCompact = false,
}: PageHeaderProps) => {
const { ref } = useParams()
const displayBreadcrumbs = isCompact && title ? [...breadcrumbs, { label: title }] : breadcrumbs
return (
<div className={cn('space-y-4', className)}>
{(displayBreadcrumbs.length > 0 ||
(isCompact && (title || primaryActions || secondaryActions))) && (
<div className={cn('flex items-center gap-4', isCompact ? 'justify-between' : 'mb-4')}>
<div className="flex items-center gap-4 flex-1 min-w-0">
{breadcrumbs.length > 0 ? (
<Breadcrumb
className={cn('text-foreground-muted', isCompact && 'text-base', 'min-w-0 flex-1')}
>
<BreadcrumbList className={cn(isCompact ? 'text-base' : 'text-xs', 'min-w-0')}>
{breadcrumbs.map((item, index) => (
<Fragment key={item.label || `breadcrumb-${index}`}>
<BreadcrumbItem>
{item.element ? (
item.element
) : item.href ? (
<BreadcrumbLink asChild className="flex items-center gap-2">
<Link href={!!ref ? item.href.replace('[ref]', ref) : item.href}>
{breadcrumbs.length === 1 && !isCompact && (
<ChevronLeft size={16} strokeWidth={1.5} />
)}
{item.label}
</Link>
</BreadcrumbLink>
) : (
<BreadcrumbPageItem className="flex items-center gap-2">
{breadcrumbs.length === 1 && (
<ChevronLeft size={16} strokeWidth={1.5} />
)}
{item.label}
</BreadcrumbPageItem>
)}
</BreadcrumbItem>
{index < breadcrumbs.length - 1 && <BreadcrumbSeparator />}
</Fragment>
))}
{isCompact && title && (
<>
<BreadcrumbSeparator />
<BreadcrumbItem className="min-w-0 flex-1">
<BreadcrumbPageItem className="min-w-0">{title}</BreadcrumbPageItem>
</BreadcrumbItem>
</>
)}
</BreadcrumbList>
</Breadcrumb>
) : isCompact ? (
<div className="min-w-0 flex-1">{title}</div>
) : null}
</div>
{isCompact && (
<div className="flex items-center gap-2 shrink-0">
{secondaryActions && (
<div className="flex items-center gap-2">{secondaryActions}</div>
)}
{primaryActions && <div className="flex items-center gap-2">{primaryActions}</div>}
</div>
)}
</div>
)}
{!isCompact && (
<div className="flex items-center justify-between gap-4">
<div className="space-y-4">
<div className="flex items-center gap-4">
{icon && <div className="text-foreground-light">{icon}</div>}
<div className="space-y-1">
{title &&
(typeof title === 'string' ? <ScaffoldTitle>{title}</ScaffoldTitle> : title)}
{subtitle &&
(typeof subtitle === 'string' ? (
<ScaffoldDescription className="text-sm text-foreground-light">
{subtitle}
</ScaffoldDescription>
) : (
subtitle
))}
</div>
</div>
</div>
<div className="flex items-center gap-2">
{secondaryActions && <div className="flex items-center gap-2">{secondaryActions}</div>}
{primaryActions && <div className="flex items-center gap-2">{primaryActions}</div>}
</div>
</div>
)}
</div>
)
}