MarketSidebar.tsx 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. 'use client';
  2. import '@/app/styles/market-sidebar.scss';
  3. import { useCallback, useEffect, useState } from 'react';
  4. import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
  5. import { faAnglesLeft, faAnglesRight, faStar, faSun, faMoon } from '@fortawesome/free-solid-svg-icons';
  6. import useTheme from '@/hooks/useTheme';
  7. const STORAGE_KEY = 'az-sidebar-collapsed';
  8. const WIDTH_EXPANDED = '220px';
  9. const WIDTH_COLLAPSED = '64px';
  10. // 좌측 사이드바 자리표시 (D6 그리드 셸) — 관심종목/게시판 즐겨찾기 본구현은 후속(MarketSidebar 본체)
  11. // 접기 메커니즘은 ChannelSidebar 와 동일: html 루트의 --antooza-sidebar-width 로 layout grid 열 폭 반응
  12. export default function MarketSidebar()
  13. {
  14. // 자리표시 단계라 기본 접힘(64px 아이콘 레일) — 본문 우선. 토글 시 localStorage 영속.
  15. const [collapsed, setCollapsed] = useState(true);
  16. const { toggleTheme, isDark } = useTheme();
  17. // 최초 mount 시 localStorage에서 collapsed 상태 복원
  18. useEffect(() => {
  19. try {
  20. const saved = window.localStorage.getItem(STORAGE_KEY);
  21. if (saved === 'false') {
  22. setCollapsed(false);
  23. }
  24. } catch {}
  25. }, []);
  26. // collapsed 변경 시 html 루트 CSS 변수 동기화
  27. useEffect(() => {
  28. const root = document.documentElement;
  29. root.style.setProperty('--antooza-sidebar-width', collapsed ? WIDTH_COLLAPSED : WIDTH_EXPANDED);
  30. try {
  31. window.localStorage.setItem(STORAGE_KEY, String(collapsed));
  32. } catch {}
  33. return () => {
  34. // unmount 시 원복 (breakpoint 별 --az-sidebar-w 기본값으로 복귀)
  35. root.style.removeProperty('--antooza-sidebar-width');
  36. };
  37. }, [collapsed]);
  38. const handleToggleCollapse = useCallback(() => {
  39. setCollapsed(prev => !prev);
  40. }, []);
  41. return (
  42. <div className={`market-sidebar${collapsed ? ' market-sidebar--collapsed' : ''}`}>
  43. {/* 헤더: 타이틀 + 접기 토글 */}
  44. <div className='market-sidebar__header'>
  45. {!collapsed && (
  46. <span className='market-sidebar__title'>관심종목</span>
  47. )}
  48. <button
  49. type='button'
  50. className='market-sidebar__collapse-btn'
  51. onClick={handleToggleCollapse}
  52. aria-label={collapsed ? '사이드바 펼치기' : '사이드바 접기'}
  53. aria-expanded={!collapsed}
  54. title={collapsed ? '사이드바 펼치기' : '사이드바 접기'}
  55. >
  56. <FontAwesomeIcon icon={collapsed ? faAnglesRight : faAnglesLeft} />
  57. </button>
  58. </div>
  59. {/* 자리표시 본문 */}
  60. <div className='market-sidebar__body'>
  61. {collapsed ? (
  62. <div className='market-sidebar__icon-placeholder' title='관심종목 (준비 중)' aria-hidden='true'>
  63. <FontAwesomeIcon icon={faStar} />
  64. </div>
  65. ) : (
  66. <p className='market-sidebar__empty'>관심종목 기능을 준비 중입니다</p>
  67. )}
  68. </div>
  69. {/* 하단: 테마 전환 (기존 ChannelSidebar 토글이 feature-off 로 미노출되어 여기서 제공) */}
  70. <div className='market-sidebar__footer'>
  71. <button type='button' className='market-sidebar__icon-btn' onClick={toggleTheme} aria-label={isDark ? '라이트 모드' : '다크 모드'} title={isDark ? '라이트 모드' : '다크 모드'}>
  72. <FontAwesomeIcon icon={isDark ? faSun : faMoon} />
  73. </button>
  74. </div>
  75. </div>
  76. );
  77. }