| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 |
- 'use client';
- import { useState, useCallback } from 'react';
- import Styles from '../styles/layout.module.scss';
- import Header from './Header';
- import Footer from './Footer'
- import ChannelSidebar from '@/app/component/channel/ChannelSidebar';
- import MarketSidebar from '@/app/component/MarketSidebar';
- import { ChannelListItem } from '@/types/channel';
- import { FEATURE_CHANNEL } from '@/constants/features';
- type Props = {
- children: React.ReactNode;
- initialChannels?: ChannelListItem[];
- // 우측 유틸 슬롯 (D6 3열 그리드) — 제공 페이지만 desktop 이상에서 3열 렌더
- utility?: React.ReactNode;
- };
- export default function Layout({ children, initialChannels, utility }: Props) {
- const [sidebarOpen, setSidebarOpen] = useState(false);
- const toggleSidebar = useCallback(() => {
- setSidebarOpen((prev) => !prev);
- }, []);
- const closeOverlay = useCallback(() => {
- setSidebarOpen(false);
- }, []);
- return (
- <>
- {/* 접근성: 스킵 링크 — Tab 첫 포커스 시 노출 */}
- <a href='#main' className={Styles.skipLink}>본문 바로가기</a>
- <div id='container' className={`${Styles.container}${sidebarOpen ? ` ${Styles.sidebarOpen}` : ''}${utility ? ` ${Styles.hasUtility}` : ''}`}>
- <Header sidebarOpen={sidebarOpen} onToggle={toggleSidebar} />
- {/* 모바일 오버레이 */}
- <div className={Styles.overlay} onClick={closeOverlay} />
- {/* 좌측 사이드바 — 채널 기능 ON: 채널 목록 / OFF: 관심종목 자리표시 (MarketSidebar) */}
- <aside id='aside' className={Styles.aside} aria-label='사이드바'>
- {FEATURE_CHANNEL ? (
- <ChannelSidebar initialChannels={initialChannels} />
- ) : (
- <MarketSidebar />
- )}
- </aside>
- {/* 메인 내용 */}
- <main id='main' className={`${Styles.main} relative`}>
- {children}
- </main>
- {/* 우측 유틸 슬롯 (desktop 이상 전용) */}
- {utility && (
- <aside id='utility' className={Styles.utility} aria-label='보조 콘텐츠'>
- {utility}
- </aside>
- )}
- {/* 하단 */}
- <Footer />
- </div>
- </>
- );
- }
|