From 056a773fb7254d5d3325461ee2bbd33950811df8 Mon Sep 17 00:00:00 2001 From: Macbook Date: Sun, 14 Jun 2026 17:16:25 +0900 Subject: [PATCH] =?UTF-8?q?=EC=95=88=EB=93=9C=EB=A1=9C=EC=9D=B4=EB=93=9C?= =?UTF-8?q?=20=EC=95=B1=20=EB=8B=A4=EC=9A=B4=EB=A1=9C=EB=93=9C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.css | 97 +++++++++++- frontend/src/components/TopMenuBar.tsx | 63 +++++--- .../src/components/TopMenuBarNavGroups.tsx | 148 ++++++++++++++++++ 3 files changed, 278 insertions(+), 30 deletions(-) create mode 100644 frontend/src/components/TopMenuBarNavGroups.tsx diff --git a/frontend/src/App.css b/frontend/src/App.css index 9d9188e..f3d8c96 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -187,6 +187,7 @@ html.theme-blue { padding: 0 12px; gap: 4px; z-index: 200; + position: relative; } /* 로고 */ @@ -227,6 +228,85 @@ html.theme-blue { flex: 1; min-width: 0; } +.tmb-nav--groups { + flex: 0 1 auto; + min-width: 0; +} +.tmb-nav-group { + position: relative; + flex-shrink: 0; +} +.tmb-nav-group-btn { + padding-right: 8px; +} +.tmb-nav-group-btn--open { + background: var(--bg4); + color: var(--text); +} +.tmb-nav-chevron { + display: inline-flex; + align-items: center; + opacity: 0.65; + margin-left: 1px; + transition: transform 0.15s ease; +} +.tmb-nav-chevron--open { + transform: rotate(180deg); +} +.tmb-nav-dropdown { + position: absolute; + top: calc(100% + 6px); + left: 0; + min-width: 168px; + padding: 4px; + background: var(--bg2); + border: 1px solid var(--border); + border-radius: 8px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28); + z-index: 400; +} +.tmb-nav-dropdown-item { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + min-height: 36px; + padding: 0 10px; + border: none; + border-radius: 6px; + background: none; + color: var(--text2); + font-size: 13px; + font-weight: 500; + text-align: left; + cursor: pointer; + transition: background 0.12s, color 0.12s; +} +.tmb-nav-dropdown-item:hover { + background: var(--bg4); + color: var(--text); +} +.tmb-nav-dropdown-item--active { + background: rgba(var(--accent-rgb, 122, 162, 247), 0.12); + color: var(--accent); +} +.app.dark .tmb-nav-dropdown-item--active { background: rgba(122, 162, 247, 0.13); } +.app.blue .tmb-nav-dropdown-item--active { background: rgba(94, 181, 255, 0.13); } +.app.light .tmb-nav-dropdown-item--active { background: rgba(25, 118, 210, 0.10); } +.tmb-nav-dropdown-icon { + display: flex; + align-items: center; + flex-shrink: 0; + opacity: 0.85; +} +.tmb-nav-dropdown-label { + flex: 1; + white-space: nowrap; +} +.tmb-notify-badge--dropdown { + position: static; + margin-left: auto; +} .tmb-nav-btn { position: relative; /* 알림 뱃지 absolute 기준점 */ display: inline-flex; @@ -269,6 +349,7 @@ html.theme-blue { align-items: center; gap: 0; flex-shrink: 0; + margin-left: auto; } /* 테마 버튼 */ @@ -12878,20 +12959,24 @@ html.theme-light .tam-disclaimer { color: #90a4ae; } } .app--mobile .top-menubar::-webkit-scrollbar { display: none; } .app--mobile .tmb-logo-text { display: none; } -.app--mobile .tmb-nav { +.app--mobile .tmb-nav--groups { flex: 1; min-width: 0; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; } -.app--mobile .tmb-nav::-webkit-scrollbar { display: none; } -.app--mobile .tmb-nav-btn { - flex-shrink: 0; - padding: 6px 10px; +.app--mobile .tmb-nav-group-btn .tmb-nav-label { + display: inline; font-size: 11px; } -.app--mobile .tmb-nav-label { display: none; } +.app--mobile .tmb-nav-dropdown { + min-width: 152px; +} +.app--mobile .tmb-nav-dropdown-item { + min-height: 40px; + font-size: 12px; +} .app--mobile .tmb-theme-label, .app--mobile .tmb-login-btn span { display: none; } .app--mobile .tmb-right { flex-shrink: 0; } diff --git a/frontend/src/components/TopMenuBar.tsx b/frontend/src/components/TopMenuBar.tsx index c4b528a..9116de6 100644 --- a/frontend/src/components/TopMenuBar.tsx +++ b/frontend/src/components/TopMenuBar.tsx @@ -2,15 +2,16 @@ * 최상단 글로벌 메뉴바 * * 레이아웃: - * [로고] | [대시보드] [실시간차트] [전략편집기] … | [테마토글] [로그인] + * [로고] | [그룹메뉴 ▾] … | [테마토글] [로그인] */ -import React, { memo, useCallback, useEffect, useState } from 'react'; +import React, { memo, useEffect, useState } from 'react'; import type { Theme } from '../types'; import type { AuthSession } from '../utils/auth'; import type { TradeAlertPopupLayout, TradeAlertPopupPosition } from '../utils/tradeAlertPopupLayout'; import { canAccessMenu } from '../utils/permissions'; import { isDesktop } from '../utils/platform'; import DesktopUpdateModal from './DesktopUpdateModal'; +import TopMenuBarNavGroups from './TopMenuBarNavGroups'; export type MenuPage = 'dashboard' | 'chart' | 'paper' | 'virtual' | 'trend-search' | 'verification-board' | 'strategy' | 'strategy-editor' | 'strategy-evaluation' | 'backtest' | 'analysis-report' | 'notifications' | 'settings' | 'widget-dashboard'; @@ -244,6 +245,34 @@ const MENU_ITEMS: { page: MenuPage; label: string; icon: React.ReactNode }[] = [ { page: 'verification-board', label: '검증게시판', icon: }, ]; +/** 기능별 그룹 — 클릭 시 하단 드롭다운 */ +const MENU_GROUPS = [ + { + id: 'home', + label: '홈·분석', + icon: , + pages: ['dashboard', 'widget-dashboard', 'analysis-report'] as MenuPage[], + }, + { + id: 'trading', + label: '차트·매매', + icon: , + pages: ['chart', 'paper', 'virtual', 'trend-search'] as MenuPage[], + }, + { + id: 'strategy', + label: '전략', + icon: , + pages: ['strategy-editor', 'strategy-evaluation', 'backtest'] as MenuPage[], + }, + { + id: 'system', + label: '알림·설정', + icon: , + pages: ['notifications', 'verification-board', 'settings'] as MenuPage[], + }, +]; + export const TopMenuBar = memo(function TopMenuBar({ activePage, theme, onPage, onTheme, tradeNotifyUnread = 0, @@ -293,28 +322,14 @@ export const TopMenuBar = memo(function TopMenuBar({
- {/* 내비게이션 메뉴 */} - + {/* 기능별 그룹 메뉴 */} +