feat: implement dynamic breadcrumbs with custom titles for pages and update dependencies
This commit is contained in:
@@ -5,6 +5,7 @@ import { Button } from "~/components/ui/button";
|
||||
import { Badge } from "~/components/ui/badge";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { BreadcrumbUpdater } from "~/components/BreadcrumbUpdater";
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{ slug: string }>;
|
||||
@@ -48,6 +49,7 @@ export default async function BlogPost({ params }: PageProps) {
|
||||
|
||||
return (
|
||||
<article className="animate-fade-in-up space-y-8">
|
||||
<BreadcrumbUpdater title={metadata.title} />
|
||||
<div className="mb-8">
|
||||
{/* <Button variant="ghost" asChild className="-ml-4 text-muted-foreground mb-4">
|
||||
<Link href="/blog">
|
||||
|
||||
+13
-10
@@ -6,6 +6,7 @@ import { Footer } from "~/components/Footer";
|
||||
import { Navigation } from "~/components/Navigation";
|
||||
import { Sidebar } from "~/components/Sidebar";
|
||||
import { BreadcrumbWrapper } from "~/components/BreadcrumbWrapper";
|
||||
import { BreadcrumbProvider } from "~/context/BreadcrumbContext";
|
||||
|
||||
import { inter, playfair } from "~/lib/fonts";
|
||||
import { description, name } from "~/lib/data";
|
||||
@@ -45,18 +46,20 @@ export default function RootLayout({ children }: React.PropsWithChildren) {
|
||||
/>
|
||||
)}
|
||||
|
||||
<Navigation />
|
||||
<div className="flex flex-1 pt-24 flex-col lg:flex-row">
|
||||
<Sidebar />
|
||||
<div className="flex-1 min-w-0 lg:pl-96">
|
||||
<div className="mx-auto max-w-screen-xl px-6 sm:px-8 lg:pl-0 lg:pr-8">
|
||||
<main className="pb-8 pt-4">
|
||||
<BreadcrumbWrapper />
|
||||
{children}
|
||||
</main>
|
||||
<BreadcrumbProvider>
|
||||
<Navigation />
|
||||
<div className="flex flex-1 pt-24 flex-col lg:flex-row">
|
||||
<Sidebar />
|
||||
<div className="flex-1 min-w-0 lg:pl-96">
|
||||
<div className="mx-auto max-w-screen-xl px-6 sm:px-8 lg:pl-0 lg:pr-8">
|
||||
<main className="pb-8 pt-4">
|
||||
<BreadcrumbWrapper />
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BreadcrumbProvider>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useBreadcrumb } from "~/context/BreadcrumbContext";
|
||||
|
||||
export function BreadcrumbUpdater({ title }: { title: string }) {
|
||||
const { setCustomTitle } = useBreadcrumb();
|
||||
|
||||
// Use effect to set title on mount and clear on unmount
|
||||
useEffect(() => {
|
||||
setCustomTitle(title);
|
||||
return () => setCustomTitle(null);
|
||||
}, [title, setCustomTitle]);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from "~/components/ui/breadcrumb";
|
||||
import { useBreadcrumb } from "~/context/BreadcrumbContext";
|
||||
|
||||
interface BreadcrumbItem {
|
||||
href: string;
|
||||
@@ -32,6 +33,7 @@ interface BreadcrumbItem {
|
||||
|
||||
export function PageBreadcrumb() {
|
||||
const pathname = usePathname();
|
||||
const { customTitle } = useBreadcrumb();
|
||||
|
||||
// Generate breadcrumb items based on current path
|
||||
const breadcrumbItems: BreadcrumbItem[] = [
|
||||
@@ -96,6 +98,15 @@ export function PageBreadcrumb() {
|
||||
icon = <File className="mr-1 h-3.5 w-3.5" />;
|
||||
}
|
||||
|
||||
// Override label for the last segment if customTitle is available and it's a blog post
|
||||
if (isLastSegment && customTitle && pathname.startsWith("/blog/")) {
|
||||
label = customTitle;
|
||||
// Truncate if too long (e.g., > 30 chars)
|
||||
if (label.length > 30) {
|
||||
label = label.substring(0, 30) + "...";
|
||||
}
|
||||
}
|
||||
|
||||
breadcrumbItems.push({
|
||||
href: currentPath,
|
||||
label,
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
"use client";
|
||||
|
||||
import React, { createContext, useContext, useState } from "react";
|
||||
|
||||
interface BreadcrumbContextType {
|
||||
customTitle: string | null;
|
||||
setCustomTitle: (title: string | null) => void;
|
||||
}
|
||||
|
||||
const BreadcrumbContext = createContext<BreadcrumbContextType | undefined>(undefined);
|
||||
|
||||
export function BreadcrumbProvider({ children }: { children: React.ReactNode }) {
|
||||
const [customTitle, setCustomTitle] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<BreadcrumbContext.Provider value={{ customTitle, setCustomTitle }}>
|
||||
{children}
|
||||
</BreadcrumbContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useBreadcrumb() {
|
||||
const context = useContext(BreadcrumbContext);
|
||||
if (context === undefined) {
|
||||
throw new Error("useBreadcrumb must be used within a BreadcrumbProvider");
|
||||
}
|
||||
return context;
|
||||
}
|
||||
Reference in New Issue
Block a user