@xihan-ui/styles 2.1.0 → 3.1.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.
Files changed (152) hide show
  1. package/CHANGELOG.md +1261 -0
  2. package/README.md +6 -1
  3. package/component-tokens.d.ts +533 -96
  4. package/components.tokens.json +35461 -26045
  5. package/css/accordion.css +34 -8
  6. package/css/alert.css +59 -1
  7. package/css/anchor.css +46 -8
  8. package/css/approval.css +116 -76
  9. package/css/avatar-group.css +1 -1
  10. package/css/avatar.css +13 -5
  11. package/css/back-top.css +61 -42
  12. package/css/badge.css +62 -12
  13. package/css/bar-code.css +1 -1
  14. package/css/breadcrumb.css +62 -3
  15. package/css/button-group.css +4 -4
  16. package/css/button.css +67 -19
  17. package/css/calendar-picker.css +19 -0
  18. package/css/calendar-range-picker.css +39 -2
  19. package/css/card.css +115 -1
  20. package/css/carousel.css +148 -43
  21. package/css/cartesian-chart.css +830 -0
  22. package/css/cascader.css +181 -65
  23. package/css/checkbox-group.css +59 -89
  24. package/css/checkbox.css +16 -49
  25. package/css/citation.css +470 -0
  26. package/css/clipboard.css +20 -63
  27. package/css/code-view.css +101 -6
  28. package/css/collapsible.css +34 -8
  29. package/css/color-field.css +18 -0
  30. package/css/color-picker.css +128 -50
  31. package/css/color-slider.css +22 -15
  32. package/css/color-swatch-picker.css +3 -1
  33. package/css/combobox.css +80 -33
  34. package/css/command.css +45 -43
  35. package/css/context-menu.css +39 -22
  36. package/css/date-field.css +56 -0
  37. package/css/date-picker.css +132 -26
  38. package/css/date-range-picker.css +337 -14
  39. package/css/description.css +17 -1
  40. package/css/descriptions.css +42 -0
  41. package/css/dialog.css +94 -36
  42. package/css/diff-view.css +97 -12
  43. package/css/download-trigger.css +44 -78
  44. package/css/drawer.css +160 -119
  45. package/css/editable.css +19 -0
  46. package/css/empty-state.css +7 -6
  47. package/css/field-array.css +36 -0
  48. package/css/field.css +20 -18
  49. package/css/file-upload.css +125 -135
  50. package/css/flex.css +324 -8
  51. package/css/float-button.css +263 -38
  52. package/css/floating-panel.css +53 -19
  53. package/css/form.css +31 -17
  54. package/css/funnel-chart.css +106 -0
  55. package/css/graph-chart.css +205 -0
  56. package/css/grid-list.css +235 -0
  57. package/css/grid.css +252 -1
  58. package/css/heatmap.css +119 -36
  59. package/css/hierarchy-chart.css +298 -0
  60. package/css/hover-card.css +31 -14
  61. package/css/icon.css +100 -0
  62. package/css/image-cropper.css +33 -7
  63. package/css/image-viewer.css +167 -22
  64. package/css/image.css +23 -4
  65. package/css/input-group.css +11 -13
  66. package/css/json-viewer.css +50 -2
  67. package/css/layout.css +68 -18
  68. package/css/liquid.css +106 -0
  69. package/css/list.css +1 -1
  70. package/css/listbox.css +46 -6
  71. package/css/loading-bar.css +27 -13
  72. package/css/log.css +129 -33
  73. package/css/markdown-stream.css +1 -1
  74. package/css/marquee.css +163 -7
  75. package/css/matrix-code.css +1 -1
  76. package/css/mention.css +35 -19
  77. package/css/menu.css +35 -21
  78. package/css/menubar.css +36 -20
  79. package/css/message-feed.css +172 -51
  80. package/css/navigation-menu.css +172 -11
  81. package/css/notification.css +435 -108
  82. package/css/number-field.css +22 -0
  83. package/css/page-header.css +3 -1
  84. package/css/pagination.css +126 -24
  85. package/css/password-input.css +40 -8
  86. package/css/pie-chart.css +247 -0
  87. package/css/popconfirm.css +44 -50
  88. package/css/popover.css +50 -14
  89. package/css/progress.css +308 -10
  90. package/css/prompt-input.css +36 -0
  91. package/css/question-flow.css +97 -57
  92. package/css/radar-chart.css +233 -0
  93. package/css/radio-group.css +363 -5
  94. package/css/rating.css +21 -23
  95. package/css/reasoning.css +61 -54
  96. package/css/resizable.css +6 -1
  97. package/css/sankey-chart.css +174 -0
  98. package/css/scroll-area.css +32 -0
  99. package/css/scrollbar.css +1 -1
  100. package/css/select.css +74 -55
  101. package/css/side-nav.css +173 -24
  102. package/css/signature-pad.css +16 -1
  103. package/css/skeleton.css +13 -14
  104. package/css/slider.css +35 -13
  105. package/css/sortable.css +37 -7
  106. package/css/sparkline.css +164 -0
  107. package/css/spinner.css +50 -22
  108. package/css/splitter.css +7 -1
  109. package/css/statistic.css +10 -0
  110. package/css/steps.css +248 -29
  111. package/css/switch.css +23 -48
  112. package/css/table.css +175 -118
  113. package/css/tabs.css +231 -19
  114. package/css/tag-group.css +27 -3
  115. package/css/tag.css +53 -12
  116. package/css/tags-input.css +40 -3
  117. package/css/text-field.css +19 -0
  118. package/css/time-field.css +20 -1
  119. package/css/time-picker.css +103 -17
  120. package/css/time-range-picker.css +45 -16
  121. package/css/timeline.css +1 -1
  122. package/css/toggle-group.css +4 -4
  123. package/css/toggle.css +4 -3
  124. package/css/tool-call.css +59 -51
  125. package/css/toolbar.css +101 -3
  126. package/css/tooltip.css +15 -2
  127. package/css/tour.css +42 -12
  128. package/css/transfer.css +98 -75
  129. package/css/tree-select.css +232 -53
  130. package/css/tree.css +271 -55
  131. package/css/truncate.css +55 -12
  132. package/css/typography.css +93 -0
  133. package/css/virtualizer.css +56 -0
  134. package/css/watermark.css +23 -0
  135. package/family/action-control.css +14 -5
  136. package/family/chart.css +480 -0
  137. package/family/check-mark.css +94 -0
  138. package/family/choice-card.css +97 -0
  139. package/family/collection-item.css +9 -15
  140. package/family/field-chrome.css +1 -12
  141. package/family/loading-ring.css +95 -0
  142. package/family/material.css +72 -0
  143. package/family/menu-choice.css +56 -0
  144. package/family/motion.css +207 -10
  145. package/family/tag-list.css +59 -0
  146. package/index.css +13824 -4030
  147. package/index.unlayered.css +13403 -3605
  148. package/package.json +20 -8
  149. package/css/gradient-text.css +0 -91
  150. package/css/icon-wrapper.css +0 -81
  151. package/css/segmented.css +0 -280
  152. package/css/toast.css +0 -499
@@ -7,9 +7,12 @@
7
7
  @import '../family/field-chrome.css';
8
8
  @import '../family/action-control.css';
9
9
  @import '../family/collection-item.css';
10
+ @import '../family/loading-ring.css';
10
11
 
11
12
  /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
12
13
  @import '../family/motion.css';
14
+ @import '../family/material.css';
15
+ @import '../family/tag-list.css';
13
16
 
14
17
  @layer xihan.components {
15
18
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -25,8 +28,8 @@
25
28
  /* 落定那一侧的可用宽度,同样由定位引擎经内联自定义属性写进来,行内轴据此夹取;
26
29
  引擎没算(尚未落位 / 无引擎 / 贴边归零)时退到视口宽 */
27
30
  --xh-_tree-select-available-w: 100vw;
28
- /* 触发器实测宽度,由定位引擎经内联自定义属性写进来,内容层拿它当最小宽的下界;
29
- 引擎没算(尚未落位 / 无引擎)时为 0,max 取静态档,等于不抬宽 */
31
+ /* 字段盒实测宽度,由定位引擎经内联自定义属性写进来,内容层与它等宽;
32
+ 引擎没算(尚未落位 / 无引擎)时为 0,面板取下界 */
30
33
  --xh-_tree-select-anchor-w: 0px;
31
34
  /* 独立浮层先截断祖先方向,再按自身 placement 开启一侧。 */
32
35
  --xh-_overlay-enter-up: 0;
@@ -216,6 +219,18 @@
216
219
  color: var(--xh-fg-disabled);
217
220
  }
218
221
 
222
+ /* 标签行:触发器里收着可见标签与 +N 那一枚的一行,行里每一枚都是 tag(data-scope="tag"),样子归 tag.css;
223
+ 行怎么排、标签怎么进出归标签行家族配方(family/tag-list.css),这里只把使用者的间距槽接进去 */
224
+ [data-scope='tree-select'][data-part='tag-list'] {
225
+ --xh-tag-list-gap: var(--xh-tree-select-tag-list-gap, var(--xh-space-1));
226
+ }
227
+
228
+ /* 标签行露面时 value-text 让位:两者是触发器里的同一格,占位文字与标签不同屏。
229
+ value-text 留在 DOM 里,trigger 的可及名仍从它取到完整的选中项文本 */
230
+ [data-scope='tree-select'][data-part='trigger']:has([data-part='tag-list']:not([hidden])) [data-part='value-text'] {
231
+ display: none;
232
+ }
233
+
219
234
  /* 有值时清空钮顶上来,箭头让位:两个图标并排堆着,用户分不清点哪个。
220
235
  判据查的是盒里此刻真有一颗没收起的清空钮,不是「有没有值」——
221
236
  作者没摆清空钮时箭头照常留着,不然让了位没人接,指示符整个消失 */
@@ -229,7 +244,7 @@
229
244
  align-items: center;
230
245
  justify-content: center;
231
246
  color: var(--xh-tree-select-indicator-fg, var(--xh-fg-muted));
232
- transition: rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter-strong);
247
+ transition: rotate var(--xh-motion-duration-nudge) var(--xh-motion-ease-enter-strong);
233
248
  }
234
249
 
235
250
  [data-scope='tree-select'][data-part='indicator'][hidden] {
@@ -275,45 +290,35 @@
275
290
  }
276
291
 
277
292
  [data-scope='tree-select'][data-part='content'] {
293
+ /* 材质由家族配方画(family/material.css 的 frosted 面:描边、底、1px 顶光、前景、投影与背景滤镜),
294
+ 这里只把使用者槽接进来 */
295
+ --xh-frosted-bg: var(--xh-tree-select-content-bg, var(--xh-_material-bg));
296
+ --xh-frosted-border: var(--xh-tree-select-content-border, var(--xh-_material-border));
297
+ --xh-frosted-fg: var(--xh-tree-select-content-fg, var(--xh-_material-fg));
298
+ --xh-frosted-shadow: var(--xh-tree-select-content-shadow, var(--xh-_material-shadow));
299
+ --xh-frosted-backdrop: var(--xh-tree-select-content-backdrop, var(--xh-_material-backdrop));
300
+ --xh-frosted-highlight: var(--xh-tree-select-content-highlight, var(--xh-_material-highlight));
301
+
278
302
  position: relative;
279
303
  display: flex;
280
304
  flex-direction: column;
281
- /* min 优先于 max,锚点与静态下界也必须受可用宽度约束。 */
282
- min-inline-size: min(
283
- max(var(--xh-tree-select-content-min-w, var(--xh-overlay-min-w)), var(--xh-_tree-select-anchor-w)),
305
+ /* 列表型浮层与字段盒等宽:长选项在条目里截断,面板不随最长的一条变宽。锚点比下界还窄时取下界,
306
+ 比可用区还宽时收成可用宽度;要封顶由作者另写上界槽 */
307
+ inline-size: min(
308
+ max(var(--xh-tree-select-content-min-w, var(--xh-overlay-menu-min-w)), var(--xh-_tree-select-anchor-w)),
284
309
  var(--xh-_tree-select-available-w)
285
310
  );
286
- /* 静态档与落位后的可用宽度取小:窄视口下面板伸出去,最右侧的内容就点不到了 */
287
- max-inline-size: min(var(--xh-tree-select-content-max-w, var(--xh-overlay-max-w)), var(--xh-_tree-select-available-w));
288
- max-block-size: min(var(--xh-tree-select-content-max-h, var(--xh-overlay-max-h)), var(--xh-_tree-select-available-h));
311
+ max-inline-size: var(--xh-tree-select-content-max-w, none);
312
+ max-block-size: min(var(--xh-tree-select-content-max-h, var(--xh-overlay-menu-max-h)), var(--xh-_tree-select-available-h));
289
313
  padding-block: var(--xh-tree-select-content-py, var(--xh-space-1));
290
314
  padding-inline: var(--xh-tree-select-content-px, var(--xh-space-1));
291
- border: var(--xh-stroke-thin) solid var(--xh-tree-select-content-border, var(--xh-material-frosted-border));
292
315
  border-radius: var(--xh-tree-select-content-radius, var(--xh-shape-overlay));
293
- background: var(--xh-tree-select-content-bg, var(--xh-material-frosted-bg));
294
- color: var(--xh-tree-select-content-fg, var(--xh-material-frosted-fg));
295
- box-shadow: var(--xh-tree-select-content-shadow, var(--xh-material-frosted-shadow));
296
- -webkit-backdrop-filter: var(--xh-tree-select-content-backdrop, var(--xh-material-frosted-backdrop));
297
- backdrop-filter: var(--xh-tree-select-content-backdrop, var(--xh-material-frosted-backdrop));
298
- /* 两轴一起给:深层节点靠缩进往行末推,横向溢出是常态,得够得着 */
299
- overflow: auto;
300
- /* 浮层滚动面:滚到头不把页面一起带走 */
301
- overscroll-behavior: contain;
316
+ /* 面板只是外壳,滚动归树:搜索框与底部操作区因此钉在树的上下沿,不随行滚走。
317
+ 行的悬停底色是方角,滚到两端时会探出外壳的圆角 */
318
+ overflow: hidden;
302
319
  pointer-events: auto;
303
320
  }
304
321
 
305
- /* 顶光位于真实边框内侧,不参与布局或拦截树行操作。 */
306
- [data-scope='tree-select'][data-part='content']::before {
307
- content: '';
308
- position: absolute;
309
- inset-block-start: 0;
310
- inset-inline: 0;
311
- block-size: var(--xh-stroke-thin);
312
- border-radius: inherit;
313
- background: var(--xh-tree-select-content-highlight, var(--xh-material-frosted-highlight));
314
- pointer-events: none;
315
- }
316
-
317
322
  /* 这里刻意没有 [data-part='content'][hidden] { display: none },与 dialog 一致:
318
323
  收起态连接层会给 content 打 hidden,而上面那条 display 正是要盖掉 UA 的
319
324
  [hidden]{display:none}——退场动画得先能播。presence 读的是 content 的 animationName,
@@ -325,7 +330,8 @@
325
330
  outline: none;
326
331
  }
327
332
 
328
- [data-scope='tree-select'][data-part='content'][data-state='open'] {
333
+ /* 挂载时就开着的那一段(data-instant)属于首帧,直接呈现 */
334
+ [data-scope='tree-select'][data-part='content'][data-state='open']:not([data-instant]) {
329
335
  animation: xh-overlay-slide-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
330
336
  }
331
337
 
@@ -335,17 +341,75 @@
335
341
  animation: xh-overlay-slide-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
336
342
  }
337
343
 
338
- /* 边框、底色与滚动都归 content,树本身只把行竖着排开 */
344
+ /* 边框与底色归 content,树只管滚动并把行竖着排开。
345
+ 两轴都滚:深层节点靠缩进往行末推,横向溢出是常态,得够得着。
346
+ min-block-size: 0 是弹性子项能真正滚动的前提——不给的话它按内容撑高,
347
+ 外壳的 max-block-size 就只会把溢出的部分裁掉而不是交给它滚 */
339
348
  [data-scope='tree-select'][data-part='tree'] {
340
349
  display: flex;
350
+ flex: 1 1 auto;
341
351
  flex-direction: column;
342
352
  gap: var(--xh-tree-select-tree-gap, var(--xh-list-option-gap));
353
+ min-block-size: 0;
354
+ overflow: auto;
355
+ /* 浮层滚动面:滚到头不把页面一起带走 */
356
+ overscroll-behavior: contain;
357
+ transition: opacity var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
358
+ }
359
+
360
+ /* 刷新:已有节点时后台在取,保留上一帧,树按 micro 淡下,数据到了再淡回;在途占位此时让位 */
361
+ [data-scope='tree-select'][data-part='tree'][aria-busy='true'] {
362
+ opacity: var(--xh-state-disabled-opacity);
343
363
  }
344
364
 
345
365
  [data-scope='tree-select'][data-part='tree'][hidden] {
346
366
  display: none;
347
367
  }
348
368
 
369
+ /* 浮层内搜索框:排在树上方,框自己不画边不画底,只在底下画一道分隔线把它与树分开;边界归面板 */
370
+ /* 面板内嵌的搜索框:通栏一行,控件高与字号随尺寸档,只画一道面内分隔的下划线(取面材质的分隔令牌);
371
+ 重置与占位前景由字段家族按 data-xh-field-input 给,占位与值文本的占位同一支组件槽 */
372
+ [data-scope='tree-select'][data-part='input'] {
373
+ --xh-field-placeholder-fg: var(--xh-tree-select-placeholder-fg, var(--xh-fg-subtle));
374
+
375
+ flex: none;
376
+ min-inline-size: 0;
377
+ block-size: var(--xh-tree-select-input-h, var(--xh-_tree-select-h));
378
+ margin-block-end: var(--xh-tree-select-input-gap, var(--xh-space-1));
379
+ padding-block: 0;
380
+ padding-inline: var(--xh-tree-select-input-px, var(--xh-_tree-select-px));
381
+ border: none;
382
+ border-block-end: var(--xh-stroke-thin) solid var(--xh-tree-select-search-divider, var(--xh-material-frosted-separator));
383
+ background: none;
384
+ color: inherit;
385
+ font-family: inherit;
386
+ font-size: var(--xh-tree-select-input-font-size, var(--xh-_tree-select-font-size));
387
+ line-height: var(--xh-leading-normal);
388
+ }
389
+
390
+ /* 聚焦不画环:插入符就是焦点指示。框没有外壳,全局那圈键盘焦点环只会压在通栏一行上 */
391
+ [data-scope='tree-select'][data-part='input']:focus-visible {
392
+ outline: none;
393
+ }
394
+
395
+ [data-scope='tree-select'][data-part='input'][hidden] {
396
+ display: none;
397
+ }
398
+
399
+ /* 自动填充:引擎给填过的框强加一套系统色,盖得过 background 与 color。
400
+ 底改用 inset 阴影铺回来,字改用 -webkit-text-fill-color 接回来;
401
+ 两个选择器名分写两条,一条列表里只要有一个名字不被认识整条就被丢弃。
402
+ 搜索框排在浮层面里,铺的底取浮起面而不是页面底 */
403
+ [data-scope='tree-select'][data-part='input']:autofill {
404
+ box-shadow: inset 0 0 0 100vmax var(--xh-tree-select-input-autofill-bg, var(--xh-bg-surface));
405
+ -webkit-text-fill-color: var(--xh-tree-select-input-autofill-fg, var(--xh-fg-default));
406
+ }
407
+
408
+ [data-scope='tree-select'][data-part='input']:-webkit-autofill {
409
+ box-shadow: inset 0 0 0 100vmax var(--xh-tree-select-input-autofill-bg, var(--xh-bg-surface));
410
+ -webkit-text-fill-color: var(--xh-tree-select-input-autofill-fg, var(--xh-fg-default));
411
+ }
412
+
349
413
  /* 空态占位:待在 content 里、tree 的兄弟(role=tree 只许拥有 treeitem 与 group)。
350
414
  盒与阴影归 content,这里只画一句居中的次要文字 */
351
415
  [data-scope='tree-select'][data-part='empty'] {
@@ -366,7 +430,10 @@
366
430
 
367
431
  /* 在途占位:与空态占位同一个位置、同一副观感,两者不同屏 */
368
432
  [data-scope='tree-select'][data-part='loading'] {
369
- display: block;
433
+ display: flex;
434
+ align-items: center;
435
+ justify-content: center;
436
+ gap: var(--xh-_tree-select-gap);
370
437
  flex: none;
371
438
  padding-block: var(--xh-tree-select-loading-py, var(--xh-space-3));
372
439
  padding-inline: var(--xh-tree-select-loading-px, var(--xh-_tree-select-row-px));
@@ -380,6 +447,15 @@
380
447
  display: none;
381
448
  }
382
449
 
450
+ /* 首次加载:文案前一枚加载环,由加载环配方画(连接层投影 data-xh-loading-ring 与 data-loading),
451
+ 直径取所在尺寸档的图标档 */
452
+ [data-scope='tree-select'][data-part='loading']::before {
453
+ content: '';
454
+ flex: none;
455
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-md));
456
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-md));
457
+ }
458
+
383
459
  /* 浮层底部的操作区:排在树下面,与树之间用一条分隔线断开;不参与收缩,行再多也压不扁它 */
384
460
  [data-scope='tree-select'][data-part='footer'] {
385
461
  display: flex;
@@ -432,7 +508,7 @@
432
508
  --xh-collection-inline-padding: var(--xh-tree-select-item-px, var(--xh-_tree-select-row-px));
433
509
  --xh-collection-gap: var(--xh-tree-select-item-gap, var(--xh-_tree-select-gap));
434
510
  --xh-collection-font-size: var(--xh-tree-select-item-font-size, var(--xh-_tree-select-font-size));
435
- --xh-collection-radius: var(--xh-tree-select-item-radius, var(--xh-shape-control));
511
+ --xh-collection-radius: var(--xh-tree-select-item-radius, var(--xh-shape-inset));
436
512
  --xh-collection-glyph-size: var(--xh-tree-select-icon-size, var(--xh-_collection-glyph-size));
437
513
  --xh-collection-fg-rest: var(--xh-tree-select-item-fg, var(--xh-material-frosted-fg));
438
514
  --xh-collection-fg-hover: var(--xh-tree-select-item-fg, var(--xh-material-frosted-fg));
@@ -570,7 +646,7 @@
570
646
  /* 家族只按行的 aria-selected 显示对号:分支行的 aria-selected 在 branch 上、半选没有选中语义,
571
647
  这两档按标记自身的 data-selected / data-indeterminate 显形 */
572
648
  [data-scope='tree-select'][data-part='item-indicator']:is([data-selected], [data-indeterminate]) {
573
- visibility: visible;
649
+ opacity: 1;
574
650
  }
575
651
 
576
652
  /* 分支行的禁用不在行的 aria-disabled 上(那在 branch 上),标记按自身的 data-disabled 降级 */
@@ -591,7 +667,7 @@
591
667
  justify-content: center;
592
668
  inline-size: var(--xh-tree-select-branch-indicator-size, var(--xh-control-indicator-size));
593
669
  color: var(--xh-tree-select-branch-indicator-fg, var(--xh-fg-subtle));
594
- transition: rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter-strong);
670
+ transition: rotate var(--xh-motion-duration-nudge) var(--xh-motion-ease-enter-strong);
595
671
  }
596
672
 
597
673
  [data-scope='tree-select'][data-part='branch-trigger'][hidden],
@@ -599,20 +675,15 @@
599
675
  display: none;
600
676
  }
601
677
 
602
- /* 收起时朝向行首、展开时转向下方,与逻辑书写方向一致:
603
- rtl 下 inline-start 在右侧,箭头因此要反着转 */
604
- [data-scope='tree-select'][data-part='branch-trigger'][data-state='open'],
605
- [data-scope='tree-select'][data-part='branch-indicator'][data-state='open'] {
606
- rotate: 90deg;
607
- }
608
-
609
- [dir='rtl'] [data-scope='tree-select'][data-part='branch-trigger'],
610
- [dir='rtl'] [data-scope='tree-select'][data-part='branch-indicator'] {
611
- rotate: 180deg;
678
+ /* 收起时指向行尾、展开时转向下方,与逻辑书写方向一致:rtl 下行尾在左侧,收起的箭头转半圈;
679
+ 旋转角乘书写方向的符号(ltr 0deg、rtl 180deg),按就近的 dir 走 */
680
+ [data-scope='tree-select'][data-part='branch-trigger'],
681
+ [data-scope='tree-select'][data-part='branch-indicator'] {
682
+ rotate: calc(90deg - 90deg * var(--xh-direction-sign));
612
683
  }
613
684
 
614
- [dir='rtl'] [data-scope='tree-select'][data-part='branch-trigger'][data-state='open'],
615
- [dir='rtl'] [data-scope='tree-select'][data-part='branch-indicator'][data-state='open'] {
685
+ [data-scope='tree-select'][data-part='branch-trigger'][data-state='open'],
686
+ [data-scope='tree-select'][data-part='branch-indicator'][data-state='open'] {
616
687
  rotate: 90deg;
617
688
  }
618
689
 
@@ -630,6 +701,69 @@
630
701
  padding-inline-start: var(--xh-tree-select-indent, var(--xh-space-4));
631
702
  }
632
703
 
704
+ /* 分支首次展开取子项时的三种占位落在子层的位置(与子层同一缩进),与整树的占位同一副样子:
705
+ 在途一枚加载环 + 文案、成功为空一句文案、失败一枚警示字形 + 说明。它们不是 treeitem,不接悬停与按下 */
706
+ [data-scope='tree-select']:is([data-part='branch-loading'], [data-part='branch-empty'], [data-part='branch-error']) {
707
+ display: flex;
708
+ align-items: center;
709
+ gap: var(--xh-_tree-select-gap);
710
+ padding-block: var(--xh-tree-select-branch-status-py, var(--xh-_tree-select-row-py));
711
+ padding-inline: calc(var(--xh-tree-select-indent, var(--xh-space-4)) + var(--xh-_tree-select-row-px)) var(--xh-_tree-select-row-px);
712
+ color: var(--xh-tree-select-branch-status-fg, var(--xh-material-frosted-fg-muted));
713
+ font-size: var(--xh-tree-select-branch-status-font-size, var(--xh-_tree-select-font-size));
714
+ line-height: var(--xh-leading-none);
715
+ user-select: none;
716
+ }
717
+
718
+ [data-scope='tree-select'][data-part='branch-loading']::before {
719
+ content: '';
720
+ flex: none;
721
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-md));
722
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-md));
723
+ }
724
+
725
+ /* 失败的非颜色通道:一枚警示字形,取危险色 */
726
+ [data-scope='tree-select'][data-part='branch-error']::before {
727
+ content: '';
728
+ flex: none;
729
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-md));
730
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-md));
731
+ background-color: var(--xh-tree-select-branch-error-fg, var(--xh-fg-danger));
732
+ -webkit-mask: var(--xh-glyph-mark-warning) center / contain no-repeat;
733
+ mask: var(--xh-glyph-mark-warning) center / contain no-repeat;
734
+ }
735
+
736
+ /* 重试钮排在失败说明之下、与说明同一缩进;指针入口,键盘在出错的分支行上按 Enter / Space 重试 */
737
+ [data-scope='tree-select'][data-part='branch-retry-trigger'] {
738
+ align-self: flex-start;
739
+ margin-inline-start: calc(var(--xh-tree-select-indent, var(--xh-space-4)) + var(--xh-_tree-select-row-px));
740
+ padding: 0;
741
+ border: 0;
742
+ background: none;
743
+ color: var(--xh-tree-select-branch-retry-fg, var(--xh-fg-brand));
744
+ font: inherit;
745
+ font-size: var(--xh-tree-select-branch-status-font-size, var(--xh-_tree-select-font-size));
746
+ line-height: var(--xh-leading-normal);
747
+ text-decoration-line: underline;
748
+ text-decoration-color: transparent;
749
+ text-underline-offset: var(--xh-space-0_5);
750
+ cursor: pointer;
751
+ transition: text-decoration-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
752
+ }
753
+
754
+ [data-scope='tree-select'][data-part='branch-retry-trigger']:hover {
755
+ text-decoration-color: currentColor;
756
+ }
757
+
758
+ [data-scope='tree-select'][data-part='branch-retry-trigger']:disabled {
759
+ color: var(--xh-fg-disabled);
760
+ cursor: not-allowed;
761
+ }
762
+
763
+ [data-scope='tree-select']:is([data-part='branch-loading'], [data-part='branch-empty'], [data-part='branch-error'], [data-part='branch-retry-trigger'])[hidden] {
764
+ display: none;
765
+ }
766
+
633
767
  /* 收起态的唯一开关:display 盖掉了 UA 的 [hidden]{display:none},漏了这条整棵子树就收不起来 */
634
768
  [data-scope='tree-select'][data-part='branch-content'][hidden],
635
769
  [data-scope='tree-select'][data-part='branch-content'][data-state='closed'] {
@@ -650,6 +784,8 @@
650
784
  mask: var(--xh-glyph-mark-chevron-down) center / contain no-repeat;
651
785
  }
652
786
 
787
+ /* 勾与半选杠叠成两层遮罩,按状态只换两层的尺寸:离开半选淡出的那一段里横杠留着,
788
+ 淡出播完才换回勾——不在淡出途中闪出一枚勾 */
653
789
  [data-scope='tree-select'][data-part='item-indicator']:empty::before {
654
790
  content: '';
655
791
  display: inline-block;
@@ -657,13 +793,21 @@
657
793
  block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
658
794
  vertical-align: var(--xh-glyph-baseline-shift);
659
795
  background-color: currentColor;
660
- -webkit-mask: var(--xh-glyph-mark-check) center / contain no-repeat;
661
- mask: var(--xh-glyph-mark-check) center / contain no-repeat;
796
+ -webkit-mask: var(--xh-glyph-mark-check) center / 100% 100% no-repeat, var(--xh-glyph-mark-minus) center / 0 0 no-repeat;
797
+ mask: var(--xh-glyph-mark-check) center / 100% 100% no-repeat, var(--xh-glyph-mark-minus) center / 0 0 no-repeat;
798
+ transition:
799
+ -webkit-mask-size 0s var(--xh-motion-duration-micro),
800
+ mask-size 0s var(--xh-motion-duration-micro);
801
+ }
802
+
803
+ [data-scope='tree-select'][data-part='item-indicator'][data-selected]:empty::before {
804
+ transition-delay: 0s;
662
805
  }
663
806
 
664
807
  [data-scope='tree-select'][data-part='item-indicator'][data-indeterminate]:not([data-selected]):empty::before {
665
- -webkit-mask-image: var(--xh-glyph-mark-minus);
666
- mask-image: var(--xh-glyph-mark-minus);
808
+ -webkit-mask-size: 0 0, 100% 100%;
809
+ mask-size: 0 0, 100% 100%;
810
+ transition-delay: 0s;
667
811
  }
668
812
 
669
813
  [data-scope='tree-select'][data-part='branch-trigger']:empty::before,
@@ -696,4 +840,39 @@
696
840
  display: none;
697
841
  }
698
842
  }
843
+
844
+ @media (forced-colors: active) {
845
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
846
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
847
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
848
+ 条目面上取 CanvasText;按下与没被高亮时的悬停,家族把条目涂成 Highlight,字形换 HighlightText;
849
+ 字段面上取 CanvasText;
850
+ 落在 Canvas 面上的取 CanvasText;
851
+ 禁用取 GrayText */
852
+ [data-scope='tree-select'][data-part='branch-error']::before,
853
+ [data-scope='tree-select'][data-part='indicator']:empty::before,
854
+ [data-scope='tree-select'][data-part='item-indicator']:empty::before,
855
+ [data-scope='tree-select']:is([data-part='branch-trigger'], [data-part='branch-indicator']):empty::before {
856
+ forced-color-adjust: none;
857
+ background-color: CanvasText;
858
+ }
859
+
860
+ [data-scope='tree-select'][data-part='clear-trigger']:empty::before {
861
+ forced-color-adjust: none;
862
+ background-color: ButtonText;
863
+ }
864
+
865
+ [data-scope='tree-select'][data-part='item']:not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error]):is(:active, [data-pressed], :hover:not(:focus-visible, [data-highlighted])) [data-scope='tree-select'][data-part='item-indicator']:empty::before,
866
+ [data-scope='tree-select'][data-part='branch-control']:not([aria-disabled='true'], [data-disabled], [aria-busy='true'], [data-error]):is(:active, [data-pressed], :hover:not(:focus-visible, [data-highlighted])) [data-scope='tree-select']:is([data-part='branch-trigger'], [data-part='branch-indicator']):empty::before,
867
+ [data-scope='tree-select'][data-part='clear-trigger']:not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
868
+ background-color: HighlightText;
869
+ }
870
+
871
+ [data-scope='tree-select'][data-part='control'][data-disabled] [data-scope='tree-select'][data-part='indicator']:empty::before,
872
+ [data-scope='tree-select'][data-part='item']:is([aria-disabled='true'], [data-disabled]) [data-scope='tree-select'][data-part='item-indicator']:empty::before,
873
+ [data-scope='tree-select'][data-part='branch-control']:is([aria-disabled='true'], [data-disabled]) [data-scope='tree-select']:is([data-part='branch-trigger'], [data-part='branch-indicator']):empty::before,
874
+ [data-scope='tree-select'][data-part='clear-trigger'][data-disabled]:empty::before {
875
+ background-color: GrayText;
876
+ }
877
+ }
699
878
  }