@manohub/ui 0.10.5 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +155 -155
- package/dist/components/form/index.js +8 -1
- package/dist/components/input/bordered.d.ts +15 -0
- package/dist/components/input/bordered.js +8 -0
- package/dist/components/input/index.d.ts +82 -31
- package/dist/components/input/index.js +16 -6
- package/dist/components/input/number-input.d.ts +239 -0
- package/dist/components/input/number-input.js +240 -0
- package/dist/components/select/index.d.ts +19 -0
- package/dist/components/select/index.js +10 -0
- package/dist/components/select/select-tree.d.ts +11 -0
- package/dist/components/select/select-tree.js +6 -0
- package/dist/components/select/select-trigger.d.ts +17 -0
- package/dist/components/select/select-trigger.js +9 -0
- package/dist/components/table/index.d.ts +85 -10
- package/dist/components/table/index.js +38 -9
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -0
- package/dist/styles/components/avatar.css +131 -131
- package/dist/styles/components/avatar.tokens.css +24 -24
- package/dist/styles/components/badge.css +266 -266
- package/dist/styles/components/badge.tokens.css +24 -24
- package/dist/styles/components/button.css +460 -460
- package/dist/styles/components/capsule.css +128 -128
- package/dist/styles/components/capsule.tokens.css +27 -27
- package/dist/styles/components/card.css +198 -198
- package/dist/styles/components/checkbox.css +145 -145
- package/dist/styles/components/checkbox.tokens.css +26 -26
- package/dist/styles/components/code-block.css +216 -216
- package/dist/styles/components/code-block.tokens.css +40 -40
- package/dist/styles/components/collapse.css +62 -62
- package/dist/styles/components/collapse.tokens.css +20 -20
- package/dist/styles/components/dialog.css +158 -158
- package/dist/styles/components/dialog.tokens.css +24 -24
- package/dist/styles/components/divider.css +75 -75
- package/dist/styles/components/divider.tokens.css +22 -22
- package/dist/styles/components/drawer.css +143 -143
- package/dist/styles/components/drawer.tokens.css +22 -22
- package/dist/styles/components/filter.css +29 -29
- package/dist/styles/components/form.css +456 -425
- package/dist/styles/components/form.tokens.css +19 -15
- package/dist/styles/components/input.css +659 -538
- package/dist/styles/components/input.tokens.css +43 -43
- package/dist/styles/components/layout.css +200 -200
- package/dist/styles/components/list-view.css +244 -244
- package/dist/styles/components/loading.css +76 -76
- package/dist/styles/components/loading.tokens.css +23 -23
- package/dist/styles/components/nav.css +48 -48
- package/dist/styles/components/notice.css +153 -153
- package/dist/styles/components/notice.tokens.css +17 -17
- package/dist/styles/components/notification.css +159 -159
- package/dist/styles/components/notification.tokens.css +37 -37
- package/dist/styles/components/page.css +473 -473
- package/dist/styles/components/pagination.css +303 -303
- package/dist/styles/components/pagination.tokens.css +32 -32
- package/dist/styles/components/panel.css +77 -77
- package/dist/styles/components/query-state.css +88 -88
- package/dist/styles/components/radio.css +271 -271
- package/dist/styles/components/radio.tokens.css +26 -26
- package/dist/styles/components/search.css +34 -34
- package/dist/styles/components/select.css +326 -326
- package/dist/styles/components/select.tokens.css +44 -44
- package/dist/styles/components/skeleton.css +60 -60
- package/dist/styles/components/skeleton.tokens.css +27 -27
- package/dist/styles/components/steps.css +205 -205
- package/dist/styles/components/steps.tokens.css +44 -44
- package/dist/styles/components/switch.css +165 -165
- package/dist/styles/components/switch.tokens.css +26 -26
- package/dist/styles/components/tabbar.css +235 -235
- package/dist/styles/components/tabbar.tokens.css +30 -30
- package/dist/styles/components/table.css +510 -504
- package/dist/styles/components/table.tokens.css +44 -44
- package/dist/styles/components/tag.css +127 -127
- package/dist/styles/components/tag.tokens.css +24 -24
- package/dist/styles/components/toast.css +185 -185
- package/dist/styles/components/toast.tokens.css +26 -26
- package/dist/styles/components/tooltip.css +89 -89
- package/dist/styles/components/tooltip.tokens.css +25 -25
- package/dist/styles/components/tree.css +173 -173
- package/dist/styles/components/upload.css +151 -151
- package/dist/styles/components/upload.tokens.css +27 -27
- package/dist/styles/index.css +153 -153
- package/dist/styles/operations/collapse.css +84 -84
- package/dist/styles/operations/popup.css +193 -193
- package/dist/styles/operations/tabs.css +82 -82
- package/package.json +62 -62
|
@@ -1,473 +1,473 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Page 页面骨架契约(滚动归属写死,调用方无法违反):
|
|
3
|
-
* - .mh-page:height 100%,纵向 flex,自身不滚
|
|
4
|
-
* - Header / Filter / Footer:flex-shrink 0,不滚
|
|
5
|
-
* - Body mode="scroll":自身 overflow-y auto(详情页长内容)
|
|
6
|
-
* - Body mode="plain":不滚(默认;滚动归区域内容区 / 内容件自持的定高容器)
|
|
7
|
-
* - Split:自身不滚,栏内滚动由内容承担(双栏内用 Panel 的 Body 滚)
|
|
8
|
-
*
|
|
9
|
-
* 页面级行只有 Filter / Header / Body / Footer 四条:页头右侧的自由位(`extra`)
|
|
10
|
-
* **不是页面级行**,它的容器 .mh-page__header-extra 就在 Header 内部。
|
|
11
|
-
*
|
|
12
|
-
* 统一 height:100% 语义(不用 100vh):子应用被注入宿主容器,
|
|
13
|
-
* 100vh 取 window 视口高度,宿主有顶栏即溢出。
|
|
14
|
-
*
|
|
15
|
-
* 与 panel.css 的分工:本文件是**页面级**(唯一),panel.css 是**区域级**(可多个)。
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
.mh-page {
|
|
19
|
-
height: 100%;
|
|
20
|
-
display: flex;
|
|
21
|
-
flex-direction: column;
|
|
22
|
-
overflow: hidden;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.mh-page__header {
|
|
26
|
-
flex-shrink: 0;
|
|
27
|
-
/* 页头条规格依据 MH后台-0911 设计稿 node 0:3888:条高 56(14+28+13+1px 分隔线)、左右 20 */
|
|
28
|
-
padding: 14px var(--ui-space-5) 13px;
|
|
29
|
-
border-bottom: 1px solid var(--ui-base-300);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.mh-page__header-inner {
|
|
33
|
-
display: flex;
|
|
34
|
-
align-items: flex-end;
|
|
35
|
-
justify-content: space-between;
|
|
36
|
-
gap: var(--ui-space-4);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.mh-page__header-text {
|
|
40
|
-
min-width: 0;
|
|
41
|
-
flex: 1;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.mh-page__header-title-row {
|
|
45
|
-
display: flex;
|
|
46
|
-
align-items: center;
|
|
47
|
-
/* 设计稿:图标 26 宽 + 10 间距 → 文字起点 36 */
|
|
48
|
-
gap: 10px;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.mh-page__header-icon {
|
|
52
|
-
display: inline-flex;
|
|
53
|
-
align-items: center;
|
|
54
|
-
color: var(--ui-primary);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.mh-page__header-icon svg {
|
|
58
|
-
/* 设计稿图标 26×26;消费方自传的 svg 节点也按此收口(<Icon> 自带 size 时与本条一致) */
|
|
59
|
-
width: 26px;
|
|
60
|
-
height: 26px;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/* 页头右侧自由位(Header 唯一可自定义位,对应 `extra` 属性 / 插槽):搜索框 / 按钮 / 下拉等,水平排列;
|
|
64
|
-
间距与垂直对齐由标准样式锁死,内容(搜索框、按钮)由消费方提供。
|
|
65
|
-
|
|
66
|
-
`align-self: stretch` 是关键的一条:父级 .mh-page__header-inner 是 `align-items: flex-end`
|
|
67
|
-
(右侧位与文本块**底边对齐**的既有口径),flex-end **只定位、不拉伸** —— 少了这条,
|
|
68
|
-
本盒子的高度就等于内容高度(内容比标题行矮时会短一截),自己画背景 / 分隔线就会露出白边。
|
|
69
|
-
拉满后盒子高 = 内容行高(有副标题时同样跟着变高),内部仍按 `align-items: center` 垂直居中。
|
|
70
|
-
注意:这里只到「内容行」的上下边,页头条上下的 14 / 13px 留白是 .mh-page__header 的 padding,
|
|
71
|
-
在 inner 之外,子项不覆盖它(贴满整条要改版式,不是加一条能解决的)。 */
|
|
72
|
-
.mh-page__header-extra {
|
|
73
|
-
flex-shrink: 0;
|
|
74
|
-
align-self: stretch;
|
|
75
|
-
display: flex;
|
|
76
|
-
align-items: center;
|
|
77
|
-
gap: var(--ui-space-3);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.mh-page__header-title {
|
|
81
|
-
margin: 0;
|
|
82
|
-
font-size: var(--ui-font-title);
|
|
83
|
-
font-weight: var(--ui-weight-semibold);
|
|
84
|
-
line-height: 28px;
|
|
85
|
-
color: var(--ui-base-content);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/* 页头副标题(Header 的 subTitle):标题下一行的次要说明,取 caption 档字号与次要色。
|
|
89
|
-
间距只用 margin-top 4:页头条的上下留白由 .mh-page__header 的 padding 提供,
|
|
90
|
-
这里只负责标题行与副标题行之间的距离 —— 有副标题时条目高度自然变高(56 → 80),
|
|
91
|
-
不再另调 padding(避免「有/无副标题」两套页头规格)。
|
|
92
|
-
文本块与右侧位的**底边对齐**仍是 .mh-page__header-inner 的既有口径(flex-end),
|
|
93
|
-
只不过右位自身被拉伸占满内容行(见 .mh-page__header-extra 的 align-self: stretch),
|
|
94
|
-
所以有副标题时它跟着变高、内容在盒内垂直居中,不在本项里改。 */
|
|
95
|
-
.mh-page__header-subtitle {
|
|
96
|
-
margin-top: var(--ui-space-1);
|
|
97
|
-
font-size: var(--ui-font-caption);
|
|
98
|
-
line-height: var(--ui-line-caption);
|
|
99
|
-
color: var(--ui-base-content-muted);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/* 筛选区块(Header 上方的第一子块):筛选条 / 查询方案。
|
|
103
|
-
无边框;padding-bottom 恒为 0 —— 与 Header 的间距由 Header 自身的 top padding
|
|
104
|
-
(14px)提供,避免两块 padding 叠加成双倍间距 */
|
|
105
|
-
.mh-page__filter {
|
|
106
|
-
flex-shrink: 0;
|
|
107
|
-
padding: 14px var(--ui-space-5) 0;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.mh-page__body {
|
|
111
|
-
flex: 1;
|
|
112
|
-
min-height: 0;
|
|
113
|
-
/* 页面内距 20(与页头左边距对齐,同设计稿) */
|
|
114
|
-
padding: 0 var(--ui-space-5);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
.mh-page__body--scroll {
|
|
118
|
-
overflow-y: auto;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
.mh-page__body--plain {
|
|
122
|
-
overflow: hidden;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.mh-page__footer {
|
|
126
|
-
flex-shrink: 0;
|
|
127
|
-
padding: var(--ui-space-3) var(--ui-space-5);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
.mh-page__footer-inner {
|
|
131
|
-
display: flex;
|
|
132
|
-
justify-content: flex-end;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/* ===== Split 双栏 =====
|
|
136
|
-
中间 1px 竖直分隔线由 Split 统一提供(线色与页头下边线同源 --ui-base-300),
|
|
137
|
-
线右侧(主栏)留 --ui-space-4(16px) 内距与线拉开间距;**侧栏不额外加内距**(内容自带)。
|
|
138
|
-
业务侧不得再给栏的子元素加竖直边框。
|
|
139
|
-
|
|
140
|
-
滚动:**Split 自身不滚,栏内滚动由内容承担**(双栏内用 Panel,其 Body overflow:auto)。
|
|
141
|
-
两栏 overflow:hidden 是为了避免「外层先滚、内层滚不到」的双层滚动容器。
|
|
142
|
-
|
|
143
|
-
侧栏宽度语义(`sidebar.width`):**内容宽**(content-box),
|
|
144
|
-
1px 边线与 16px 右内距加在框外 —— 消费方不必手算补偿值。 */
|
|
145
|
-
.mh-page__split {
|
|
146
|
-
height: 100%;
|
|
147
|
-
display: flex;
|
|
148
|
-
gap: 0;
|
|
149
|
-
overflow: hidden;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
.mh-page__split-sidebar {
|
|
153
|
-
flex-shrink: 0;
|
|
154
|
-
/* width 语义是内容宽 → 1px 边线加在内容框外 */
|
|
155
|
-
box-sizing: content-box;
|
|
156
|
-
border-right: 1px solid var(--ui-base-300);
|
|
157
|
-
overflow: hidden;
|
|
158
|
-
/* 线**左侧** 16px 内距:与主栏的 padding-left 对称,内容与分隔线各让 16px 拉开
|
|
159
|
-
(2026-09-20 口径;改它 = 改口径,锁定断言在 __tests__/page.spec.ts)。 */
|
|
160
|
-
padding-right: var(--ui-space-4);
|
|
161
|
-
/* 收起/展开的宽度过渡(拖拽期由 [data-dragging] 禁掉,保证跟手) */
|
|
162
|
-
transition: width 0.2s ease, padding-right 0.2s ease, border-right-width 0.2s ease;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
.mh-page__split-main {
|
|
166
|
-
flex: 1;
|
|
167
|
-
min-width: 0;
|
|
168
|
-
/* 线右侧 16px 内距:把内容与分隔线拉开 */
|
|
169
|
-
padding-left: var(--ui-space-4);
|
|
170
|
-
overflow: hidden;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/* ===== Split 侧栏交互(拖拽调宽 / 收起展开 / 右侧栏镜像)=====
|
|
174
|
-
三条口径先写在这里(改它们 = 改口径):
|
|
175
|
-
1. **手柄是 0 宽 flex 子项**:不参与空间分配 ⇒ 侧栏的 content-box 宽度语义、线两侧各 16px 内距、
|
|
176
|
-
两栏 overflow:hidden 全部原样成立(命中区靠 ::before 向两侧扩,不占布局)。
|
|
177
|
-
2. **高亮是「覆盖」不是「叠加」**:::after 精确压在侧栏那条 1px 边框上(left/right: -1px),
|
|
178
|
-
因此页面任何时候都只有一条线。
|
|
179
|
-
3. **每个规则只写一个选择器**(契约断言取「选择器首次出现」的规则体,并集会让断言取错规则),
|
|
180
|
-
且**基类必须排在修饰类之前**(ruleBody 是子串匹配:收起态规则的前缀里含基类选择器子串,
|
|
181
|
-
顺序颠倒会让既有断言读到收起态那条)。
|
|
182
|
-
另外这条也适用于注释:**注释里不要出现「选择器 + 花括号」字样** —— ruleBody 用 indexOf 找
|
|
183
|
-
选择器后的花括号,命中注释会让括号平衡算错(加这段注释时当场踩到「规则未闭合」)。 */
|
|
184
|
-
|
|
185
|
-
/* --- 收起态展开把手:常驻 DOM,宽度 0 ↔ 16 过渡(用常驻而非条件渲染,展开瞬间不会边缘瞬跳)--- */
|
|
186
|
-
.mh-page__split-rail {
|
|
187
|
-
flex: 0 0 auto;
|
|
188
|
-
width: 0;
|
|
189
|
-
overflow: hidden;
|
|
190
|
-
transition: width 0.2s ease;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
.mh-page__split--left-collapsed .mh-page__split-rail {
|
|
194
|
-
width: 16px;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
.mh-page__split-rail--right {
|
|
198
|
-
flex: 0 0 auto;
|
|
199
|
-
width: 0;
|
|
200
|
-
overflow: hidden;
|
|
201
|
-
transition: width 0.2s ease;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
.mh-page__split--right-collapsed .mh-page__split-rail--right {
|
|
205
|
-
width: 16px;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/* 展开按钮:**占满整条把手**(全高全宽)—— 收起态里它是整块侧栏唯一看得见的东西,
|
|
209
|
-
所以它自己就是那条「带边框的竖条」,不是里面再嵌一个小按钮。
|
|
210
|
-
盒模型走 border-box:1px 描边要算在 16px 里,否则右侧那条边会被把手的 overflow:hidden 裁掉。
|
|
211
|
-
与收起按钮共用同一套描边 / 底 / 圆角(尺寸不同是有意的:收起按钮钉在分隔线上,只能是小药丸)。 */
|
|
212
|
-
.mh-page__split-rail-btn {
|
|
213
|
-
box-sizing: border-box;
|
|
214
|
-
display: flex;
|
|
215
|
-
align-items: center;
|
|
216
|
-
justify-content: center;
|
|
217
|
-
width: 100%;
|
|
218
|
-
height: 100%;
|
|
219
|
-
padding: 0;
|
|
220
|
-
border: 1px solid var(--ui-base-300);
|
|
221
|
-
border-radius: var(--ui-radius-sm);
|
|
222
|
-
background: var(--ui-base-100);
|
|
223
|
-
color: var(--ui-base-content-muted);
|
|
224
|
-
cursor: pointer;
|
|
225
|
-
/* 自绘按钮必须关掉原生外观与原生焦点环:否则按下 / 聚焦时浏览器会画系统级描边
|
|
226
|
-
(真机反馈的「点击按钮出现黑色边框」就是它),焦点反馈只由下面的 :focus-visible 提供 */
|
|
227
|
-
appearance: none;
|
|
228
|
-
-webkit-appearance: none;
|
|
229
|
-
outline: none;
|
|
230
|
-
transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
.mh-page__split-rail-btn:hover {
|
|
234
|
-
border-color: var(--ui-primary);
|
|
235
|
-
color: var(--ui-primary);
|
|
236
|
-
background: var(--ui-base-200);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/* 同收起按钮:上游 `button:focus` 的 5px 深色环特异性高于基础态,必须显式清掉 */
|
|
240
|
-
.mh-page__split-rail-btn:focus {
|
|
241
|
-
outline: none;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
.mh-page__split-rail-btn:focus-visible {
|
|
245
|
-
color: var(--ui-primary);
|
|
246
|
-
outline: 2px solid var(--ui-primary-weak);
|
|
247
|
-
outline-offset: -2px;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/* --- 宽度过渡期间**内容不跟着重排**(收起 / 展开都算)---
|
|
251
|
-
内容按 `--mh-page-split-frozen`(该侧展开时的宽度,组件在收起那一刻实测后下发)定宽排布,
|
|
252
|
-
多出来的部分由本盒子的 overflow:hidden 裁掉 —— 观感还是「滑出去 / 滑进来」,
|
|
253
|
-
但栏内的滚动容器(Panel.Body、表格包裹层这些 overflow:auto 的件)不会因为宽度被压缩而现出滚动条。
|
|
254
|
-
取不到值时退回 100%:那种情形是「没给 width 的自适应侧栏」,容器宽度本身不可插值(auto 不能动画),
|
|
255
|
-
收起/展开是瞬间跳变,也就无所谓过渡期间的重排。 */
|
|
256
|
-
.mh-page__split-sidebar > * {
|
|
257
|
-
width: var(--mh-page-split-frozen, 100%);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/* --- 右侧栏:左侧栏的镜像(分隔线在侧栏左侧、内距镜像)--- */
|
|
261
|
-
.mh-page__split-sidebar--right {
|
|
262
|
-
flex-shrink: 0;
|
|
263
|
-
box-sizing: content-box;
|
|
264
|
-
border-left: 1px solid var(--ui-base-300);
|
|
265
|
-
overflow: hidden;
|
|
266
|
-
padding-left: var(--ui-space-4);
|
|
267
|
-
transition: width 0.2s ease, padding-left 0.2s ease, border-left-width 0.2s ease;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/* 右侧栏的内容定宽之外再靠右锚定:镜像方向要让「揭开」从右边缘往左走(否则内容像从左栏那侧长出来) */
|
|
271
|
-
.mh-page__split-sidebar--right > * {
|
|
272
|
-
width: var(--mh-page-split-frozen, 100%);
|
|
273
|
-
margin-left: auto;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/* --- 主栏在右侧栏存在时补右内距(左侧那条是基类,逐条镜像)---
|
|
277
|
-
挂类条件只看「右槽位是否存在」,与是否收起无关 ⇒ 收起时主栏留白不跳变。 */
|
|
278
|
-
.mh-page__split--has-right .mh-page__split-main {
|
|
279
|
-
padding-right: var(--ui-space-4);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/* --- 收起态:宽 0 + 内距 0 + 边线宽 0(边线透明仍占 1px,故归零宽度);内容延迟到过渡结束才隐藏 ---
|
|
283
|
-
收起后那一条只剩 16px 的展开把手,可辨认性由**把手上的按钮**(描边 + 底)承担,不由分隔线承担。 */
|
|
284
|
-
.mh-page__split--left-collapsed .mh-page__split-sidebar {
|
|
285
|
-
padding-right: 0;
|
|
286
|
-
border-right-width: 0;
|
|
287
|
-
visibility: hidden;
|
|
288
|
-
transition: width 0.2s ease, padding-right 0.2s ease, border-right-width 0.2s ease, visibility 0s linear 0.2s;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
.mh-page__split--right-collapsed .mh-page__split-sidebar--right {
|
|
292
|
-
padding-left: 0;
|
|
293
|
-
border-left-width: 0;
|
|
294
|
-
visibility: hidden;
|
|
295
|
-
transition: width 0.2s ease, padding-left 0.2s ease, border-left-width 0.2s ease, visibility 0s linear 0.2s;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/* --- 拖拽中禁过渡(逐帧过渡会让拖拽滞后)--- */
|
|
299
|
-
.mh-page__split-sidebar[data-dragging] {
|
|
300
|
-
transition: none;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
.mh-page__split-sidebar--right[data-dragging] {
|
|
304
|
-
transition: none;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/* --- 拖拽手柄:0 宽 flex 子项 --- */
|
|
308
|
-
.mh-page__split-handle {
|
|
309
|
-
flex: 0 0 auto;
|
|
310
|
-
width: 0;
|
|
311
|
-
position: relative;
|
|
312
|
-
/* 手柄整体压在手柄两侧的栏之上:否则跨在分隔线上的收起按钮会被栏内内容盖住 */
|
|
313
|
-
z-index: 1;
|
|
314
|
-
cursor: col-resize;
|
|
315
|
-
/* 触屏拖拽的前提:不交出滚动/缩放手势 */
|
|
316
|
-
touch-action: none;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/* 命中带:以 1px 分隔线为中心、两侧各扩 4px(9px 可抓),仍在 Split 的 overflow 边界内 */
|
|
320
|
-
.mh-page__split-handle::before {
|
|
321
|
-
content: '';
|
|
322
|
-
position: absolute;
|
|
323
|
-
top: 0;
|
|
324
|
-
bottom: 0;
|
|
325
|
-
left: -4px;
|
|
326
|
-
width: 9px;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/* 高亮:主色 1px,精确覆盖原灰线(不新增第二条线) */
|
|
330
|
-
.mh-page__split-handle::after {
|
|
331
|
-
content: '';
|
|
332
|
-
position: absolute;
|
|
333
|
-
top: 0;
|
|
334
|
-
bottom: 0;
|
|
335
|
-
left: -1px;
|
|
336
|
-
width: 1px;
|
|
337
|
-
background: var(--ui-primary);
|
|
338
|
-
opacity: 0;
|
|
339
|
-
transition: opacity 0.15s ease;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
.mh-page__split-handle:hover::after {
|
|
343
|
-
opacity: 1;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
.mh-page__split-handle[data-dragging]::after {
|
|
347
|
-
opacity: 1;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
.mh-page__split-handle--right {
|
|
351
|
-
flex: 0 0 auto;
|
|
352
|
-
width: 0;
|
|
353
|
-
position: relative;
|
|
354
|
-
z-index: 1;
|
|
355
|
-
cursor: col-resize;
|
|
356
|
-
touch-action: none;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
.mh-page__split-handle--right::before {
|
|
360
|
-
content: '';
|
|
361
|
-
position: absolute;
|
|
362
|
-
top: 0;
|
|
363
|
-
bottom: 0;
|
|
364
|
-
right: -4px;
|
|
365
|
-
width: 9px;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
.mh-page__split-handle--right::after {
|
|
369
|
-
content: '';
|
|
370
|
-
position: absolute;
|
|
371
|
-
top: 0;
|
|
372
|
-
bottom: 0;
|
|
373
|
-
right: -1px;
|
|
374
|
-
width: 1px;
|
|
375
|
-
background: var(--ui-primary);
|
|
376
|
-
opacity: 0;
|
|
377
|
-
transition: opacity 0.15s ease;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
.mh-page__split-handle--right:hover::after {
|
|
381
|
-
opacity: 1;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
.mh-page__split-handle--right[data-dragging]::after {
|
|
385
|
-
opacity: 1;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/* --- 收起按钮:**扁高条**(宽 12 / 高 28,跨在分隔线上);默认不可见也不拦事件。
|
|
389
|
-
两条位置关系都是踩过的坑:
|
|
390
|
-
① 它比 9px 命中带宽,常驻可点会吞掉栏边内容的点击 ⇒ 默认 `opacity: 0` + `pointer-events: none`;
|
|
391
|
-
② 高亮线是手柄的**伪元素**,而伪元素默认绘制在子元素(本按钮)**之上** ⇒ 必须给按钮 `z-index`
|
|
392
|
-
(同层叠上下文内只有定位元素的 z-index 参与比较,`auto` 视作 0),否则那条 1px 线会横穿按钮中央。 */
|
|
393
|
-
.mh-page__split-handle-btn {
|
|
394
|
-
position: absolute;
|
|
395
|
-
top: 50%;
|
|
396
|
-
left: 50%;
|
|
397
|
-
transform: translate(-50%, -50%);
|
|
398
|
-
z-index: 1;
|
|
399
|
-
display: flex;
|
|
400
|
-
align-items: center;
|
|
401
|
-
justify-content: center;
|
|
402
|
-
width: 12px;
|
|
403
|
-
height: 28px;
|
|
404
|
-
padding: 0;
|
|
405
|
-
border: 1px solid var(--ui-base-300);
|
|
406
|
-
border-radius: var(--ui-radius-sm);
|
|
407
|
-
background: var(--ui-base-100);
|
|
408
|
-
color: var(--ui-base-content-muted);
|
|
409
|
-
cursor: pointer;
|
|
410
|
-
/* 同 rail-btn:自绘按钮必须关掉原生外观与原生焦点环,否则按下时会出现系统级描边(黑框) */
|
|
411
|
-
appearance: none;
|
|
412
|
-
-webkit-appearance: none;
|
|
413
|
-
outline: none;
|
|
414
|
-
opacity: 0;
|
|
415
|
-
pointer-events: none;
|
|
416
|
-
transition: opacity 0.15s ease, border-color 0.15s ease, color 0.15s ease;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
.mh-page__split-handle:hover .mh-page__split-handle-btn {
|
|
420
|
-
opacity: 1;
|
|
421
|
-
pointer-events: auto;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
.mh-page__split-handle--right:hover .mh-page__split-handle-btn {
|
|
425
|
-
opacity: 1;
|
|
426
|
-
pointer-events: auto;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/* 显式清掉 `:focus` 态的轮廓:上游 farris 有 `button:focus { outline: -webkit-focus-ring-color auto 5px }`,
|
|
430
|
-
特异性 (0,1,1) 高于本按钮基础态的 (0,1,0) —— 只写基础态 `outline: none` 压不住它,
|
|
431
|
-
鼠标点击(`:focus-visible` 不匹配)就会露出那道 5px 深色环。本规则 (0,2,0) 压过上游;
|
|
432
|
-
焦点反馈只留给紧随其后的 `:focus-visible`(同特异性、顺序在后 ⇒ 它赢)。
|
|
433
|
-
注:自绘层零 farris,本包之外的样式链若给全局按钮上过焦点环,本规则同样兜住。 */
|
|
434
|
-
.mh-page__split-handle-btn:focus {
|
|
435
|
-
outline: none;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
.mh-page__split-handle-btn:focus-visible {
|
|
439
|
-
opacity: 1;
|
|
440
|
-
pointer-events: auto;
|
|
441
|
-
outline: 2px solid var(--ui-primary-weak);
|
|
442
|
-
outline-offset: 1px;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
.mh-page__split-handle-btn:hover {
|
|
446
|
-
border-color: var(--ui-primary);
|
|
447
|
-
color: var(--ui-primary);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/* --- 拖拽期:整块保持拖动光标并禁选中文字 --- */
|
|
451
|
-
.mh-page__split--resizing {
|
|
452
|
-
cursor: col-resize;
|
|
453
|
-
user-select: none;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
/* --- 动效降级:系统要求减少动效时,收起/展开直接到位 --- */
|
|
457
|
-
@media (prefers-reduced-motion: reduce) {
|
|
458
|
-
.mh-page__split-sidebar {
|
|
459
|
-
transition: none;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
.mh-page__split-sidebar--right {
|
|
463
|
-
transition: none;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
.mh-page__split-rail {
|
|
467
|
-
transition: none;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
.mh-page__split-rail--right {
|
|
471
|
-
transition: none;
|
|
472
|
-
}
|
|
473
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Page 页面骨架契约(滚动归属写死,调用方无法违反):
|
|
3
|
+
* - .mh-page:height 100%,纵向 flex,自身不滚
|
|
4
|
+
* - Header / Filter / Footer:flex-shrink 0,不滚
|
|
5
|
+
* - Body mode="scroll":自身 overflow-y auto(详情页长内容)
|
|
6
|
+
* - Body mode="plain":不滚(默认;滚动归区域内容区 / 内容件自持的定高容器)
|
|
7
|
+
* - Split:自身不滚,栏内滚动由内容承担(双栏内用 Panel 的 Body 滚)
|
|
8
|
+
*
|
|
9
|
+
* 页面级行只有 Filter / Header / Body / Footer 四条:页头右侧的自由位(`extra`)
|
|
10
|
+
* **不是页面级行**,它的容器 .mh-page__header-extra 就在 Header 内部。
|
|
11
|
+
*
|
|
12
|
+
* 统一 height:100% 语义(不用 100vh):子应用被注入宿主容器,
|
|
13
|
+
* 100vh 取 window 视口高度,宿主有顶栏即溢出。
|
|
14
|
+
*
|
|
15
|
+
* 与 panel.css 的分工:本文件是**页面级**(唯一),panel.css 是**区域级**(可多个)。
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
.mh-page {
|
|
19
|
+
height: 100%;
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-direction: column;
|
|
22
|
+
overflow: hidden;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.mh-page__header {
|
|
26
|
+
flex-shrink: 0;
|
|
27
|
+
/* 页头条规格依据 MH后台-0911 设计稿 node 0:3888:条高 56(14+28+13+1px 分隔线)、左右 20 */
|
|
28
|
+
padding: 14px var(--ui-space-5) 13px;
|
|
29
|
+
border-bottom: 1px solid var(--ui-base-300);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.mh-page__header-inner {
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: flex-end;
|
|
35
|
+
justify-content: space-between;
|
|
36
|
+
gap: var(--ui-space-4);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.mh-page__header-text {
|
|
40
|
+
min-width: 0;
|
|
41
|
+
flex: 1;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.mh-page__header-title-row {
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
/* 设计稿:图标 26 宽 + 10 间距 → 文字起点 36 */
|
|
48
|
+
gap: 10px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.mh-page__header-icon {
|
|
52
|
+
display: inline-flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
color: var(--ui-primary);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.mh-page__header-icon svg {
|
|
58
|
+
/* 设计稿图标 26×26;消费方自传的 svg 节点也按此收口(<Icon> 自带 size 时与本条一致) */
|
|
59
|
+
width: 26px;
|
|
60
|
+
height: 26px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* 页头右侧自由位(Header 唯一可自定义位,对应 `extra` 属性 / 插槽):搜索框 / 按钮 / 下拉等,水平排列;
|
|
64
|
+
间距与垂直对齐由标准样式锁死,内容(搜索框、按钮)由消费方提供。
|
|
65
|
+
|
|
66
|
+
`align-self: stretch` 是关键的一条:父级 .mh-page__header-inner 是 `align-items: flex-end`
|
|
67
|
+
(右侧位与文本块**底边对齐**的既有口径),flex-end **只定位、不拉伸** —— 少了这条,
|
|
68
|
+
本盒子的高度就等于内容高度(内容比标题行矮时会短一截),自己画背景 / 分隔线就会露出白边。
|
|
69
|
+
拉满后盒子高 = 内容行高(有副标题时同样跟着变高),内部仍按 `align-items: center` 垂直居中。
|
|
70
|
+
注意:这里只到「内容行」的上下边,页头条上下的 14 / 13px 留白是 .mh-page__header 的 padding,
|
|
71
|
+
在 inner 之外,子项不覆盖它(贴满整条要改版式,不是加一条能解决的)。 */
|
|
72
|
+
.mh-page__header-extra {
|
|
73
|
+
flex-shrink: 0;
|
|
74
|
+
align-self: stretch;
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
gap: var(--ui-space-3);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.mh-page__header-title {
|
|
81
|
+
margin: 0;
|
|
82
|
+
font-size: var(--ui-font-title);
|
|
83
|
+
font-weight: var(--ui-weight-semibold);
|
|
84
|
+
line-height: 28px;
|
|
85
|
+
color: var(--ui-base-content);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* 页头副标题(Header 的 subTitle):标题下一行的次要说明,取 caption 档字号与次要色。
|
|
89
|
+
间距只用 margin-top 4:页头条的上下留白由 .mh-page__header 的 padding 提供,
|
|
90
|
+
这里只负责标题行与副标题行之间的距离 —— 有副标题时条目高度自然变高(56 → 80),
|
|
91
|
+
不再另调 padding(避免「有/无副标题」两套页头规格)。
|
|
92
|
+
文本块与右侧位的**底边对齐**仍是 .mh-page__header-inner 的既有口径(flex-end),
|
|
93
|
+
只不过右位自身被拉伸占满内容行(见 .mh-page__header-extra 的 align-self: stretch),
|
|
94
|
+
所以有副标题时它跟着变高、内容在盒内垂直居中,不在本项里改。 */
|
|
95
|
+
.mh-page__header-subtitle {
|
|
96
|
+
margin-top: var(--ui-space-1);
|
|
97
|
+
font-size: var(--ui-font-caption);
|
|
98
|
+
line-height: var(--ui-line-caption);
|
|
99
|
+
color: var(--ui-base-content-muted);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* 筛选区块(Header 上方的第一子块):筛选条 / 查询方案。
|
|
103
|
+
无边框;padding-bottom 恒为 0 —— 与 Header 的间距由 Header 自身的 top padding
|
|
104
|
+
(14px)提供,避免两块 padding 叠加成双倍间距 */
|
|
105
|
+
.mh-page__filter {
|
|
106
|
+
flex-shrink: 0;
|
|
107
|
+
padding: 14px var(--ui-space-5) 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.mh-page__body {
|
|
111
|
+
flex: 1;
|
|
112
|
+
min-height: 0;
|
|
113
|
+
/* 页面内距 20(与页头左边距对齐,同设计稿) */
|
|
114
|
+
padding: 0 var(--ui-space-5);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.mh-page__body--scroll {
|
|
118
|
+
overflow-y: auto;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.mh-page__body--plain {
|
|
122
|
+
overflow: hidden;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.mh-page__footer {
|
|
126
|
+
flex-shrink: 0;
|
|
127
|
+
padding: var(--ui-space-3) var(--ui-space-5);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.mh-page__footer-inner {
|
|
131
|
+
display: flex;
|
|
132
|
+
justify-content: flex-end;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* ===== Split 双栏 =====
|
|
136
|
+
中间 1px 竖直分隔线由 Split 统一提供(线色与页头下边线同源 --ui-base-300),
|
|
137
|
+
线右侧(主栏)留 --ui-space-4(16px) 内距与线拉开间距;**侧栏不额外加内距**(内容自带)。
|
|
138
|
+
业务侧不得再给栏的子元素加竖直边框。
|
|
139
|
+
|
|
140
|
+
滚动:**Split 自身不滚,栏内滚动由内容承担**(双栏内用 Panel,其 Body overflow:auto)。
|
|
141
|
+
两栏 overflow:hidden 是为了避免「外层先滚、内层滚不到」的双层滚动容器。
|
|
142
|
+
|
|
143
|
+
侧栏宽度语义(`sidebar.width`):**内容宽**(content-box),
|
|
144
|
+
1px 边线与 16px 右内距加在框外 —— 消费方不必手算补偿值。 */
|
|
145
|
+
.mh-page__split {
|
|
146
|
+
height: 100%;
|
|
147
|
+
display: flex;
|
|
148
|
+
gap: 0;
|
|
149
|
+
overflow: hidden;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.mh-page__split-sidebar {
|
|
153
|
+
flex-shrink: 0;
|
|
154
|
+
/* width 语义是内容宽 → 1px 边线加在内容框外 */
|
|
155
|
+
box-sizing: content-box;
|
|
156
|
+
border-right: 1px solid var(--ui-base-300);
|
|
157
|
+
overflow: hidden;
|
|
158
|
+
/* 线**左侧** 16px 内距:与主栏的 padding-left 对称,内容与分隔线各让 16px 拉开
|
|
159
|
+
(2026-09-20 口径;改它 = 改口径,锁定断言在 __tests__/page.spec.ts)。 */
|
|
160
|
+
padding-right: var(--ui-space-4);
|
|
161
|
+
/* 收起/展开的宽度过渡(拖拽期由 [data-dragging] 禁掉,保证跟手) */
|
|
162
|
+
transition: width 0.2s ease, padding-right 0.2s ease, border-right-width 0.2s ease;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.mh-page__split-main {
|
|
166
|
+
flex: 1;
|
|
167
|
+
min-width: 0;
|
|
168
|
+
/* 线右侧 16px 内距:把内容与分隔线拉开 */
|
|
169
|
+
padding-left: var(--ui-space-4);
|
|
170
|
+
overflow: hidden;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* ===== Split 侧栏交互(拖拽调宽 / 收起展开 / 右侧栏镜像)=====
|
|
174
|
+
三条口径先写在这里(改它们 = 改口径):
|
|
175
|
+
1. **手柄是 0 宽 flex 子项**:不参与空间分配 ⇒ 侧栏的 content-box 宽度语义、线两侧各 16px 内距、
|
|
176
|
+
两栏 overflow:hidden 全部原样成立(命中区靠 ::before 向两侧扩,不占布局)。
|
|
177
|
+
2. **高亮是「覆盖」不是「叠加」**:::after 精确压在侧栏那条 1px 边框上(left/right: -1px),
|
|
178
|
+
因此页面任何时候都只有一条线。
|
|
179
|
+
3. **每个规则只写一个选择器**(契约断言取「选择器首次出现」的规则体,并集会让断言取错规则),
|
|
180
|
+
且**基类必须排在修饰类之前**(ruleBody 是子串匹配:收起态规则的前缀里含基类选择器子串,
|
|
181
|
+
顺序颠倒会让既有断言读到收起态那条)。
|
|
182
|
+
另外这条也适用于注释:**注释里不要出现「选择器 + 花括号」字样** —— ruleBody 用 indexOf 找
|
|
183
|
+
选择器后的花括号,命中注释会让括号平衡算错(加这段注释时当场踩到「规则未闭合」)。 */
|
|
184
|
+
|
|
185
|
+
/* --- 收起态展开把手:常驻 DOM,宽度 0 ↔ 16 过渡(用常驻而非条件渲染,展开瞬间不会边缘瞬跳)--- */
|
|
186
|
+
.mh-page__split-rail {
|
|
187
|
+
flex: 0 0 auto;
|
|
188
|
+
width: 0;
|
|
189
|
+
overflow: hidden;
|
|
190
|
+
transition: width 0.2s ease;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.mh-page__split--left-collapsed .mh-page__split-rail {
|
|
194
|
+
width: 16px;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.mh-page__split-rail--right {
|
|
198
|
+
flex: 0 0 auto;
|
|
199
|
+
width: 0;
|
|
200
|
+
overflow: hidden;
|
|
201
|
+
transition: width 0.2s ease;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.mh-page__split--right-collapsed .mh-page__split-rail--right {
|
|
205
|
+
width: 16px;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* 展开按钮:**占满整条把手**(全高全宽)—— 收起态里它是整块侧栏唯一看得见的东西,
|
|
209
|
+
所以它自己就是那条「带边框的竖条」,不是里面再嵌一个小按钮。
|
|
210
|
+
盒模型走 border-box:1px 描边要算在 16px 里,否则右侧那条边会被把手的 overflow:hidden 裁掉。
|
|
211
|
+
与收起按钮共用同一套描边 / 底 / 圆角(尺寸不同是有意的:收起按钮钉在分隔线上,只能是小药丸)。 */
|
|
212
|
+
.mh-page__split-rail-btn {
|
|
213
|
+
box-sizing: border-box;
|
|
214
|
+
display: flex;
|
|
215
|
+
align-items: center;
|
|
216
|
+
justify-content: center;
|
|
217
|
+
width: 100%;
|
|
218
|
+
height: 100%;
|
|
219
|
+
padding: 0;
|
|
220
|
+
border: 1px solid var(--ui-base-300);
|
|
221
|
+
border-radius: var(--ui-radius-sm);
|
|
222
|
+
background: var(--ui-base-100);
|
|
223
|
+
color: var(--ui-base-content-muted);
|
|
224
|
+
cursor: pointer;
|
|
225
|
+
/* 自绘按钮必须关掉原生外观与原生焦点环:否则按下 / 聚焦时浏览器会画系统级描边
|
|
226
|
+
(真机反馈的「点击按钮出现黑色边框」就是它),焦点反馈只由下面的 :focus-visible 提供 */
|
|
227
|
+
appearance: none;
|
|
228
|
+
-webkit-appearance: none;
|
|
229
|
+
outline: none;
|
|
230
|
+
transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.mh-page__split-rail-btn:hover {
|
|
234
|
+
border-color: var(--ui-primary);
|
|
235
|
+
color: var(--ui-primary);
|
|
236
|
+
background: var(--ui-base-200);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/* 同收起按钮:上游 `button:focus` 的 5px 深色环特异性高于基础态,必须显式清掉 */
|
|
240
|
+
.mh-page__split-rail-btn:focus {
|
|
241
|
+
outline: none;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.mh-page__split-rail-btn:focus-visible {
|
|
245
|
+
color: var(--ui-primary);
|
|
246
|
+
outline: 2px solid var(--ui-primary-weak);
|
|
247
|
+
outline-offset: -2px;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* --- 宽度过渡期间**内容不跟着重排**(收起 / 展开都算)---
|
|
251
|
+
内容按 `--mh-page-split-frozen`(该侧展开时的宽度,组件在收起那一刻实测后下发)定宽排布,
|
|
252
|
+
多出来的部分由本盒子的 overflow:hidden 裁掉 —— 观感还是「滑出去 / 滑进来」,
|
|
253
|
+
但栏内的滚动容器(Panel.Body、表格包裹层这些 overflow:auto 的件)不会因为宽度被压缩而现出滚动条。
|
|
254
|
+
取不到值时退回 100%:那种情形是「没给 width 的自适应侧栏」,容器宽度本身不可插值(auto 不能动画),
|
|
255
|
+
收起/展开是瞬间跳变,也就无所谓过渡期间的重排。 */
|
|
256
|
+
.mh-page__split-sidebar > * {
|
|
257
|
+
width: var(--mh-page-split-frozen, 100%);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/* --- 右侧栏:左侧栏的镜像(分隔线在侧栏左侧、内距镜像)--- */
|
|
261
|
+
.mh-page__split-sidebar--right {
|
|
262
|
+
flex-shrink: 0;
|
|
263
|
+
box-sizing: content-box;
|
|
264
|
+
border-left: 1px solid var(--ui-base-300);
|
|
265
|
+
overflow: hidden;
|
|
266
|
+
padding-left: var(--ui-space-4);
|
|
267
|
+
transition: width 0.2s ease, padding-left 0.2s ease, border-left-width 0.2s ease;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/* 右侧栏的内容定宽之外再靠右锚定:镜像方向要让「揭开」从右边缘往左走(否则内容像从左栏那侧长出来) */
|
|
271
|
+
.mh-page__split-sidebar--right > * {
|
|
272
|
+
width: var(--mh-page-split-frozen, 100%);
|
|
273
|
+
margin-left: auto;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* --- 主栏在右侧栏存在时补右内距(左侧那条是基类,逐条镜像)---
|
|
277
|
+
挂类条件只看「右槽位是否存在」,与是否收起无关 ⇒ 收起时主栏留白不跳变。 */
|
|
278
|
+
.mh-page__split--has-right .mh-page__split-main {
|
|
279
|
+
padding-right: var(--ui-space-4);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/* --- 收起态:宽 0 + 内距 0 + 边线宽 0(边线透明仍占 1px,故归零宽度);内容延迟到过渡结束才隐藏 ---
|
|
283
|
+
收起后那一条只剩 16px 的展开把手,可辨认性由**把手上的按钮**(描边 + 底)承担,不由分隔线承担。 */
|
|
284
|
+
.mh-page__split--left-collapsed .mh-page__split-sidebar {
|
|
285
|
+
padding-right: 0;
|
|
286
|
+
border-right-width: 0;
|
|
287
|
+
visibility: hidden;
|
|
288
|
+
transition: width 0.2s ease, padding-right 0.2s ease, border-right-width 0.2s ease, visibility 0s linear 0.2s;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.mh-page__split--right-collapsed .mh-page__split-sidebar--right {
|
|
292
|
+
padding-left: 0;
|
|
293
|
+
border-left-width: 0;
|
|
294
|
+
visibility: hidden;
|
|
295
|
+
transition: width 0.2s ease, padding-left 0.2s ease, border-left-width 0.2s ease, visibility 0s linear 0.2s;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/* --- 拖拽中禁过渡(逐帧过渡会让拖拽滞后)--- */
|
|
299
|
+
.mh-page__split-sidebar[data-dragging] {
|
|
300
|
+
transition: none;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.mh-page__split-sidebar--right[data-dragging] {
|
|
304
|
+
transition: none;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* --- 拖拽手柄:0 宽 flex 子项 --- */
|
|
308
|
+
.mh-page__split-handle {
|
|
309
|
+
flex: 0 0 auto;
|
|
310
|
+
width: 0;
|
|
311
|
+
position: relative;
|
|
312
|
+
/* 手柄整体压在手柄两侧的栏之上:否则跨在分隔线上的收起按钮会被栏内内容盖住 */
|
|
313
|
+
z-index: 1;
|
|
314
|
+
cursor: col-resize;
|
|
315
|
+
/* 触屏拖拽的前提:不交出滚动/缩放手势 */
|
|
316
|
+
touch-action: none;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* 命中带:以 1px 分隔线为中心、两侧各扩 4px(9px 可抓),仍在 Split 的 overflow 边界内 */
|
|
320
|
+
.mh-page__split-handle::before {
|
|
321
|
+
content: '';
|
|
322
|
+
position: absolute;
|
|
323
|
+
top: 0;
|
|
324
|
+
bottom: 0;
|
|
325
|
+
left: -4px;
|
|
326
|
+
width: 9px;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/* 高亮:主色 1px,精确覆盖原灰线(不新增第二条线) */
|
|
330
|
+
.mh-page__split-handle::after {
|
|
331
|
+
content: '';
|
|
332
|
+
position: absolute;
|
|
333
|
+
top: 0;
|
|
334
|
+
bottom: 0;
|
|
335
|
+
left: -1px;
|
|
336
|
+
width: 1px;
|
|
337
|
+
background: var(--ui-primary);
|
|
338
|
+
opacity: 0;
|
|
339
|
+
transition: opacity 0.15s ease;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.mh-page__split-handle:hover::after {
|
|
343
|
+
opacity: 1;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.mh-page__split-handle[data-dragging]::after {
|
|
347
|
+
opacity: 1;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.mh-page__split-handle--right {
|
|
351
|
+
flex: 0 0 auto;
|
|
352
|
+
width: 0;
|
|
353
|
+
position: relative;
|
|
354
|
+
z-index: 1;
|
|
355
|
+
cursor: col-resize;
|
|
356
|
+
touch-action: none;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.mh-page__split-handle--right::before {
|
|
360
|
+
content: '';
|
|
361
|
+
position: absolute;
|
|
362
|
+
top: 0;
|
|
363
|
+
bottom: 0;
|
|
364
|
+
right: -4px;
|
|
365
|
+
width: 9px;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.mh-page__split-handle--right::after {
|
|
369
|
+
content: '';
|
|
370
|
+
position: absolute;
|
|
371
|
+
top: 0;
|
|
372
|
+
bottom: 0;
|
|
373
|
+
right: -1px;
|
|
374
|
+
width: 1px;
|
|
375
|
+
background: var(--ui-primary);
|
|
376
|
+
opacity: 0;
|
|
377
|
+
transition: opacity 0.15s ease;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.mh-page__split-handle--right:hover::after {
|
|
381
|
+
opacity: 1;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.mh-page__split-handle--right[data-dragging]::after {
|
|
385
|
+
opacity: 1;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/* --- 收起按钮:**扁高条**(宽 12 / 高 28,跨在分隔线上);默认不可见也不拦事件。
|
|
389
|
+
两条位置关系都是踩过的坑:
|
|
390
|
+
① 它比 9px 命中带宽,常驻可点会吞掉栏边内容的点击 ⇒ 默认 `opacity: 0` + `pointer-events: none`;
|
|
391
|
+
② 高亮线是手柄的**伪元素**,而伪元素默认绘制在子元素(本按钮)**之上** ⇒ 必须给按钮 `z-index`
|
|
392
|
+
(同层叠上下文内只有定位元素的 z-index 参与比较,`auto` 视作 0),否则那条 1px 线会横穿按钮中央。 */
|
|
393
|
+
.mh-page__split-handle-btn {
|
|
394
|
+
position: absolute;
|
|
395
|
+
top: 50%;
|
|
396
|
+
left: 50%;
|
|
397
|
+
transform: translate(-50%, -50%);
|
|
398
|
+
z-index: 1;
|
|
399
|
+
display: flex;
|
|
400
|
+
align-items: center;
|
|
401
|
+
justify-content: center;
|
|
402
|
+
width: 12px;
|
|
403
|
+
height: 28px;
|
|
404
|
+
padding: 0;
|
|
405
|
+
border: 1px solid var(--ui-base-300);
|
|
406
|
+
border-radius: var(--ui-radius-sm);
|
|
407
|
+
background: var(--ui-base-100);
|
|
408
|
+
color: var(--ui-base-content-muted);
|
|
409
|
+
cursor: pointer;
|
|
410
|
+
/* 同 rail-btn:自绘按钮必须关掉原生外观与原生焦点环,否则按下时会出现系统级描边(黑框) */
|
|
411
|
+
appearance: none;
|
|
412
|
+
-webkit-appearance: none;
|
|
413
|
+
outline: none;
|
|
414
|
+
opacity: 0;
|
|
415
|
+
pointer-events: none;
|
|
416
|
+
transition: opacity 0.15s ease, border-color 0.15s ease, color 0.15s ease;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.mh-page__split-handle:hover .mh-page__split-handle-btn {
|
|
420
|
+
opacity: 1;
|
|
421
|
+
pointer-events: auto;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.mh-page__split-handle--right:hover .mh-page__split-handle-btn {
|
|
425
|
+
opacity: 1;
|
|
426
|
+
pointer-events: auto;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/* 显式清掉 `:focus` 态的轮廓:上游 farris 有 `button:focus { outline: -webkit-focus-ring-color auto 5px }`,
|
|
430
|
+
特异性 (0,1,1) 高于本按钮基础态的 (0,1,0) —— 只写基础态 `outline: none` 压不住它,
|
|
431
|
+
鼠标点击(`:focus-visible` 不匹配)就会露出那道 5px 深色环。本规则 (0,2,0) 压过上游;
|
|
432
|
+
焦点反馈只留给紧随其后的 `:focus-visible`(同特异性、顺序在后 ⇒ 它赢)。
|
|
433
|
+
注:自绘层零 farris,本包之外的样式链若给全局按钮上过焦点环,本规则同样兜住。 */
|
|
434
|
+
.mh-page__split-handle-btn:focus {
|
|
435
|
+
outline: none;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.mh-page__split-handle-btn:focus-visible {
|
|
439
|
+
opacity: 1;
|
|
440
|
+
pointer-events: auto;
|
|
441
|
+
outline: 2px solid var(--ui-primary-weak);
|
|
442
|
+
outline-offset: 1px;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.mh-page__split-handle-btn:hover {
|
|
446
|
+
border-color: var(--ui-primary);
|
|
447
|
+
color: var(--ui-primary);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* --- 拖拽期:整块保持拖动光标并禁选中文字 --- */
|
|
451
|
+
.mh-page__split--resizing {
|
|
452
|
+
cursor: col-resize;
|
|
453
|
+
user-select: none;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/* --- 动效降级:系统要求减少动效时,收起/展开直接到位 --- */
|
|
457
|
+
@media (prefers-reduced-motion: reduce) {
|
|
458
|
+
.mh-page__split-sidebar {
|
|
459
|
+
transition: none;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.mh-page__split-sidebar--right {
|
|
463
|
+
transition: none;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.mh-page__split-rail {
|
|
467
|
+
transition: none;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.mh-page__split-rail--right {
|
|
471
|
+
transition: none;
|
|
472
|
+
}
|
|
473
|
+
}
|