@xihan-ui/styles 1.0.0-alpha.0
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/CHANGELOG.md +44 -0
- package/LICENSE +21 -0
- package/README.md +27 -0
- package/css/accordion.css +108 -0
- package/css/affix.css +27 -0
- package/css/alert.css +108 -0
- package/css/anchor.css +137 -0
- package/css/avatar-group.css +82 -0
- package/css/avatar.css +55 -0
- package/css/back-top.css +61 -0
- package/css/badge.css +50 -0
- package/css/breadcrumb.css +127 -0
- package/css/button-group.css +131 -0
- package/css/button.css +98 -0
- package/css/calendar.css +219 -0
- package/css/card.css +116 -0
- package/css/carousel.css +193 -0
- package/css/cascader.css +397 -0
- package/css/checkbox-group.css +185 -0
- package/css/checkbox.css +91 -0
- package/css/clipboard.css +133 -0
- package/css/code-block.css +81 -0
- package/css/collapsible.css +63 -0
- package/css/color-picker.css +424 -0
- package/css/combobox.css +433 -0
- package/css/composer.css +127 -0
- package/css/context-menu.css +263 -0
- package/css/countdown.css +21 -0
- package/css/date-field.css +200 -0
- package/css/date-picker.css +231 -0
- package/css/descriptions.css +200 -0
- package/css/dialog.css +127 -0
- package/css/drawer.css +372 -0
- package/css/dynamic-input.css +157 -0
- package/css/editable.css +249 -0
- package/css/ellipsis.css +41 -0
- package/css/empty-state.css +78 -0
- package/css/field.css +65 -0
- package/css/file-upload.css +298 -0
- package/css/flex.css +95 -0
- package/css/float-button.css +121 -0
- package/css/form.css +185 -0
- package/css/gradient-text.css +62 -0
- package/css/grid.css +428 -0
- package/css/highlight.css +21 -0
- package/css/hover-card.css +146 -0
- package/css/icon-wrapper.css +64 -0
- package/css/icon.css +54 -0
- package/css/image.css +45 -0
- package/css/infinite-scroll.css +22 -0
- package/css/layers.css +2 -0
- package/css/layout.css +169 -0
- package/css/list.css +117 -0
- package/css/listbox.css +174 -0
- package/css/loading-bar.css +66 -0
- package/css/log.css +66 -0
- package/css/marquee.css +182 -0
- package/css/mention.css +222 -0
- package/css/menu.css +144 -0
- package/css/menubar.css +271 -0
- package/css/motion.css +58 -0
- package/css/navigation-menu.css +270 -0
- package/css/number-animation.css +32 -0
- package/css/number-field.css +219 -0
- package/css/page-header.css +85 -0
- package/css/pagination.css +148 -0
- package/css/pin-input.css +157 -0
- package/css/popconfirm.css +150 -0
- package/css/popover.css +150 -0
- package/css/popselect.css +207 -0
- package/css/progress.css +37 -0
- package/css/qr-code.css +50 -0
- package/css/radio-group.css +101 -0
- package/css/rating.css +141 -0
- package/css/reset.css +22 -0
- package/css/result.css +104 -0
- package/css/scroll-area.css +176 -0
- package/css/select.css +264 -0
- package/css/separator.css +17 -0
- package/css/skeleton.css +78 -0
- package/css/slider.css +197 -0
- package/css/spinner.css +74 -0
- package/css/splitter.css +127 -0
- package/css/statistic.css +67 -0
- package/css/steps.css +252 -0
- package/css/switch.css +61 -0
- package/css/table.css +404 -0
- package/css/tabs.css +157 -0
- package/css/tags-input.css +360 -0
- package/css/text-field.css +195 -0
- package/css/thread.css +107 -0
- package/css/time-field.css +200 -0
- package/css/time-picker.css +347 -0
- package/css/time.css +20 -0
- package/css/timeline.css +284 -0
- package/css/toast.css +195 -0
- package/css/toaster.css +69 -0
- package/css/toggle-group.css +160 -0
- package/css/toggle.css +103 -0
- package/css/tone.css +85 -0
- package/css/toolbar.css +114 -0
- package/css/tooltip.css +109 -0
- package/css/tour.css +305 -0
- package/css/transfer.css +318 -0
- package/css/tree-select.css +441 -0
- package/css/tree.css +204 -0
- package/css/typography.css +151 -0
- package/css/undefined.css +38 -0
- package/css/virtualizer.css +104 -0
- package/css/visually-hidden.css +13 -0
- package/css/watermark.css +48 -0
- package/index.css +113 -0
- package/index.unlayered.css +16164 -0
- package/package.json +163 -0
package/css/tour.css
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
@layer xihan.motion {
|
|
2
|
+
/* 气泡贴着目标探出来:纯淡入分不清它是从哪个元素长出来的 */
|
|
3
|
+
@keyframes xh-tour-content-in {
|
|
4
|
+
from {
|
|
5
|
+
opacity: 0;
|
|
6
|
+
translate: 0 4px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
to {
|
|
10
|
+
opacity: 1;
|
|
11
|
+
translate: 0 0;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* 高亮框落定时那一下呼吸:换步时视线要跟着它走,纯位移太容易被漏看 */
|
|
16
|
+
@keyframes xh-tour-spotlight-in {
|
|
17
|
+
from {
|
|
18
|
+
opacity: 0;
|
|
19
|
+
scale: 1.04;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
to {
|
|
23
|
+
opacity: 1;
|
|
24
|
+
scale: 1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@media (prefers-reduced-motion: reduce) {
|
|
29
|
+
[data-scope='tour'] [data-part] {
|
|
30
|
+
animation-duration: 0.01ms !important;
|
|
31
|
+
transition-duration: 0.01ms !important;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@layer xihan.components {
|
|
37
|
+
/* 根节点只是几个浮层的归属容器,不参与排版。
|
|
38
|
+
这里不给 display,UA 的 [hidden]{display:none} 就仍然压得住 */
|
|
39
|
+
[data-scope='tour'][data-part='root'] {
|
|
40
|
+
color: var(--xh-tour-fg, var(--xh-fg-default));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* 遮罩:整页压暗,把注意力逼到高亮框那一块上 */
|
|
44
|
+
[data-scope='tour'][data-part='backdrop'] {
|
|
45
|
+
position: fixed;
|
|
46
|
+
inset: 0;
|
|
47
|
+
z-index: var(--xh-tour-backdrop-z, var(--xh-layer-modal));
|
|
48
|
+
background: var(--xh-tour-backdrop-bg, var(--xh-bg-overlay));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
[data-scope='tour'][data-part='backdrop'][hidden] {
|
|
52
|
+
display: none;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
[data-scope='tour'][data-part='backdrop'][data-state='open'] {
|
|
56
|
+
animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
[data-scope='tour'][data-part='backdrop'][data-state='closed'] {
|
|
60
|
+
animation: xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* 挖洞的高亮框。位置与尺寸由连接层量出来写进内联 style(position/inset/尺寸那几条轴归它),
|
|
64
|
+
这里只管长相:一圈描边 + 一片铺满视口的投影当暗幕,中间那块因此是亮的——
|
|
65
|
+
这就是"洞"。不吃指针:它盖在目标上面,吃了的话目标就再也点不动了 */
|
|
66
|
+
[data-scope='tour'][data-part='spotlight'] {
|
|
67
|
+
z-index: var(--xh-tour-spotlight-z, var(--xh-layer-modal));
|
|
68
|
+
pointer-events: none;
|
|
69
|
+
border-radius: var(--xh-tour-spotlight-radius, var(--xh-shape-surface));
|
|
70
|
+
box-shadow:
|
|
71
|
+
0 0 0 var(--xh-stroke-thick) var(--xh-tour-spotlight-ring, var(--xh-ring-focus)),
|
|
72
|
+
0 0 0 100vmax var(--xh-tour-spotlight-shroud, var(--xh-bg-overlay));
|
|
73
|
+
/* 换步时框自己滑过去,用户的视线跟得上;逻辑属性照样可过渡 */
|
|
74
|
+
transition:
|
|
75
|
+
inset-inline-start var(--xh-motion-duration-enter) var(--xh-motion-ease-enter),
|
|
76
|
+
inset-block-start var(--xh-motion-duration-enter) var(--xh-motion-ease-enter),
|
|
77
|
+
inline-size var(--xh-motion-duration-enter) var(--xh-motion-ease-enter),
|
|
78
|
+
block-size var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
[data-scope='tour'][data-part='spotlight'][hidden] {
|
|
82
|
+
display: none;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
[data-scope='tour'][data-part='spotlight'][data-state='open'] {
|
|
86
|
+
animation: xh-tour-spotlight-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* 高亮框在场时遮罩让位:暗幕已经由那圈铺满视口的投影负责,
|
|
90
|
+
两层叠加会把页面压得过暗,而且洞里也会跟着变暗——洞就白挖了。
|
|
91
|
+
居中步的高亮框带 hidden,此时遮罩照常压暗(那一步本来就没有要突出的元素) */
|
|
92
|
+
[data-scope='tour'][data-part='root']:has([data-scope='tour'][data-part='spotlight']:not([hidden]))
|
|
93
|
+
[data-scope='tour'][data-part='backdrop'] {
|
|
94
|
+
background: transparent;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* 空壳不吃指针,交互只发生在 content 上。
|
|
98
|
+
锚定步的坐标由定位引擎写进内联 style,这里不碰那两条轴 */
|
|
99
|
+
[data-scope='tour'][data-part='positioner'] {
|
|
100
|
+
z-index: var(--xh-tour-positioner-z, var(--xh-layer-popover));
|
|
101
|
+
pointer-events: none;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* 居中步:没有锚点,铺满视口把气泡摆中间 */
|
|
105
|
+
[data-scope='tour'][data-part='positioner'][data-position='center'] {
|
|
106
|
+
inset: 0;
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
justify-content: center;
|
|
110
|
+
padding: var(--xh-space-4);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* 必须排在 data-position='center' 之后:两条选择器的特指度相同(都是三个属性),
|
|
114
|
+
写在前面会被那条 display:flex 盖掉,收起态的居中步就一直摊在屏幕上 */
|
|
115
|
+
[data-scope='tour'][data-part='positioner'][hidden] {
|
|
116
|
+
display: none;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
[data-scope='tour'][data-part='content'] {
|
|
120
|
+
position: relative;
|
|
121
|
+
display: flex;
|
|
122
|
+
flex-direction: column;
|
|
123
|
+
gap: var(--xh-tour-gap, var(--xh-space-2));
|
|
124
|
+
inline-size: max-content;
|
|
125
|
+
max-inline-size: var(--xh-tour-max-w, 22rem);
|
|
126
|
+
padding-block: var(--xh-tour-py, var(--xh-space-4));
|
|
127
|
+
padding-inline: var(--xh-tour-px, var(--xh-control-px-lg));
|
|
128
|
+
border: var(--xh-stroke-thin) solid var(--xh-tour-border, var(--xh-border-default));
|
|
129
|
+
border-radius: var(--xh-tour-radius, var(--xh-shape-surface));
|
|
130
|
+
background: var(--xh-tour-bg, var(--xh-bg-surface));
|
|
131
|
+
color: var(--xh-tour-fg, var(--xh-fg-default));
|
|
132
|
+
box-shadow: var(--xh-tour-shadow, var(--xh-elevation-3));
|
|
133
|
+
pointer-events: auto;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* display 会盖掉 hidden 属性的 UA 规则,收起态显式让位 */
|
|
137
|
+
[data-scope='tour'][data-part='content'][hidden] {
|
|
138
|
+
display: none;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* 焦点落在气泡本身(Enter/Space 归"下一步"管),但它是纯容器,不画焦点环 */
|
|
142
|
+
[data-scope='tour'][data-part='content']:focus-visible {
|
|
143
|
+
outline: none;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
[data-scope='tour'][data-part='content'][data-state='open'] {
|
|
147
|
+
animation: xh-tour-content-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
[data-scope='tour'][data-part='title'] {
|
|
151
|
+
margin: 0;
|
|
152
|
+
font-size: var(--xh-tour-title-font-size, var(--xh-text-heading-3-size));
|
|
153
|
+
font-weight: var(--xh-tour-title-font-weight, var(--xh-text-heading-3-weight));
|
|
154
|
+
color: var(--xh-tour-title-fg, var(--xh-fg-default));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* 关闭按钮是绝对定位的,不占流内宽度:标题得自己让出右上角那块,
|
|
158
|
+
否则长标题会直接钻到按钮底下。只在真有关闭按钮时让位。 */
|
|
159
|
+
[data-scope='tour'][data-part='content']:has([data-scope='tour'][data-part='close-trigger'])
|
|
160
|
+
[data-scope='tour'][data-part='title'] {
|
|
161
|
+
padding-inline-end: calc(var(--xh-control-h-sm) + var(--xh-space-2));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
[data-scope='tour'][data-part='description'] {
|
|
165
|
+
margin: 0;
|
|
166
|
+
font-size: var(--xh-text-body-size);
|
|
167
|
+
line-height: var(--xh-text-body-leading);
|
|
168
|
+
color: var(--xh-tour-description-fg, var(--xh-fg-muted));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* "第 m 步,共 n 步":辅助信息,字号压一档,与按钮排在同一行的两端 */
|
|
172
|
+
[data-scope='tour'][data-part='progress-text'] {
|
|
173
|
+
font-size: var(--xh-tour-progress-font-size, var(--xh-font-size-xs));
|
|
174
|
+
color: var(--xh-tour-progress-fg, var(--xh-fg-subtle));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
[data-scope='tour'][data-part='prev-trigger'],
|
|
178
|
+
[data-scope='tour'][data-part='next-trigger'],
|
|
179
|
+
[data-scope='tour'][data-part='skip-trigger'] {
|
|
180
|
+
display: inline-flex;
|
|
181
|
+
align-items: center;
|
|
182
|
+
justify-content: center;
|
|
183
|
+
block-size: var(--xh-control-h-sm);
|
|
184
|
+
padding-inline: var(--xh-control-px-md);
|
|
185
|
+
border: var(--xh-stroke-thin) solid transparent;
|
|
186
|
+
border-radius: var(--xh-shape-control);
|
|
187
|
+
font-size: var(--xh-font-size-sm);
|
|
188
|
+
cursor: pointer;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
[data-scope='tour'][data-part='prev-trigger'][hidden],
|
|
192
|
+
[data-scope='tour'][data-part='next-trigger'][hidden],
|
|
193
|
+
[data-scope='tour'][data-part='skip-trigger'][hidden] {
|
|
194
|
+
display: none;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* 主按钮:整条引导的主线动作 */
|
|
198
|
+
[data-scope='tour'][data-part='next-trigger'] {
|
|
199
|
+
background: var(--xh-tour-next-bg, var(--xh-bg-brand));
|
|
200
|
+
color: var(--xh-tour-next-fg, var(--xh-fg-on-brand));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
[data-scope='tour'][data-part='next-trigger']:hover {
|
|
204
|
+
background: var(--xh-tour-next-bg-hover, var(--xh-bg-brand-hover));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
[data-scope='tour'][data-part='prev-trigger'],
|
|
208
|
+
[data-scope='tour'][data-part='skip-trigger'] {
|
|
209
|
+
background: transparent;
|
|
210
|
+
color: var(--xh-fg-muted);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
[data-scope='tour'][data-part='prev-trigger'] {
|
|
214
|
+
border-color: var(--xh-border-default);
|
|
215
|
+
color: var(--xh-fg-default);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
[data-scope='tour'][data-part='prev-trigger']:hover:not(:disabled),
|
|
219
|
+
[data-scope='tour'][data-part='skip-trigger']:hover {
|
|
220
|
+
background: var(--xh-bg-subtle);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* 单体控件用原生 disabled(集合条目才用 [data-disabled]):首步的上一步就是这个形态 */
|
|
224
|
+
[data-scope='tour'][data-part='prev-trigger']:disabled {
|
|
225
|
+
color: var(--xh-fg-disabled);
|
|
226
|
+
border-color: var(--xh-border-subtle);
|
|
227
|
+
cursor: not-allowed;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
[data-scope='tour'][data-part='close-trigger'] {
|
|
231
|
+
position: absolute;
|
|
232
|
+
inset-block-start: var(--xh-space-2);
|
|
233
|
+
inset-inline-end: var(--xh-space-2);
|
|
234
|
+
display: inline-flex;
|
|
235
|
+
align-items: center;
|
|
236
|
+
justify-content: center;
|
|
237
|
+
block-size: var(--xh-control-h-sm);
|
|
238
|
+
inline-size: var(--xh-control-h-sm);
|
|
239
|
+
border: 0;
|
|
240
|
+
border-radius: var(--xh-shape-control);
|
|
241
|
+
background: transparent;
|
|
242
|
+
color: var(--xh-fg-muted);
|
|
243
|
+
cursor: pointer;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
[data-scope='tour'][data-part='close-trigger'][hidden] {
|
|
247
|
+
display: none;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
[data-scope='tour'][data-part='close-trigger']:hover {
|
|
251
|
+
background: var(--xh-bg-subtle);
|
|
252
|
+
color: var(--xh-fg-default);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
[data-scope='tour'][data-part='prev-trigger']:focus-visible,
|
|
256
|
+
[data-scope='tour'][data-part='next-trigger']:focus-visible,
|
|
257
|
+
[data-scope='tour'][data-part='skip-trigger']:focus-visible,
|
|
258
|
+
[data-scope='tour'][data-part='close-trigger']:focus-visible {
|
|
259
|
+
outline: var(--xh-ring-width) solid var(--xh-ring-focus);
|
|
260
|
+
outline-offset: var(--xh-ring-offset);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* 箭头是旋转 45° 的方块,只保留朝向目标那两条边的描边;
|
|
264
|
+
引擎不产出箭头偏移,因此沿交叉轴固定居中。居中步没有锚点,连接层会给它发 hidden */
|
|
265
|
+
[data-scope='tour'][data-part='arrow'] {
|
|
266
|
+
position: absolute;
|
|
267
|
+
inline-size: var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size));
|
|
268
|
+
block-size: var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size));
|
|
269
|
+
background: var(--xh-tour-bg, var(--xh-bg-surface));
|
|
270
|
+
border: var(--xh-stroke-thin) solid var(--xh-tour-border, var(--xh-border-default));
|
|
271
|
+
rotate: 45deg;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
[data-scope='tour'][data-part='arrow'][hidden] {
|
|
275
|
+
display: none;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
[data-scope='tour'][data-part='arrow'][data-placement^='bottom'] {
|
|
279
|
+
inset-block-start: calc(var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / -2);
|
|
280
|
+
inset-inline-start: calc(50% - var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / 2);
|
|
281
|
+
border-block-end: 0;
|
|
282
|
+
border-inline-end: 0;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
[data-scope='tour'][data-part='arrow'][data-placement^='top'] {
|
|
286
|
+
inset-block-end: calc(var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / -2);
|
|
287
|
+
inset-inline-start: calc(50% - var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / 2);
|
|
288
|
+
border-block-start: 0;
|
|
289
|
+
border-inline-start: 0;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
[data-scope='tour'][data-part='arrow'][data-placement^='right'] {
|
|
293
|
+
inset-inline-start: calc(var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / -2);
|
|
294
|
+
inset-block-start: calc(50% - var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / 2);
|
|
295
|
+
border-block-start: 0;
|
|
296
|
+
border-inline-end: 0;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
[data-scope='tour'][data-part='arrow'][data-placement^='left'] {
|
|
300
|
+
inset-inline-end: calc(var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / -2);
|
|
301
|
+
inset-block-start: calc(50% - var(--xh-tour-arrow-size, var(--xh-overlay-arrow-size)) / 2);
|
|
302
|
+
border-block-end: 0;
|
|
303
|
+
border-inline-start: 0;
|
|
304
|
+
}
|
|
305
|
+
}
|
package/css/transfer.css
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
@layer xihan.components {
|
|
2
|
+
/* 三栏栅格:两侧面板各占一栏、跨两行,中间那栏上下各放一个搬运按钮,
|
|
3
|
+
两个按钮在中线处相遇。栅格的列号本身就是随书写方向走的(第 1 栏永远在行首侧),
|
|
4
|
+
dir=rtl 时整块自动镜像,不必另写一套。
|
|
5
|
+
中间那栏没有对应的角色节点(anatomy 里两个按钮是 root 的直接子节点),
|
|
6
|
+
所以只能按位置显式安放,不能靠自动流——自动流会把"往左搬"甩到右侧面板的位置上 */
|
|
7
|
+
[data-scope='transfer'][data-part='root'] {
|
|
8
|
+
display: grid;
|
|
9
|
+
grid-template-rows: 1fr 1fr;
|
|
10
|
+
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
|
11
|
+
gap: var(--xh-transfer-gap, var(--xh-space-3));
|
|
12
|
+
align-items: center;
|
|
13
|
+
color: var(--xh-transfer-fg, var(--xh-fg-default));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
[data-scope='transfer'][data-part='source-panel'] {
|
|
17
|
+
grid-row: 1 / 3;
|
|
18
|
+
grid-column: 1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
[data-scope='transfer'][data-part='target-panel'] {
|
|
22
|
+
grid-row: 1 / 3;
|
|
23
|
+
grid-column: 3;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* 上格贴底、下格贴顶:两个按钮因此夹着中线成对出现,而不是各自漂在半栏中央 */
|
|
27
|
+
[data-scope='transfer'][data-part='to-target-trigger'] {
|
|
28
|
+
grid-row: 1;
|
|
29
|
+
grid-column: 2;
|
|
30
|
+
align-self: end;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
[data-scope='transfer'][data-part='to-source-trigger'] {
|
|
34
|
+
grid-row: 2;
|
|
35
|
+
grid-column: 2;
|
|
36
|
+
align-self: start;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
[data-scope='transfer'][data-part='source-panel'],
|
|
40
|
+
[data-scope='transfer'][data-part='target-panel'] {
|
|
41
|
+
display: flex;
|
|
42
|
+
flex-direction: column;
|
|
43
|
+
block-size: 100%;
|
|
44
|
+
min-inline-size: 0;
|
|
45
|
+
border: var(--xh-stroke-thin) solid var(--xh-transfer-panel-border, var(--xh-border-default));
|
|
46
|
+
border-radius: var(--xh-transfer-panel-radius, var(--xh-shape-surface));
|
|
47
|
+
background: var(--xh-transfer-panel-bg, var(--xh-bg-surface));
|
|
48
|
+
overflow: hidden;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* 整体禁用只写在 root 与各角色节点上;面板自己也带 data-disabled,观感一处收口 */
|
|
52
|
+
[data-scope='transfer'][data-part='source-panel'][data-disabled],
|
|
53
|
+
[data-scope='transfer'][data-part='target-panel'][data-disabled] {
|
|
54
|
+
background: var(--xh-transfer-panel-bg-disabled, var(--xh-bg-muted));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
[data-scope='transfer'][data-part='panel-header'] {
|
|
58
|
+
display: flex;
|
|
59
|
+
gap: var(--xh-transfer-header-gap, var(--xh-control-gap-md));
|
|
60
|
+
align-items: center;
|
|
61
|
+
padding-block: var(--xh-transfer-header-py, var(--xh-space-2));
|
|
62
|
+
padding-inline: var(--xh-transfer-header-px, var(--xh-control-px-sm));
|
|
63
|
+
border-block-end: var(--xh-stroke-thin) solid var(--xh-transfer-panel-border, var(--xh-border-default));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* 标题占满剩余宽度,把计数挤到行尾侧 */
|
|
67
|
+
[data-scope='transfer'][data-part='panel-title'] {
|
|
68
|
+
flex: 1;
|
|
69
|
+
min-inline-size: 0;
|
|
70
|
+
overflow: hidden;
|
|
71
|
+
color: var(--xh-transfer-title-fg, var(--xh-fg-default));
|
|
72
|
+
font-size: var(--xh-transfer-title-font-size, var(--xh-text-label-size));
|
|
73
|
+
font-weight: var(--xh-transfer-title-font-weight, var(--xh-text-label-weight));
|
|
74
|
+
line-height: var(--xh-leading-none);
|
|
75
|
+
white-space: nowrap;
|
|
76
|
+
text-overflow: ellipsis;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
[data-scope='transfer'][data-part='panel-count'] {
|
|
80
|
+
flex: none;
|
|
81
|
+
color: var(--xh-transfer-count-fg, var(--xh-fg-subtle));
|
|
82
|
+
font-size: var(--xh-transfer-count-font-size, var(--xh-font-size-xs));
|
|
83
|
+
line-height: var(--xh-leading-none);
|
|
84
|
+
font-variant-numeric: tabular-nums;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* 连接层只给数字(data-checked-count / data-count),文案是作者的事:
|
|
88
|
+
语言、量词、单复数都由他定。作者什么都不写时这里补出"勾中/可见",
|
|
89
|
+
写了内容就用作者的(:empty 只命中空节点) */
|
|
90
|
+
[data-scope='transfer'][data-part='panel-count']:empty::after {
|
|
91
|
+
content: attr(data-checked-count) ' / ' attr(data-count);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
[data-scope='transfer'][data-part='search'] {
|
|
95
|
+
display: block;
|
|
96
|
+
inline-size: 100%;
|
|
97
|
+
block-size: var(--xh-transfer-search-h, var(--xh-control-h-sm));
|
|
98
|
+
padding-inline: var(--xh-transfer-search-px, var(--xh-control-px-sm));
|
|
99
|
+
border: 0;
|
|
100
|
+
border-block-end: var(--xh-stroke-thin) solid var(--xh-transfer-panel-border, var(--xh-border-default));
|
|
101
|
+
background: var(--xh-transfer-search-bg, transparent);
|
|
102
|
+
color: var(--xh-transfer-search-fg, var(--xh-fg-default));
|
|
103
|
+
font: inherit;
|
|
104
|
+
font-size: var(--xh-transfer-search-font-size, var(--xh-text-body-size));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
[data-scope='transfer'][data-part='search']:focus-visible {
|
|
108
|
+
outline: var(--xh-ring-width) solid var(--xh-ring-focus);
|
|
109
|
+
outline-offset: calc(var(--xh-ring-offset) * -1);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* 单体控件的禁用是原生 disabled,样式只能写 :disabled;集合条目才读 data-disabled */
|
|
113
|
+
[data-scope='transfer'][data-part='search']:disabled {
|
|
114
|
+
color: var(--xh-fg-disabled);
|
|
115
|
+
cursor: not-allowed;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
[data-scope='transfer'][data-part='list'] {
|
|
119
|
+
display: flex;
|
|
120
|
+
flex: 1;
|
|
121
|
+
flex-direction: column;
|
|
122
|
+
min-block-size: var(--xh-transfer-list-min-h, 8rem);
|
|
123
|
+
max-block-size: var(--xh-transfer-list-max-h, 16rem);
|
|
124
|
+
padding-block: var(--xh-transfer-list-py, var(--xh-space-1));
|
|
125
|
+
padding-inline: var(--xh-transfer-list-px, var(--xh-space-1));
|
|
126
|
+
overflow: auto;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* 焦点在列表外时容器自己进 Tab 序列兜底,落焦不画环:高亮永远长在条目上 */
|
|
130
|
+
[data-scope='transfer'][data-part='list']:focus-visible {
|
|
131
|
+
outline: none;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
[data-scope='transfer'][data-part='item'] {
|
|
135
|
+
display: flex;
|
|
136
|
+
flex: none;
|
|
137
|
+
gap: var(--xh-transfer-item-gap, var(--xh-control-gap-md));
|
|
138
|
+
align-items: center;
|
|
139
|
+
padding-block: var(--xh-transfer-item-py, var(--xh-space-1));
|
|
140
|
+
padding-inline: var(--xh-transfer-item-px, var(--xh-control-px-sm));
|
|
141
|
+
border-radius: var(--xh-transfer-item-radius, var(--xh-shape-control));
|
|
142
|
+
color: var(--xh-transfer-item-fg, var(--xh-fg-default));
|
|
143
|
+
font-size: var(--xh-transfer-item-font-size, var(--xh-text-body-size));
|
|
144
|
+
/* 行高留出文字的上下伸展空间,行内那层 overflow:hidden 才不会把字削掉 */
|
|
145
|
+
line-height: var(--xh-transfer-item-leading, var(--xh-leading-normal));
|
|
146
|
+
white-space: nowrap;
|
|
147
|
+
cursor: pointer;
|
|
148
|
+
user-select: none;
|
|
149
|
+
transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* 键盘高亮与指针悬停共用一套底色:条目本身承载焦点,不另画描边。
|
|
153
|
+
data-highlighted 跟的是连接层记的焦点条目,与勾选互相独立 */
|
|
154
|
+
[data-scope='transfer'][data-part='item']:not([data-disabled]):is(:hover, [data-highlighted]) {
|
|
155
|
+
background: var(--xh-transfer-item-bg-highlight, var(--xh-bg-subtle-hover));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
[data-scope='transfer'][data-part='item']:focus-visible {
|
|
159
|
+
outline: var(--xh-ring-width) solid var(--xh-ring-focus);
|
|
160
|
+
outline-offset: calc(var(--xh-ring-offset) * -1);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* 禁用走 aria-disabled 而非原生 disabled:条目仍要可聚焦(它还得当方向键的起点),
|
|
164
|
+
所以样式只能读 data-disabled,写 :disabled 永远不命中 */
|
|
165
|
+
[data-scope='transfer'][data-part='item'][data-disabled] {
|
|
166
|
+
color: var(--xh-fg-disabled);
|
|
167
|
+
cursor: not-allowed;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
[data-scope='transfer'][data-part='item-text'] {
|
|
171
|
+
flex: 1;
|
|
172
|
+
min-inline-size: 0;
|
|
173
|
+
overflow: hidden;
|
|
174
|
+
white-space: nowrap;
|
|
175
|
+
text-overflow: ellipsis;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* 条目的勾选格与全选格共用同一套盒子,并成一条规则写:分开写迟早只改到一半,
|
|
179
|
+
两个方框就此走形。全选格没有对应的角色节点(它自己就是按钮),由伪元素补出来 */
|
|
180
|
+
[data-scope='transfer'][data-part='item-checkbox'],
|
|
181
|
+
[data-scope='transfer'][data-part='select-all-trigger']::before {
|
|
182
|
+
display: inline-flex;
|
|
183
|
+
flex: none;
|
|
184
|
+
align-items: center;
|
|
185
|
+
justify-content: center;
|
|
186
|
+
inline-size: var(--xh-transfer-checkbox-size, 16px);
|
|
187
|
+
block-size: var(--xh-transfer-checkbox-size, 16px);
|
|
188
|
+
border: var(--xh-stroke-thin) solid var(--xh-transfer-checkbox-border, var(--xh-border-default));
|
|
189
|
+
border-radius: var(--xh-transfer-checkbox-radius, var(--xh-radius-sm));
|
|
190
|
+
background: var(--xh-transfer-checkbox-bg, var(--xh-bg-canvas));
|
|
191
|
+
color: var(--xh-transfer-checkbox-fg, var(--xh-fg-on-brand));
|
|
192
|
+
font-size: var(--xh-transfer-checkbox-font-size, 12px);
|
|
193
|
+
line-height: var(--xh-leading-none);
|
|
194
|
+
transition:
|
|
195
|
+
background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
|
|
196
|
+
border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* content 只能给伪元素,绝不能并进上面那条:content 落在普通元素上会顶掉它的全部子节点,
|
|
200
|
+
item-checkbox 里作者写的图标就此消失 */
|
|
201
|
+
[data-scope='transfer'][data-part='select-all-trigger']::before {
|
|
202
|
+
content: '';
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* 勾中即填色。全选格三态里 all 与 some 都算"有值",与条目共用同一副填色 */
|
|
206
|
+
[data-scope='transfer'][data-part='item-checkbox'][data-state='checked'],
|
|
207
|
+
[data-scope='transfer'][data-part='select-all-trigger']:is([data-state='all'], [data-state='some'])::before {
|
|
208
|
+
border-color: var(--xh-transfer-checkbox-border-checked, var(--xh-bg-brand));
|
|
209
|
+
background: var(--xh-transfer-checkbox-bg-checked, var(--xh-bg-brand));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* 勾号由皮肤补,作者不必自己往格子里塞字符 */
|
|
213
|
+
[data-scope='transfer'][data-part='item-checkbox'][data-state='checked']::before {
|
|
214
|
+
content: '✓';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* 全选格的三态:all 打勾、some 打横杠、none 留空。
|
|
218
|
+
some 就是读屏听到的 aria-checked=mixed,两者同源,样式只认 data-state */
|
|
219
|
+
[data-scope='transfer'][data-part='select-all-trigger'][data-state='all']::before {
|
|
220
|
+
content: '✓';
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
[data-scope='transfer'][data-part='select-all-trigger'][data-state='some']::before {
|
|
224
|
+
content: '−';
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
[data-scope='transfer'][data-part='item-checkbox'][data-disabled] {
|
|
228
|
+
border-color: var(--xh-transfer-checkbox-border-disabled, var(--xh-border-subtle));
|
|
229
|
+
background: var(--xh-transfer-checkbox-bg-disabled, var(--xh-bg-muted));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* 全选格是原生按钮(三态经 aria-checked 上报,激活由平台负责),
|
|
233
|
+
禁用因此写 :disabled,不写 data-disabled */
|
|
234
|
+
[data-scope='transfer'][data-part='select-all-trigger'] {
|
|
235
|
+
display: inline-flex;
|
|
236
|
+
flex: none;
|
|
237
|
+
gap: var(--xh-transfer-select-all-gap, var(--xh-control-gap-sm));
|
|
238
|
+
align-items: center;
|
|
239
|
+
padding: 0;
|
|
240
|
+
border: 0;
|
|
241
|
+
border-radius: var(--xh-transfer-select-all-radius, var(--xh-shape-control));
|
|
242
|
+
background: none;
|
|
243
|
+
color: var(--xh-transfer-select-all-fg, var(--xh-fg-muted));
|
|
244
|
+
font-size: var(--xh-transfer-select-all-font-size, var(--xh-font-size-xs));
|
|
245
|
+
font-weight: var(--xh-font-weight-medium);
|
|
246
|
+
line-height: var(--xh-leading-none);
|
|
247
|
+
cursor: pointer;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
[data-scope='transfer'][data-part='select-all-trigger']:focus-visible {
|
|
251
|
+
outline: var(--xh-ring-width) solid var(--xh-ring-focus);
|
|
252
|
+
outline-offset: var(--xh-ring-offset);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
[data-scope='transfer'][data-part='select-all-trigger']:disabled {
|
|
256
|
+
color: var(--xh-fg-disabled);
|
|
257
|
+
cursor: not-allowed;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
[data-scope='transfer'][data-part='to-target-trigger'],
|
|
261
|
+
[data-scope='transfer'][data-part='to-source-trigger'] {
|
|
262
|
+
display: inline-flex;
|
|
263
|
+
align-items: center;
|
|
264
|
+
justify-content: center;
|
|
265
|
+
min-inline-size: var(--xh-transfer-trigger-size, var(--xh-control-h-sm));
|
|
266
|
+
block-size: var(--xh-transfer-trigger-size, var(--xh-control-h-sm));
|
|
267
|
+
padding-inline: var(--xh-transfer-trigger-px, var(--xh-space-2));
|
|
268
|
+
border: var(--xh-stroke-thin) solid var(--xh-transfer-trigger-border, var(--xh-border-default));
|
|
269
|
+
border-radius: var(--xh-transfer-trigger-radius, var(--xh-shape-control));
|
|
270
|
+
background: var(--xh-transfer-trigger-bg, var(--xh-bg-subtle));
|
|
271
|
+
color: var(--xh-transfer-trigger-fg, var(--xh-fg-default));
|
|
272
|
+
font-size: var(--xh-transfer-trigger-font-size, var(--xh-text-label-size));
|
|
273
|
+
line-height: var(--xh-leading-none);
|
|
274
|
+
cursor: pointer;
|
|
275
|
+
transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
[data-scope='transfer'][data-part='to-target-trigger']:hover:not(:disabled),
|
|
279
|
+
[data-scope='transfer'][data-part='to-source-trigger']:hover:not(:disabled) {
|
|
280
|
+
background: var(--xh-transfer-trigger-bg-hover, var(--xh-bg-subtle-hover));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
[data-scope='transfer'][data-part='to-target-trigger']:active:not(:disabled),
|
|
284
|
+
[data-scope='transfer'][data-part='to-source-trigger']:active:not(:disabled) {
|
|
285
|
+
background: var(--xh-transfer-trigger-bg-active, var(--xh-bg-subtle-active));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
[data-scope='transfer'][data-part='to-target-trigger']:focus-visible,
|
|
289
|
+
[data-scope='transfer'][data-part='to-source-trigger']:focus-visible {
|
|
290
|
+
outline: var(--xh-ring-width) solid var(--xh-ring-focus);
|
|
291
|
+
outline-offset: var(--xh-ring-offset);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* 搬运按钮是单体控件,禁用走原生 disabled */
|
|
295
|
+
[data-scope='transfer'][data-part='to-target-trigger']:disabled,
|
|
296
|
+
[data-scope='transfer'][data-part='to-source-trigger']:disabled {
|
|
297
|
+
color: var(--xh-fg-disabled);
|
|
298
|
+
cursor: not-allowed;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/* 上面凡是给了 display 的角色节点都要补这条:display 声明会盖掉 UA 的 [hidden]{display:none},
|
|
302
|
+
不补的话收起态整个失效。连接层真正会写 hidden 的是 search(searchable 关掉时)、
|
|
303
|
+
item(不属于本侧、或被搜索筛掉)、item-checkbox(oneWay 下的右侧),
|
|
304
|
+
其余几个是作者自己收起时的兜底 */
|
|
305
|
+
[data-scope='transfer'][data-part='root'][hidden],
|
|
306
|
+
[data-scope='transfer'][data-part='source-panel'][hidden],
|
|
307
|
+
[data-scope='transfer'][data-part='target-panel'][hidden],
|
|
308
|
+
[data-scope='transfer'][data-part='panel-header'][hidden],
|
|
309
|
+
[data-scope='transfer'][data-part='search'][hidden],
|
|
310
|
+
[data-scope='transfer'][data-part='list'][hidden],
|
|
311
|
+
[data-scope='transfer'][data-part='item'][hidden],
|
|
312
|
+
[data-scope='transfer'][data-part='item-checkbox'][hidden],
|
|
313
|
+
[data-scope='transfer'][data-part='select-all-trigger'][hidden],
|
|
314
|
+
[data-scope='transfer'][data-part='to-target-trigger'][hidden],
|
|
315
|
+
[data-scope='transfer'][data-part='to-source-trigger'][hidden] {
|
|
316
|
+
display: none;
|
|
317
|
+
}
|
|
318
|
+
}
|