- 引入 ConfigError/TemplateError/BuildError/FileError 与 wrapAsyncError,统一错误输出 - generator 入口接入 wrapAsyncError,确保命令行执行路径一致 - 兜底逻辑使用 instanceof,保留 BuildError/TemplateError 上下文信息 - 合并格式化提交(仅缩进/换行调整)
181 lines
5.4 KiB
JavaScript
181 lines
5.4 KiB
JavaScript
module.exports = function initUi(state, dom) {
|
||
const {
|
||
searchInput,
|
||
searchBox,
|
||
menuToggle,
|
||
searchToggle,
|
||
sidebar,
|
||
searchContainer,
|
||
overlay,
|
||
sidebarToggle,
|
||
content,
|
||
themeToggle,
|
||
themeIcon,
|
||
} = dom;
|
||
|
||
// 移除预加载类,允许 CSS 过渡效果
|
||
document.documentElement.classList.remove('preload');
|
||
|
||
// 应用从 localStorage 读取的主题设置(预加载阶段已写入 class)
|
||
if (document.documentElement.classList.contains('theme-preload')) {
|
||
document.documentElement.classList.remove('theme-preload');
|
||
document.body.classList.add('light-theme');
|
||
state.isLightTheme = true;
|
||
}
|
||
|
||
// 应用从 localStorage 读取的侧边栏状态(预加载阶段已写入 class)
|
||
if (document.documentElement.classList.contains('sidebar-collapsed-preload')) {
|
||
document.documentElement.classList.remove('sidebar-collapsed-preload');
|
||
sidebar.classList.add('collapsed');
|
||
content.classList.add('expanded');
|
||
state.isSidebarCollapsed = true;
|
||
}
|
||
|
||
// 即时移除 loading 类,确保侧边栏可见
|
||
document.body.classList.remove('loading');
|
||
document.body.classList.add('loaded');
|
||
|
||
function isMobile() {
|
||
return window.innerWidth <= 768;
|
||
}
|
||
|
||
// 侧边栏折叠功能
|
||
function toggleSidebarCollapse() {
|
||
// 仅在交互时启用布局相关动画,避免首屏闪烁
|
||
document.documentElement.classList.add('with-anim');
|
||
|
||
state.isSidebarCollapsed = !state.isSidebarCollapsed;
|
||
|
||
// 使用 requestAnimationFrame 确保平滑过渡
|
||
requestAnimationFrame(() => {
|
||
sidebar.classList.toggle('collapsed', state.isSidebarCollapsed);
|
||
content.classList.toggle('expanded', state.isSidebarCollapsed);
|
||
|
||
// 保存折叠状态到 localStorage
|
||
localStorage.setItem('sidebarCollapsed', state.isSidebarCollapsed ? 'true' : 'false');
|
||
});
|
||
}
|
||
|
||
// 初始化侧边栏折叠状态 - 已在页面加载前处理,此处仅完成图标状态初始化等次要任务
|
||
function initSidebarState() {
|
||
// 从 localStorage 获取侧边栏状态
|
||
const savedState = localStorage.getItem('sidebarCollapsed');
|
||
|
||
// 图标状态与折叠状态保持一致
|
||
if (savedState === 'true' && !isMobile()) {
|
||
state.isSidebarCollapsed = true;
|
||
} else {
|
||
state.isSidebarCollapsed = false;
|
||
}
|
||
}
|
||
|
||
// 主题切换功能
|
||
function toggleTheme() {
|
||
state.isLightTheme = !state.isLightTheme;
|
||
document.body.classList.toggle('light-theme', state.isLightTheme);
|
||
|
||
// 更新图标
|
||
if (state.isLightTheme) {
|
||
themeIcon.classList.remove('fa-moon');
|
||
themeIcon.classList.add('fa-sun');
|
||
} else {
|
||
themeIcon.classList.remove('fa-sun');
|
||
themeIcon.classList.add('fa-moon');
|
||
}
|
||
|
||
// 保存主题偏好到 localStorage
|
||
localStorage.setItem('theme', state.isLightTheme ? 'light' : 'dark');
|
||
}
|
||
|
||
// 初始化主题 - 已在页面加载前处理,此处仅完成图标状态初始化等次要任务
|
||
function initTheme() {
|
||
// 从 localStorage 获取主题偏好
|
||
const savedTheme = localStorage.getItem('theme');
|
||
|
||
// 更新图标状态以匹配当前主题
|
||
if (savedTheme === 'light') {
|
||
state.isLightTheme = true;
|
||
themeIcon.classList.remove('fa-moon');
|
||
themeIcon.classList.add('fa-sun');
|
||
} else {
|
||
state.isLightTheme = false;
|
||
themeIcon.classList.remove('fa-sun');
|
||
themeIcon.classList.add('fa-moon');
|
||
}
|
||
}
|
||
|
||
// 移动端菜单切换
|
||
function toggleSidebar() {
|
||
state.isSidebarOpen = !state.isSidebarOpen;
|
||
sidebar.classList.toggle('active', state.isSidebarOpen);
|
||
overlay.classList.toggle('active', state.isSidebarOpen);
|
||
}
|
||
|
||
// 移动端:搜索框常驻显示(CSS 控制),无需“搜索面板”开关;点击仅聚焦输入框
|
||
function toggleSearch() {
|
||
searchInput && searchInput.focus();
|
||
}
|
||
|
||
// 关闭所有移动端面板
|
||
function closeAllPanels() {
|
||
if (state.isSidebarOpen) {
|
||
toggleSidebar();
|
||
}
|
||
}
|
||
|
||
// 侧边栏折叠按钮点击事件
|
||
if (sidebarToggle) {
|
||
sidebarToggle.addEventListener('click', toggleSidebarCollapse);
|
||
}
|
||
|
||
// 主题切换按钮点击事件
|
||
themeToggle.addEventListener('click', toggleTheme);
|
||
|
||
// 移动端事件监听
|
||
menuToggle.addEventListener('click', toggleSidebar);
|
||
searchToggle.addEventListener('click', toggleSearch);
|
||
overlay.addEventListener('click', closeAllPanels);
|
||
|
||
// 全局快捷键:Ctrl/Cmd + K 聚焦搜索
|
||
document.addEventListener('keydown', (e) => {
|
||
const key = (e.key || '').toLowerCase();
|
||
if (key !== 'k') return;
|
||
if ((!e.ctrlKey && !e.metaKey) || e.altKey) return;
|
||
|
||
const target = e.target;
|
||
const isTypingTarget =
|
||
target &&
|
||
(target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable);
|
||
|
||
if (isTypingTarget && target !== searchInput) return;
|
||
|
||
e.preventDefault();
|
||
|
||
searchInput && searchInput.focus();
|
||
});
|
||
|
||
// 窗口大小改变时处理
|
||
window.addEventListener('resize', () => {
|
||
if (!isMobile()) {
|
||
sidebar.classList.remove('active');
|
||
searchContainer.classList.remove('active');
|
||
overlay.classList.remove('active');
|
||
state.isSidebarOpen = false;
|
||
} else {
|
||
// 在移动设备下,重置侧边栏折叠状态
|
||
sidebar.classList.remove('collapsed');
|
||
content.classList.remove('expanded');
|
||
}
|
||
});
|
||
|
||
// 仅用于静态检查:确保未用变量不被 lint 报错(未来可用于搜索 UI 状态)
|
||
void searchBox;
|
||
|
||
return {
|
||
isMobile,
|
||
closeAllPanels,
|
||
initTheme,
|
||
initSidebarState,
|
||
};
|
||
};
|