import type { ReactNode } from 'react'; import { Link } from 'react-router'; import { PROJECT_DISPLAY_NAME, Wordmark } from '@/components/Logo'; import { useDocsVisibility } from '@/hooks/api'; import { isDocsHrefVisible } from '@/lib/docs-links'; import { cn } from '@/lib/utils'; /* * Site footer. One footer for every public page: wordmark-only brand column, * link columns under uppercase micro-headings, and an optional trailing column * of icon buttons (socials, package registries…) laid out two per row. A single * top hairline is the only rule — no copyright bar (copyright notice has been * legally optional since the Berne Convention; drop it unless your counsel * disagrees). Dashboard pages render no footer at all. */ interface FooterLink { text: string; href: string; } interface FooterColumnProps { title: string; links: FooterLink[]; } interface FooterIconLink { label: string; href: string; icon: ReactNode; } interface FooterIconColumnProps { title: string; links: FooterIconLink[]; } interface FooterProps { logo?: ReactNode; columns?: FooterColumnProps[]; /** Trailing column of icon-only buttons, e.g. GitHub / npm / Discord. */ iconColumn?: FooterIconColumnProps; className?: string; } const DEFAULT_COLUMNS: FooterColumnProps[] = [ { title: 'Product', links: [ { text: 'Features', href: '#features' }, { text: 'Pricing', href: '#pricing' }, { text: 'Changelog', href: '/changelog' }, ], }, { title: 'Docs', links: [ { text: 'Getting started', href: '/docs' }, { text: 'Guides', href: '/docs' }, { text: 'API reference', href: '/docs' }, ], }, { title: 'Legal', links: [ { text: 'Privacy', href: '/privacy' }, { text: 'Terms', href: '/terms' }, ], }, ]; function ColumnHeading({ children }: { children: ReactNode }) { return (

{children}

); } function ColumnLink({ text, href }: FooterLink) { const className = 'w-fit text-sm text-muted-foreground transition-colors hover:text-primary'; return href.startsWith('/') && !href.startsWith('//') ? ( {text} ) : ( {text} ); } export default function FooterSection({ logo = , columns = DEFAULT_COLUMNS, iconColumn, className, }: FooterProps) { const docsVisibility = useDocsVisibility(); // Drop links to a documentation surface the operator turned off, then drop // any column left with nothing in it — the default "Docs" column is entirely // user-docs links, so hiding them would otherwise leave a bare heading. const visibleColumns = columns .map((column) => ({ ...column, links: column.links.filter((link) => isDocsHrefVisible(link.href, docsVisibility)), })) .filter((column) => column.links.length > 0); return ( ); }