feat: add navigation collapse functionality to layout (#5815)

* feat(layout): add collapsible navbar toggle in UI settings

* refactor(layout): adjust collapsed navbar styles

* docs: Changelog.md

---------

Co-authored-by: Slinetrac <realakayuki@gmail.com>
This commit is contained in:
Cactus
2025-12-19 18:15:20 +08:00
committed by GitHub
parent fc84dc561c
commit 57b17ab8d3
22 changed files with 151 additions and 4 deletions

View File

@@ -10,6 +10,7 @@
- 允许代理页面允许高级过滤搜索
- 备份设置页面新增导入备份按钮
- 允许修改通知弹窗位置(「界面设置」->「通知位置」)
- 支持收起导航栏(「界面设置」->「收起导航栏」)
</details>

View File

@@ -70,6 +70,9 @@ pub struct IVerge {
#[serde(skip_serializing_if = "Option::is_none")]
pub notice_position: Option<String>,
/// collapse navigation bar
pub collapse_navbar: Option<bool>,
/// sysproxy tray icon
pub sysproxy_tray_icon: Option<bool>,
@@ -396,6 +399,7 @@ impl IVerge {
tray_icon: Some("monochrome".into()),
menu_icon: Some("monochrome".into()),
notice_position: Some("top-right".into()),
collapse_navbar: Some(false),
common_tray_icon: Some(false),
sysproxy_tray_icon: Some(false),
tun_tray_icon: Some(false),
@@ -481,6 +485,7 @@ impl IVerge {
patch!(menu_icon);
patch!(menu_order);
patch!(notice_position);
patch!(collapse_navbar);
patch!(common_tray_icon);
patch!(sysproxy_tray_icon);
patch!(tun_tray_icon);

View File

@@ -158,3 +158,103 @@
}
}
}
.layout.layout--nav-collapsed {
--layout-nav-collapsed-width: 72px;
--layout-nav-item-size: 52px;
--layout-nav-item-radius: 12px;
--layout-nav-logo-size: 56px;
.layout-content {
&__left {
flex: 0 0 var(--layout-nav-collapsed-width) !important;
width: var(--layout-nav-collapsed-width) !important;
min-width: var(--layout-nav-collapsed-width) !important;
max-width: var(--layout-nav-collapsed-width) !important;
border-right: none !important;
.the-traffic {
display: none !important;
}
> .MuiBox-root {
display: none !important;
}
.the-logo {
flex: 0 0 var(--layout-nav-logo-size) !important;
height: var(--layout-nav-logo-size) !important;
margin: 8px 0 4px !important;
padding: 0 !important;
align-items: center !important;
justify-content: center !important;
}
.the-logo > div {
width: 100% !important;
height: 100% !important;
align-items: center !important;
justify-content: center !important;
}
.the-logo > div > svg:last-child {
display: none !important;
}
.the-logo .MuiSvgIcon-root {
margin: 0 !important;
}
.the-logo .the-newbtn {
display: none !important;
}
.the-menu {
width: var(--layout-nav-collapsed-width) !important;
overflow-x: hidden;
scrollbar-width: none;
&::-webkit-scrollbar {
width: 0;
height: 0;
}
li:last-child {
border-bottom: none;
}
.MuiListItem-root {
max-width: 100% !important;
padding: 0 !important;
}
.MuiListItemButton-root {
justify-content: center !important;
width: var(--layout-nav-item-size) !important;
height: var(--layout-nav-item-size) !important;
min-height: var(--layout-nav-item-size) !important;
max-width: var(--layout-nav-item-size) !important;
flex: 0 0 var(--layout-nav-item-size) !important;
flex-grow: 0 !important;
flex-shrink: 0 !important;
margin: 6px auto !important;
padding: 0 !important;
border-radius: var(--layout-nav-item-radius) !important;
}
.MuiListItemIcon-root {
min-width: auto !important;
margin: 0 !important;
margin-left: 0 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
.MuiListItemText-root {
display: none !important;
}
}
}
}
}

View File

@@ -33,10 +33,14 @@ export const LayoutItem = (props: Props) => {
const { to, children, icon, sortable } = props;
const { verge } = useVerge();
const { menu_icon } = verge ?? {};
const navCollapsed = verge?.collapse_navbar ?? false;
const resolved = useResolvedPath(to);
const match = useMatch({ path: resolved.pathname, end: true });
const navigate = useNavigate();
const effectiveMenuIcon =
navCollapsed && menu_icon === "disable" ? "monochrome" : menu_icon;
const { setNodeRef, attributes, listeners, style, isDragging, disabled } =
sortable ?? {};
@@ -84,9 +88,11 @@ export const LayoutItem = (props: Props) => {
};
},
]}
title={navCollapsed ? children : undefined}
aria-label={navCollapsed ? children : undefined}
onClick={() => navigate(to)}
>
{(menu_icon === "monochrome" || !menu_icon) && (
{(effectiveMenuIcon === "monochrome" || !effectiveMenuIcon) && (
<ListItemIcon
sx={{
color: "text.primary",
@@ -97,7 +103,7 @@ export const LayoutItem = (props: Props) => {
{icon[0]}
</ListItemIcon>
)}
{menu_icon === "colorful" && (
{effectiveMenuIcon === "colorful" && (
<ListItemIcon sx={{ cursor: draggable ? "grab" : "inherit" }}>
{icon[1]}
</ListItemIcon>
@@ -105,7 +111,7 @@ export const LayoutItem = (props: Props) => {
<ListItemText
sx={{
textAlign: "center",
marginLeft: menu_icon === "disable" ? "" : "-35px",
marginLeft: effectiveMenuIcon === "disable" ? "" : "-35px",
}}
primary={children}
/>

View File

@@ -338,6 +338,24 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
</GuardState>
</Item>
<Item>
<ListItemText
primary={t(
"settings.components.verge.layout.fields.collapseNavBar",
)}
/>
<GuardState
value={verge?.collapse_navbar ?? false}
valueProps="checked"
onCatch={onError}
onFormat={onSwitchFormat}
onChange={(e) => onChangeData({ collapse_navbar: e })}
onGuard={(e) => patchVerge({ collapse_navbar: e })}
>
<Switch edge="end" />
</GuardState>
</Item>
{OS === "macos" && (
<Item>
<ListItemText

View File

@@ -209,6 +209,7 @@
"hoverNavigator": "Hover Jump Navigator",
"hoverNavigatorDelay": "Hover Jump Navigator Delay",
"navIcon": "أيقونة التنقل",
"collapseNavBar": "طي شريط التنقل",
"trayIcon": "أيقونة شريط المهام",
"showProxyGroupsInline": "Show Proxy Groups Inline",
"commonTrayIcon": "أيقونة شريط مهام عامة",

View File

@@ -209,6 +209,7 @@
"hoverNavigator": "Hover Jump Navigator",
"hoverNavigatorDelay": "Hover Jump Navigator Delay",
"navIcon": "Navigationsleiste-Symbol",
"collapseNavBar": "Navigationsleiste einklappen",
"trayIcon": "Tray-Symbol",
"showProxyGroupsInline": "Show Proxy Groups Inline",
"commonTrayIcon": "Standard-Tray-Symbol",

View File

@@ -209,6 +209,7 @@
"hoverNavigator": "Hover Jump Navigator",
"hoverNavigatorDelay": "Hover Jump Navigator Delay",
"navIcon": "Nav Icon",
"collapseNavBar": "Collapse Navigation Bar",
"trayIcon": "Tray Icon",
"showProxyGroupsInline": "Show Proxy Groups Inline",
"commonTrayIcon": "Common Tray Icon",

View File

@@ -209,6 +209,7 @@
"hoverNavigator": "Hover Jump Navigator",
"hoverNavigatorDelay": "Hover Jump Navigator Delay",
"navIcon": "Icono de la barra de navegación",
"collapseNavBar": "Colapsar barra de navegación",
"trayIcon": "Icono de la bandeja",
"showProxyGroupsInline": "Show Proxy Groups Inline",
"commonTrayIcon": "Icono de bandeja común",

View File

@@ -209,6 +209,7 @@
"hoverNavigator": "Hover Jump Navigator",
"hoverNavigatorDelay": "Hover Jump Navigator Delay",
"navIcon": "آیکون ناوبری",
"collapseNavBar": "جمع کردن نوار ناوبری",
"trayIcon": "آیکون سینی سیستم",
"showProxyGroupsInline": "Show Proxy Groups Inline",
"commonTrayIcon": "آیکون مشترک سینی سیستم",

View File

@@ -209,6 +209,7 @@
"hoverNavigator": "Hover Jump Navigator",
"hoverNavigatorDelay": "Hover Jump Navigator Delay",
"navIcon": "Ikon Navigasi",
"collapseNavBar": "Ciutkan bilah navigasi",
"trayIcon": "Ikon Tray",
"showProxyGroupsInline": "Show Proxy Groups Inline",
"commonTrayIcon": "Ikon Tray Umum",

View File

@@ -209,6 +209,7 @@
"hoverNavigator": "Hover Jump Navigator",
"hoverNavigatorDelay": "Hover Jump Navigator Delay",
"navIcon": "ナビゲーションバーアイコン",
"collapseNavBar": "ナビゲーションバーを折りたたむ",
"trayIcon": "トレイアイコン",
"showProxyGroupsInline": "Show Proxy Groups Inline",
"commonTrayIcon": "通常のトレイアイコン",

View File

@@ -209,6 +209,7 @@
"hoverNavigator": "호버 점프 내비게이터",
"hoverNavigatorDelay": "호버 점프 내비게이터 지연",
"navIcon": "내비게이션 아이콘",
"collapseNavBar": "내비게이션 바 접기",
"trayIcon": "트레이 아이콘",
"showProxyGroupsInline": "프록시 그룹 인라인 표시",
"commonTrayIcon": "공용 트레이 아이콘",

View File

@@ -209,6 +209,7 @@
"hoverNavigator": "Hover Jump Navigator",
"hoverNavigatorDelay": "Hover Jump Navigator Delay",
"navIcon": "Иконки навигации",
"collapseNavBar": "Свернуть панель навигации",
"trayIcon": "Иконка в трее",
"showProxyGroupsInline": "Show Proxy Groups Inline",
"commonTrayIcon": "Общий значок в трее",

View File

@@ -209,6 +209,7 @@
"hoverNavigator": "Hover Jump Navigator",
"hoverNavigatorDelay": "Hover Jump Navigator Delay",
"navIcon": "Gezinme Simgesi",
"collapseNavBar": "Gezinme çubuğunu daralt",
"trayIcon": "Tepsi Simgesi",
"showProxyGroupsInline": "Show Proxy Groups Inline",
"commonTrayIcon": "Genel Tepsi Simgesi",

View File

@@ -209,6 +209,7 @@
"hoverNavigator": "Hover Jump Navigator",
"hoverNavigatorDelay": "Hover Jump Navigator Delay",
"navIcon": "Навигация иконкасы",
"collapseNavBar": "Навигация панелен җыю",
"trayIcon": "Трей иконкасы",
"showProxyGroupsInline": "Show Proxy Groups Inline",
"commonTrayIcon": "Гомуми трей иконкасы",

View File

@@ -209,6 +209,7 @@
"hoverNavigator": "悬浮跳转导航",
"hoverNavigatorDelay": "悬浮跳转导航延迟",
"navIcon": "导航栏图标",
"collapseNavBar": "收起导航栏",
"trayIcon": "托盘图标",
"showProxyGroupsInline": "将代理组显示在托盘一级菜单",
"commonTrayIcon": "常规托盘图标",

View File

@@ -209,6 +209,7 @@
"hoverNavigator": "懸浮跳轉導航",
"hoverNavigatorDelay": "懸浮跳轉導航延遲",
"navIcon": "導覽列圖示",
"collapseNavBar": "收起導覽列",
"trayIcon": "系統匣圖示",
"showProxyGroupsInline": "將代理組顯示在系統匣一級選單",
"commonTrayIcon": "一般系統匣圖示",

View File

@@ -117,6 +117,7 @@ const Layout = () => {
const { theme } = useCustomTheme();
const { verge, mutateVerge, patchVerge } = useVerge();
const { language } = verge ?? {};
const navCollapsed = verge?.collapse_navbar ?? false;
const { switchLanguage } = useI18n();
const navigate = useNavigate();
const themeReady = useMemo(() => Boolean(theme), [theme]);
@@ -281,7 +282,7 @@ const Layout = () => {
<Paper
square
elevation={0}
className={`${OS} layout`}
className={`${OS} layout${navCollapsed ? " layout--nav-collapsed" : ""}`}
style={{
borderTopLeftRadius: "0px",
borderTopRightRadius: "0px",

View File

@@ -429,6 +429,7 @@ export const translationKeys = [
"settings.components.verge.layout.fields.hoverNavigator",
"settings.components.verge.layout.fields.hoverNavigatorDelay",
"settings.components.verge.layout.fields.navIcon",
"settings.components.verge.layout.fields.collapseNavBar",
"settings.components.verge.layout.fields.trayIcon",
"settings.components.verge.layout.fields.showProxyGroupsInline",
"settings.components.verge.layout.fields.commonTrayIcon",

View File

@@ -616,6 +616,7 @@ export interface TranslationResources {
};
layout: {
fields: {
collapseNavBar: string;
commonTrayIcon: string;
enableTrayIcon: string;
enableTraySpeed: string;

View File

@@ -815,6 +815,7 @@ interface IVergeConfig {
menu_icon?: "monochrome" | "colorful" | "disable";
menu_order?: string[];
notice_position?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
collapse_navbar?: boolean;
tray_icon?: "monochrome" | "colorful";
common_tray_icon?: boolean;
sysproxy_tray_icon?: boolean;