'use client';
import { useState, useEffect } from 'react';
import { createPortal } from 'react-dom';
import { FiList, FiX } from 'react-icons/fi';
import { PostToc } from './post-toc';
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export function PostLayout({ children, hasToc = true, contentKey }: { children: React.ReactNode; hasToc?: boolean; contentKey?: string }) {
const [isTocOpen, setIsTocOpen] = useState(false); // Default closed on mobile
const [isDesktopTocOpen, setIsDesktopTocOpen] = useState(hasToc); // Separate state for desktop
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
// Lock body scroll when mobile TOC is open
useEffect(() => {
if (isTocOpen) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = '';
}
return () => {
document.body.style.overflow = '';
};
}, [isTocOpen]);
const mobileToc = hasToc && mounted
? createPortal(
<>
{/* Backdrop */}
setIsTocOpen(false)}
aria-hidden="true"
/>
{/* Drawer */}
{/* Handle / Header */}
setIsTocOpen(false)}>
目錄
{/* Content */}
setIsTocOpen(false)}
showTitle={false}
className="w-full"
/>
>,
document.body
)
: null;
const tocButton = hasToc && mounted ? (
) : null;
const desktopTocButton = hasToc && mounted ? (
) : null;
return (
{/* Main Content Area */}
{/* Desktop Sidebar (TOC) */}
{/* Mobile TOC Overlay */}
{mobileToc}
{/* Toggle Buttons - Rendered via Portal */}
{mounted && createPortal(
<>
{tocButton}
{desktopTocButton}
>,
document.body
)}
);
}