- 新增布局管理器支持拖拽调整面板宽度 - 新增状态管理器实现布局持久化 - 重构前端页面结构优化四栏式布局 - 新增 Jest 测试框架和完整测试用例 - 完善项目文档(PRD/FD/TDD/TODO/LOG) - 清理 Python 缓存文件并更新 .gitignore Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
231 lines
6.8 KiB
CSS
231 lines
6.8 KiB
CSS
/**
|
||
* 可调整布局系统(基础样式)
|
||
*
|
||
* 说明:
|
||
* - 使用 CSS Grid + CSS 变量控制三列侧边栏宽度(分组/账号/邮件列表)与详情区域自适应。
|
||
* - resizer / 折叠相关样式在此文件集中管理,避免污染现有 main.css。
|
||
* - 该文件将被在 main.css 之后引入,以覆盖旧的 flex 固定宽度布局。
|
||
*/
|
||
|
||
:root {
|
||
/* ===================== 面板默认宽度 ===================== */
|
||
--group-panel-width: 200px;
|
||
--account-panel-width: 260px;
|
||
--email-list-panel-width: 380px;
|
||
|
||
/* ===================== 折叠相关宽度 ===================== */
|
||
--panel-collapsed-width: 0px;
|
||
--panel-collapsed-indicator-width: 48px;
|
||
|
||
/* ===================== 宽度限制(用于 Grid 与 JS 计算) ===================== */
|
||
--group-panel-min-width: 150px;
|
||
--group-panel-max-width: 400px;
|
||
--account-panel-min-width: 180px;
|
||
--account-panel-max-width: 500px;
|
||
--email-list-panel-min-width: 280px;
|
||
--email-list-panel-max-width: 600px;
|
||
--email-detail-panel-min-width: 400px;
|
||
|
||
/* ===================== Grid 生效宽度(折叠时可被置为 0,保存宽度不丢失) ===================== */
|
||
--group-panel-layout-width: var(--group-panel-width);
|
||
--account-panel-layout-width: var(--account-panel-width);
|
||
--email-list-panel-layout-width: var(--email-list-panel-width);
|
||
--group-panel-layout-min-width: var(--group-panel-min-width);
|
||
--account-panel-layout-min-width: var(--account-panel-min-width);
|
||
--email-list-panel-layout-min-width: var(--email-list-panel-min-width);
|
||
|
||
/* ===================== 交互与动画 ===================== */
|
||
--resizer-width: 4px;
|
||
--panel-transition-duration: 200ms;
|
||
--panel-transition-timing-function: ease;
|
||
--collapsed-indicator-width: var(--panel-collapsed-indicator-width);
|
||
}
|
||
|
||
/* ===================== Grid 布局(覆盖 main.css) ===================== */
|
||
.main-container {
|
||
display: grid;
|
||
grid-template-columns:
|
||
minmax(var(--group-panel-layout-min-width), var(--group-panel-layout-width))
|
||
minmax(var(--account-panel-layout-min-width), var(--account-panel-layout-width))
|
||
minmax(var(--email-list-panel-layout-min-width), var(--email-list-panel-layout-width))
|
||
minmax(var(--email-detail-panel-min-width), 1fr);
|
||
grid-template-rows: 1fr;
|
||
/* 防止折叠时面板内容溢出把 body 撑出滚动条(会导致“指示器高度异常/过长”观感) */
|
||
overflow: hidden;
|
||
transition: grid-template-columns var(--panel-transition-duration)
|
||
var(--panel-transition-timing-function);
|
||
will-change: grid-template-columns;
|
||
}
|
||
|
||
/* ===================== 状态恢复时禁用动画(避免闪烁) ===================== */
|
||
.layout-no-transition .main-container {
|
||
transition: none !important;
|
||
}
|
||
|
||
.layout-no-transition .resizable-panel > :not(.collapsed-indicator) {
|
||
transition: none !important;
|
||
}
|
||
|
||
/* 让 Grid 子项允许收缩,避免内容撑破布局 */
|
||
.main-container > * {
|
||
min-width: 0;
|
||
min-height: 0;
|
||
}
|
||
|
||
/* ===================== Resizable 面板基础样式 ===================== */
|
||
.resizable-panel {
|
||
position: relative;
|
||
/* 默认层级:保持低于 modal 等全局浮层 */
|
||
z-index: 1;
|
||
}
|
||
|
||
/* ===================== Resizer 样式 ===================== */
|
||
.resizer {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
width: var(--resizer-width);
|
||
height: 100%;
|
||
cursor: col-resize;
|
||
z-index: 20;
|
||
touch-action: none;
|
||
}
|
||
|
||
.resizer:hover,
|
||
.resizer:focus,
|
||
.resizer.active {
|
||
background-color: rgba(26, 26, 26, 0.1);
|
||
box-shadow: inset 0 0 0 2px #1a1a1a;
|
||
}
|
||
|
||
.resizer:focus-visible {
|
||
outline: 2px solid #1a1a1a;
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* ===================== 折叠状态(基础) ===================== */
|
||
.resizable-panel {
|
||
will-change: opacity;
|
||
}
|
||
|
||
/* 折叠时仅隐藏面板内容,指示器仍需可见 */
|
||
.resizable-panel > :not(.collapsed-indicator) {
|
||
transition: opacity var(--panel-transition-duration)
|
||
var(--panel-transition-timing-function);
|
||
}
|
||
|
||
.resizable-panel.collapsed > :not(.collapsed-indicator) {
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
/*
|
||
* 关键修复:
|
||
* - 折叠时列宽为 0,若仅用 opacity 隐藏,面板内文本/控件会在 0 宽下强制换行,
|
||
* 仍参与布局计算并可能把页面撑出滚动条,导致指示器“看起来过长”。
|
||
* - 直接 display:none 让隐藏内容不参与布局与滚动高度计算。
|
||
*/
|
||
display: none;
|
||
}
|
||
|
||
.resizable-panel.collapsed {
|
||
overflow: visible;
|
||
/*
|
||
* 关键修复:
|
||
* - 折叠后 Grid 列宽会变为 0,指示器需要“溢出”到右侧可见区域。
|
||
* - 由于 Grid 子项的绘制顺序受 DOM 影响(后面的面板会盖住前面的面板),
|
||
* 当多面板折叠时,指示器可能被邮件详情面板覆盖导致“消失且无法展开”。
|
||
* - 提升折叠面板的 z-index,并强制其宽度为 0,确保指示器可见且不挡住右侧交互。
|
||
*/
|
||
z-index: 200;
|
||
width: 0 !important;
|
||
min-width: 0 !important;
|
||
max-width: 0 !important;
|
||
}
|
||
|
||
.resizable-panel.collapsed .resizer {
|
||
display: none;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* ===================== 折叠指示器(48px,竖排文字) ===================== */
|
||
.collapsed-indicator {
|
||
display: none;
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: var(--collapsed-indicator-width);
|
||
height: 100%;
|
||
background-color: #fafafa;
|
||
border: 1px solid #e5e5e5;
|
||
box-sizing: border-box;
|
||
cursor: pointer;
|
||
z-index: 30;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transform: translateX(var(--collapsed-indicator-offset, 0px));
|
||
}
|
||
|
||
.resizable-panel.collapsed .collapsed-indicator {
|
||
display: flex;
|
||
}
|
||
|
||
.collapsed-indicator:hover,
|
||
.collapsed-indicator:focus {
|
||
background-color: #f0f0f0;
|
||
}
|
||
|
||
.collapsed-indicator:focus-visible {
|
||
outline: 2px solid #1a1a1a;
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
.collapsed-indicator .indicator-text {
|
||
writing-mode: vertical-rl;
|
||
font-size: 14px;
|
||
color: #666;
|
||
user-select: none;
|
||
}
|
||
|
||
/* 折叠按钮:先复用 main.css 的 .panel-action-btn 基础样式,仅补充可访问性样式 */
|
||
.collapse-btn:focus-visible {
|
||
outline: 2px solid #1a1a1a;
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* ===================== 拖动中全局反馈 ===================== */
|
||
.layout-resizing,
|
||
.layout-resizing * {
|
||
cursor: col-resize !important;
|
||
}
|
||
|
||
.layout-resizing {
|
||
user-select: none;
|
||
-webkit-user-select: none;
|
||
}
|
||
|
||
/* ===================== F3:恢复默认布局(确认对话框细节) ===================== */
|
||
#resetLayoutModal .text-muted {
|
||
color: #999;
|
||
font-size: 12px;
|
||
margin-top: 6px;
|
||
}
|
||
|
||
/* ===================== F3:恢复默认布局(按钮) ===================== */
|
||
.reset-layout-btn {
|
||
width: 32px;
|
||
height: 32px;
|
||
padding: 0;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.reset-layout-btn svg {
|
||
width: 16px;
|
||
height: 16px;
|
||
}
|
||
|
||
/* 重置动画:临时将布局过渡时长提升到 300ms(FD F3.4) */
|
||
.layout-resetting {
|
||
--panel-transition-duration: 300ms;
|
||
}
|