@xihan-ui/styles 1.0.0 → 2.0.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 (171) hide show
  1. package/CHANGELOG.md +5146 -6
  2. package/README.md +22 -2
  3. package/component-tokens.d.ts +4212 -0
  4. package/component-tokens.js +2 -0
  5. package/components.tokens.json +80116 -0
  6. package/css/accordion.css +131 -75
  7. package/css/affix.css +16 -1
  8. package/css/alert.css +114 -67
  9. package/css/anchor.css +99 -27
  10. package/css/approval.css +461 -0
  11. package/css/avatar-group.css +14 -6
  12. package/css/avatar.css +33 -9
  13. package/css/back-top.css +103 -48
  14. package/css/badge.css +42 -14
  15. package/css/bar-code.css +62 -0
  16. package/css/breadcrumb.css +40 -21
  17. package/css/button-group.css +146 -24
  18. package/css/button.css +116 -99
  19. package/css/calendar-picker.css +448 -0
  20. package/css/calendar-range-picker.css +517 -0
  21. package/css/card.css +54 -77
  22. package/css/carousel.css +412 -68
  23. package/css/cascader.css +429 -246
  24. package/css/checkbox-group.css +225 -77
  25. package/css/checkbox.css +174 -37
  26. package/css/clipboard.css +266 -47
  27. package/css/code-view.css +279 -0
  28. package/css/collapsible.css +108 -63
  29. package/css/color-field.css +158 -0
  30. package/css/color-picker.css +172 -184
  31. package/css/color-slider.css +274 -0
  32. package/css/color-swatch-picker.css +243 -0
  33. package/css/color-swatch.css +35 -0
  34. package/css/combobox.css +267 -273
  35. package/css/command.css +399 -0
  36. package/css/context-menu.css +183 -108
  37. package/css/date-field.css +94 -121
  38. package/css/date-picker.css +486 -249
  39. package/css/date-range-picker.css +559 -0
  40. package/css/description.css +51 -0
  41. package/css/descriptions.css +133 -87
  42. package/css/dialog.css +247 -68
  43. package/css/diff-view.css +428 -0
  44. package/css/download-trigger.css +155 -46
  45. package/css/drawer.css +194 -111
  46. package/css/editable.css +172 -152
  47. package/css/empty-state.css +80 -11
  48. package/css/field-array.css +190 -0
  49. package/css/field.css +84 -31
  50. package/css/fieldset.css +51 -18
  51. package/css/file-upload.css +134 -36
  52. package/css/flex.css +20 -1
  53. package/css/float-button.css +238 -57
  54. package/css/floating-panel.css +173 -92
  55. package/css/focus.css +46 -0
  56. package/css/forced-colors.css +93 -0
  57. package/css/form.css +254 -77
  58. package/css/gradient-text.css +34 -10
  59. package/css/grid.css +516 -5
  60. package/css/heatmap.css +87 -29
  61. package/css/highlight.css +20 -5
  62. package/css/hover-card.css +97 -47
  63. package/css/icon-wrapper.css +15 -3
  64. package/css/icon.css +74 -8
  65. package/css/image-cropper.css +143 -29
  66. package/css/image-viewer.css +150 -89
  67. package/css/image.css +30 -1
  68. package/css/infinite-scroll.css +51 -1
  69. package/css/input-group.css +277 -0
  70. package/css/json-viewer.css +102 -45
  71. package/css/kbd.css +72 -0
  72. package/css/label.css +78 -0
  73. package/css/layers.css +5 -0
  74. package/css/layout.css +175 -31
  75. package/css/list.css +24 -3
  76. package/css/listbox.css +208 -67
  77. package/css/loading-bar.css +52 -3
  78. package/css/log.css +144 -9
  79. package/css/markdown-stream.css +149 -0
  80. package/css/marquee.css +27 -1
  81. package/css/masonry.css +7 -0
  82. package/css/matrix-code.css +67 -0
  83. package/css/mention.css +234 -152
  84. package/css/menu.css +180 -83
  85. package/css/menubar.css +204 -136
  86. package/css/message-feed.css +197 -0
  87. package/css/navigation-menu.css +144 -90
  88. package/css/notification.css +178 -100
  89. package/css/number-animation.css +7 -0
  90. package/css/number-field.css +120 -277
  91. package/css/overlay-arrow.css +12 -7
  92. package/css/page-header.css +99 -14
  93. package/css/pagination.css +175 -135
  94. package/css/password-input.css +222 -219
  95. package/css/pin-input.css +101 -67
  96. package/css/pointer.css +36 -0
  97. package/css/popconfirm.css +185 -94
  98. package/css/popover.css +121 -77
  99. package/css/progress.css +60 -12
  100. package/css/prompt-input.css +224 -0
  101. package/css/question-flow.css +513 -0
  102. package/css/radio-group.css +147 -30
  103. package/css/rating.css +193 -43
  104. package/css/reasoning.css +336 -0
  105. package/css/reset.css +84 -6
  106. package/css/resizable.css +237 -0
  107. package/css/scroll-area.css +68 -7
  108. package/css/scrollbar.css +47 -13
  109. package/css/segmented.css +94 -29
  110. package/css/select.css +288 -274
  111. package/css/separator.css +101 -1
  112. package/css/side-nav.css +133 -75
  113. package/css/signature-pad.css +74 -54
  114. package/css/skeleton.css +84 -11
  115. package/css/slider.css +132 -39
  116. package/css/sortable.css +198 -0
  117. package/css/spinner.css +126 -12
  118. package/css/splitter.css +29 -8
  119. package/css/statistic.css +55 -0
  120. package/css/steps.css +206 -72
  121. package/css/switch.css +268 -36
  122. package/css/table.css +695 -118
  123. package/css/tabs.css +604 -42
  124. package/css/tag-group.css +219 -0
  125. package/css/tag.css +118 -28
  126. package/css/tags-input.css +156 -274
  127. package/css/text-field.css +101 -249
  128. package/css/time-field.css +100 -128
  129. package/css/time-picker.css +323 -252
  130. package/css/time-range-picker.css +719 -0
  131. package/css/timeline.css +202 -68
  132. package/css/timer.css +56 -52
  133. package/css/timestamp.css +34 -0
  134. package/css/toast.css +373 -144
  135. package/css/toggle-group.css +309 -76
  136. package/css/toggle.css +116 -85
  137. package/css/tone.css +47 -0
  138. package/css/tool-call.css +421 -0
  139. package/css/toolbar.css +181 -33
  140. package/css/tooltip.css +107 -36
  141. package/css/tour.css +183 -152
  142. package/css/transfer.css +351 -138
  143. package/css/tree-select.css +323 -216
  144. package/css/tree.css +294 -62
  145. package/css/truncate.css +65 -0
  146. package/css/typography.css +228 -5
  147. package/css/undefined.css +10 -1
  148. package/css/virtualizer.css +9 -10
  149. package/css/visually-hidden.css +7 -0
  150. package/css/watermark.css +7 -0
  151. package/family/action-control.css +487 -0
  152. package/family/collection-item.css +721 -0
  153. package/family/field-chrome.css +298 -0
  154. package/family/motion.css +166 -0
  155. package/family/swatch.css +59 -0
  156. package/index.css +41705 -126
  157. package/index.unlayered.css +33692 -17339
  158. package/package.json +51 -17
  159. package/css/calendar.css +0 -367
  160. package/css/code-block.css +0 -91
  161. package/css/composer.css +0 -134
  162. package/css/countdown.css +0 -26
  163. package/css/dynamic-input.css +0 -220
  164. package/css/ellipsis.css +0 -52
  165. package/css/hotkeys.css +0 -90
  166. package/css/popselect.css +0 -391
  167. package/css/qr-code.css +0 -55
  168. package/css/result.css +0 -119
  169. package/css/space.css +0 -117
  170. package/css/thread.css +0 -120
  171. package/css/time.css +0 -25
@@ -1,29 +1,15 @@
1
- @layer xihan.motion {
2
- /* 动画名的查找只认「文档里有没有这个名字」:引用别处文件里的名字,那份文件不一定在场,
3
- 动画会静默地整个不跑。每份皮肤都能单独引入,所以它用到的关键帧都写在自己身上。 */
4
- @keyframes xh-pop-in {
5
- from {
6
- opacity: 0;
7
- scale: var(--xh-motion-scale-enter);
8
- }
1
+ /*
2
+ * Copyright (c) 2021-Present XiHanFun and contributors.
3
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
4
+ */
9
5
 
10
- to {
11
- opacity: 1;
12
- scale: 1;
13
- }
14
- }
15
- @keyframes xh-pop-out {
16
- from {
17
- opacity: 1;
18
- scale: 1;
19
- }
6
+ /* TreeSelect 独立皮肤同时传递引入字段视觉盒、字段内动作与浮层集合行;full bundle 递归去重。 */
7
+ @import '../family/field-chrome.css';
8
+ @import '../family/action-control.css';
9
+ @import '../family/collection-item.css';
20
10
 
21
- to {
22
- opacity: 0;
23
- scale: var(--xh-motion-scale-enter);
24
- }
25
- }
26
- }
11
+ /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
12
+ @import '../family/motion.css';
27
13
 
28
14
  @layer xihan.components {
29
15
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -36,12 +22,23 @@
36
22
  /* 落定那一侧的可用高度,由定位引擎经内联自定义属性写进来,内容层继承它限高;
37
23
  引擎没算(尚未落位 / 无引擎)时不额外限高 */
38
24
  --xh-_tree-select-available-h: 100vh;
25
+ /* 落定那一侧的可用宽度,同样由定位引擎经内联自定义属性写进来,行内轴据此夹取;
26
+ 引擎没算(尚未落位 / 无引擎 / 贴边归零)时退到视口宽 */
27
+ --xh-_tree-select-available-w: 100vw;
28
+ /* 触发器实测宽度,由定位引擎经内联自定义属性写进来,内容层拿它当最小宽的下界;
29
+ 引擎没算(尚未落位 / 无引擎)时为 0,max 取静态档,等于不抬宽 */
30
+ --xh-_tree-select-anchor-w: 0px;
31
+ /* 独立浮层先截断祖先方向,再按自身 placement 开启一侧。 */
32
+ --xh-_overlay-enter-up: 0;
33
+ --xh-_overlay-enter-down: 0;
34
+ --xh-_overlay-enter-left: 0;
35
+ --xh-_overlay-enter-right: 0;
39
36
 
40
37
  /* 树的自绘条浮在内容之上:轨道不上底色,实色轨道会沿内容边缘糊出一条灰带 */
41
38
  --xh-scrollbar-track-bg: transparent;
42
39
 
43
40
  position: fixed;
44
- z-index: var(--xh-tree-select-layer, var(--xh-layer-popover));
41
+ z-index: var(--xh-tree-select-layer, var(--xh-_layer, var(--xh-layer-popover)));
45
42
  /* 空壳不吃指针,交互只发生在 content 里 */
46
43
  pointer-events: none;
47
44
  }
@@ -51,31 +48,41 @@
51
48
  visibility: hidden;
52
49
  }
53
50
 
51
+ /* 位移按实际落位的物理方向给,不随书写方向翻转。 */
52
+ [data-scope='tree-select'][data-part='positioner'][data-placement^='top'] {
53
+ --xh-_overlay-enter-down: 1;
54
+ }
55
+
56
+ [data-scope='tree-select'][data-part='positioner'][data-placement^='bottom'] {
57
+ --xh-_overlay-enter-up: 1;
58
+ }
59
+
60
+ [data-scope='tree-select'][data-part='positioner'][data-placement^='left'] {
61
+ --xh-_overlay-enter-right: 1;
62
+ }
63
+
64
+ [data-scope='tree-select'][data-part='positioner'][data-placement^='right'] {
65
+ --xh-_overlay-enter-left: 1;
66
+ }
67
+
54
68
  /* 私有槽同时声明在根与 positioner 上:浮层被搬到 portal 落点,不是根的后代,继承不过去 */
55
69
  [data-scope='tree-select']:is([data-part='root'], [data-part='positioner']) {
56
- /* 作者塞进指示符槽的图标跟着尺寸档走,与皮肤画的兜底字形同一把尺 */
70
+ /* 作者塞进触发器指示符槽 / 清除钮的图标跟着尺寸档走,与皮肤画的兜底字形同一把尺。这支只管作者放进
71
+ 盒与行里的图标:面板里自绘的状态字形(展开方向、行尾对号与半选杠)是指示符,各部件在自己身上
72
+ 把 --xh-icon-size 改接到 --xh-control-indicator-* 一族的盒尺(§6.5) */
57
73
  --xh-icon-size: var(--xh-tree-select-icon-size, var(--xh-glyph-size-md));
58
74
 
59
- /* 尺寸档只换这几个私有槽,盒与树里的行都从这两处继承它们,换档不必给每个部件各写一条选择器 */
75
+ /* 尺寸档只换这几个私有槽,盒与树里的行都从这两处继承它们,换档不必给每个部件各写一条选择器;
76
+ 形态三档由家族按 control 上的 data-variant 生成,浮层与树不跟着变,皮肤不再声明 */
60
77
  --xh-_tree-select-h: var(--xh-control-h-md);
61
78
  --xh-_tree-select-px: var(--xh-control-px-md);
62
79
  --xh-_tree-select-row-py: var(--xh-list-option-py-md);
63
80
  --xh-_tree-select-row-px: var(--xh-control-px-md);
64
81
  --xh-_tree-select-gap: var(--xh-control-gap-md);
65
82
  --xh-_tree-select-font-size: var(--xh-control-font-md);
66
- --xh-_tree-select-label-font-size: var(--xh-control-font-md);
67
-
68
- /* 形态档只换触发框的描边与底色这三个槽,浮层与树不跟着变:
69
- 变的是「这是个输入位」的表达方式,不是弹出内容的表达方式 */
70
- --xh-_tree-select-border: var(--xh-border-control);
71
- --xh-_tree-select-border-hover: var(--xh-border-control-hover);
72
- --xh-_tree-select-bg: var(--xh-bg-canvas);
73
83
 
74
- /* 选中行的文字色随语气走,没写 data-tone 时退回品牌强调色 */
75
- --xh-_tree-select-selected-fg: var(--xh-_tone-fg, var(--xh-fg-brand-strong));
76
-
77
- /* 聚焦环恒用固定环色;校验失败时换成告警环色 */
78
- --xh-_tree-select-ring: var(--xh-ring-focus);
84
+ /* 强调色随语气走,只落在勾选标记上;没写 data-tone 时退回品牌色 */
85
+ --xh-_tree-select-accent: var(--xh-_tone, var(--xh-fg-brand));
79
86
  }
80
87
 
81
88
  /* 标签一行、盒一行的两行堆叠 */
@@ -83,6 +90,12 @@
83
90
  display: inline-flex;
84
91
  flex-direction: column;
85
92
  gap: var(--xh-tree-select-gap, var(--xh-space-1));
93
+
94
+ /* 不传尺寸时一律这么宽,宽度不随内容走;父容器给不出这么宽时整件跟着收,地板同步让步,不越出父容器。
95
+ 地板不高过缺省宽:使用者把缺省宽钉到底线以下时不必再放开底线 */
96
+ inline-size: var(--xh-tree-select-control-w, var(--xh-control-w));
97
+ min-inline-size: min(var(--xh-tree-select-control-w, var(--xh-control-w)), var(--xh-tree-select-control-min-w, var(--xh-control-min-w)), 100%);
98
+ max-inline-size: 100%;
86
99
  }
87
100
 
88
101
  /* 只要声明了 display 就盖掉了 UA 的 [hidden]{display:none},作者手动收起时要自己还回去 */
@@ -90,29 +103,6 @@
90
103
  display: none;
91
104
  }
92
105
 
93
- /* —— 形态 —— */
94
- /* 形态只决定描边与底色怎么用;用哪族颜色由 data-tone 决定,没写 tone 就沿用原来的中性配色 */
95
- /* outline 就是缺省那一身:描边加画布底,写出来只为让三档并列可读 */
96
- [data-scope='tree-select']:is([data-part='root'], [data-part='positioner'])[data-variant='outline'] {
97
- --xh-_tree-select-border: var(--xh-border-control);
98
- --xh-_tree-select-border-hover: var(--xh-border-control-hover);
99
- --xh-_tree-select-bg: var(--xh-bg-canvas);
100
- }
101
-
102
- /* subtle 是填充式:底色接过「这里能点」的表达,描边随之让位 */
103
- [data-scope='tree-select']:is([data-part='root'], [data-part='positioner'])[data-variant='subtle'] {
104
- --xh-_tree-select-border: transparent;
105
- --xh-_tree-select-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
106
- --xh-_tree-select-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
107
- }
108
-
109
- /* 无框:底与常态描边透明,悬停浮出描边,聚焦由焦点环表达 */
110
- [data-scope='tree-select']:is([data-part='root'], [data-part='positioner'])[data-variant='ghost'] {
111
- --xh-_tree-select-border: transparent;
112
- --xh-_tree-select-border-hover: var(--xh-_tone-border, var(--xh-border-control-hover));
113
- --xh-_tree-select-bg: transparent;
114
- }
115
-
116
106
  /* —— 尺寸 —— */
117
107
  /* 缺省档写在上面的基础规则里 */
118
108
  [data-scope='tree-select']:is([data-part='root'], [data-part='positioner'])[data-size='sm'] {
@@ -123,7 +113,6 @@
123
113
  --xh-_tree-select-row-px: var(--xh-control-px-sm);
124
114
  --xh-_tree-select-gap: var(--xh-control-gap-sm);
125
115
  --xh-_tree-select-font-size: var(--xh-control-font-sm);
126
- --xh-_tree-select-label-font-size: var(--xh-control-font-sm);
127
116
  }
128
117
 
129
118
  [data-scope='tree-select']:is([data-part='root'], [data-part='positioner'])[data-size='lg'] {
@@ -134,12 +123,11 @@
134
123
  --xh-_tree-select-row-px: var(--xh-control-px-lg);
135
124
  --xh-_tree-select-gap: var(--xh-control-gap-lg);
136
125
  --xh-_tree-select-font-size: var(--xh-control-font-lg);
137
- --xh-_tree-select-label-font-size: var(--xh-control-font-lg);
138
126
  }
139
127
 
140
128
  [data-scope='tree-select'][data-part='label'] {
141
129
  color: var(--xh-tree-select-label-fg, var(--xh-fg-default));
142
- font-size: var(--xh-tree-select-label-font-size, var(--xh-_tree-select-label-font-size));
130
+ font-size: var(--xh-tree-select-label-font-size, var(--xh-text-label-size));
143
131
  font-weight: var(--xh-tree-select-label-font-weight, var(--xh-text-label-weight));
144
132
  line-height: var(--xh-leading-none);
145
133
  }
@@ -149,56 +137,36 @@
149
137
  color: var(--xh-fg-subtle);
150
138
  }
151
139
 
152
- /* 盒:描边、底色、控件高度与行内内衬都落在这一层,触发按钮与清空按钮在里面并排 */
140
+ /* 盒:描边、底色、圆角、控件高度、行内内衬、聚焦环、五态与悬停由 Field Chrome 家族画在这一层,
141
+ 触发按钮与清空按钮在里面并排;Tab 停靠点是盒里的 trigger(展开后才移进树里),环由家族的
142
+ :focus-within 画在盒上。组件只映射既有公开槽(shadow 缺省 none:字段静息描边式、无影);盒里是按钮 */
153
143
  [data-scope='tree-select'][data-part='control'] {
154
- display: inline-flex;
155
- align-items: center;
156
- gap: var(--xh-tree-select-control-gap, var(--xh-_tree-select-gap));
157
- min-inline-size: var(--xh-tree-select-control-min-w, var(--xh-control-min-w));
158
- block-size: var(--xh-tree-select-control-h, var(--xh-_tree-select-h));
159
- padding-inline: var(--xh-tree-select-control-px, var(--xh-_tree-select-px));
160
- border: var(--xh-stroke-thin) solid var(--xh-tree-select-control-border, var(--xh-_tree-select-border));
161
- border-radius: var(--xh-tree-select-control-radius, var(--xh-shape-control));
162
- background: var(--xh-tree-select-control-bg, var(--xh-_tree-select-bg));
163
- color: var(--xh-tree-select-control-fg, var(--xh-fg-default));
164
- cursor: pointer;
165
- transition: border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
144
+ --xh-field-control-height: var(--xh-tree-select-control-h, var(--xh-_tree-select-h));
145
+ --xh-field-control-padding-inline: var(--xh-tree-select-control-px, var(--xh-_tree-select-px));
146
+ --xh-field-control-gap: var(--xh-tree-select-control-gap, var(--xh-_tree-select-gap));
147
+ --xh-field-control-min-inline-size: var(--xh-tree-select-control-min-w, var(--xh-control-min-w));
148
+ --xh-field-control-radius: var(--xh-tree-select-control-radius, var(--xh-shape-control));
149
+ --xh-field-bg-rest: var(--xh-tree-select-control-bg, var(--xh-_field-variant-bg-rest));
150
+ --xh-field-bg-hover: var(--xh-tree-select-control-bg-hover, var(--xh-_field-variant-bg-hover));
151
+ --xh-field-bg-read-only: var(--xh-tree-select-control-bg-readonly, var(--xh-_field-variant-bg-read-only));
152
+ --xh-field-bg-disabled: var(--xh-tree-select-control-bg-disabled, var(--xh-_field-variant-bg-disabled));
153
+ --xh-field-fg-rest: var(--xh-tree-select-control-fg, var(--xh-fg-default));
154
+ --xh-field-border-rest: var(--xh-tree-select-control-border, var(--xh-_field-variant-border-rest));
155
+ --xh-field-border-hover: var(--xh-tree-select-control-border-hover, var(--xh-_field-variant-border-hover));
156
+ --xh-field-border-focus: var(--xh-tree-select-control-border-focus, var(--xh-_field-variant-border-focus));
157
+ --xh-field-border-invalid: var(--xh-tree-select-control-border-invalid, var(--xh-_field-variant-border-invalid));
158
+ --xh-field-ring-focus: var(--xh-ring-focus);
159
+ --xh-field-ring-invalid: var(--xh-ring-invalid);
160
+ --xh-field-shadow-rest: var(--xh-tree-select-control-shadow, none);
161
+ --xh-field-cursor-rest: pointer;
162
+ /* 家族 chrome 会重写 --xh-icon-size,使用者图标槽必须经桥接槽下发才能在盒内生效 */
163
+ --xh-field-glyph-size: var(--xh-tree-select-icon-size, var(--xh-_field-size-glyph-size));
166
164
  }
167
165
 
168
166
  [data-scope='tree-select'][data-part='control'][hidden] {
169
167
  display: none;
170
168
  }
171
169
 
172
- /* hover 只在中性态换描边:invalid 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
173
- [data-scope='tree-select'][data-part='control']:hover:not([data-disabled], [data-invalid]) {
174
- border-color: var(--xh-tree-select-control-border-hover, var(--xh-_tree-select-border-hover));
175
- }
176
-
177
- /* Tab 停靠点是盒里的 trigger(展开后才移进树里),环画在盒上 */
178
- [data-scope='tree-select'][data-part='control']:focus-within:not([data-disabled]) {
179
- border-color: var(--xh-tree-select-control-border-focus, var(--xh-_tone, var(--xh-border-control-focus)));
180
- outline: var(--xh-ring-width) solid var(--xh-_tree-select-ring);
181
- outline-offset: var(--xh-ring-offset);
182
- }
183
-
184
- [data-scope='tree-select'][data-part='control'][data-invalid] {
185
- border-color: var(--xh-tree-select-control-border-invalid, var(--xh-border-invalid));
186
-
187
- /* 校验失败时环也跟着变红:两圈等宽相邻却一红一蓝,读的人不知道哪个才是当前状态 */
188
- --xh-_tree-select-ring: var(--xh-ring-invalid);
189
- }
190
-
191
- /* 只读仍可聚焦、浮层仍展得开、树仍能浏览,所以不置灰,只把底色压下去表示「改不动」 */
192
- [data-scope='tree-select'][data-part='control'][data-readonly] {
193
- background: var(--xh-tree-select-control-bg-readonly, var(--xh-bg-subtle));
194
- }
195
-
196
- [data-scope='tree-select'][data-part='control'][data-disabled] {
197
- background: var(--xh-tree-select-control-bg-disabled, var(--xh-bg-subtle));
198
- color: var(--xh-fg-disabled);
199
- cursor: not-allowed;
200
- }
201
-
202
170
  /* 盒内那颗可聚焦的按钮:不画描边不铺底也不自带高度,只占满剩余宽度、排布回显与箭头 */
203
171
  [data-scope='tree-select'][data-part='trigger'] {
204
172
  display: inline-flex;
@@ -225,7 +193,6 @@
225
193
  /* trigger 是单体控件,用原生 disabled(树里的集合条目一律 aria-disabled,写 :disabled 永不命中) */
226
194
  [data-scope='tree-select'][data-part='trigger']:disabled {
227
195
  color: var(--xh-fg-disabled);
228
- cursor: not-allowed;
229
196
  }
230
197
 
231
198
  /* 多选时值会连成很长一串,必须能截断;框宽由盒定,这里得允许自己被压缩 */
@@ -250,8 +217,9 @@
250
217
  }
251
218
 
252
219
  /* 有值时清空钮顶上来,箭头让位:两个图标并排堆着,用户分不清点哪个。
253
- 让位而不是禁用——禁用的图标还占着位置,看着像坏了 */
254
- [data-scope='tree-select'][data-part='indicator'][data-clearable] {
220
+ 判据查的是盒里此刻真有一颗没收起的清空钮,不是「有没有值」——
221
+ 作者没摆清空钮时箭头照常留着,不然让了位没人接,指示符整个消失 */
222
+ [data-scope='tree-select'][data-part='control']:has([data-part='clear-trigger']:not([hidden])) [data-part='indicator'] {
255
223
  display: none;
256
224
  }
257
225
 
@@ -261,7 +229,7 @@
261
229
  align-items: center;
262
230
  justify-content: center;
263
231
  color: var(--xh-tree-select-indicator-fg, var(--xh-fg-muted));
264
- transition: rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
232
+ transition: rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter-strong);
265
233
  }
266
234
 
267
235
  [data-scope='tree-select'][data-part='indicator'][hidden] {
@@ -276,66 +244,76 @@
276
244
  color: var(--xh-fg-disabled);
277
245
  }
278
246
 
279
- /* 盒内靠右的小方钮,不撑高那一行 */
247
+ /* 盒内靠右的小方钮,不撑高那一行:正方视觉盒、inset 圆角、悬停 100 / 按下 200 与 0.97 按压、粗指针热区与
248
+ :focus-visible 关环(盒的 :focus-within 已画了一圈)全由 Action Control 的 field-inset ghost 档给,
249
+ 组件只映射既有覆盖槽 */
280
250
  [data-scope='tree-select'][data-part='clear-trigger'] {
281
- display: inline-flex;
282
- flex: none;
283
- align-items: center;
284
- justify-content: center;
285
- inline-size: var(--xh-tree-select-action-size, var(--xh-control-action-size));
286
- block-size: var(--xh-tree-select-action-size, var(--xh-control-action-size));
287
- padding: 0;
288
- border: 0;
289
- border-radius: var(--xh-tree-select-action-radius, var(--xh-shape-control));
290
- background: var(--xh-tree-select-action-bg, transparent);
291
- color: var(--xh-tree-select-action-fg, var(--xh-fg-muted));
292
- font-family: inherit;
293
- font-size: var(--xh-tree-select-action-font-size, var(--xh-text-secondary-size));
294
- line-height: var(--xh-leading-none);
295
- cursor: pointer;
296
- user-select: none;
297
- transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter), scale var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
298
- }
299
-
251
+ --xh-action-visual-size: var(--xh-tree-select-action-size, var(--xh-_action-profile-visual-size));
252
+ --xh-action-min-inline-size: var(--xh-tree-select-action-size, var(--xh-_action-profile-visual-size));
253
+ --xh-action-radius: var(--xh-tree-select-action-radius, var(--xh-shape-inset));
254
+ /* 底色阶梯由 ghost 档给(透明 → 100 → 200),使用者槽只作覆盖入口 */
255
+ --xh-action-bg-rest: var(--xh-tree-select-action-bg, var(--xh-_action-variant-bg-rest));
256
+ --xh-action-bg-hover: var(--xh-tree-select-action-bg-hover, var(--xh-_action-variant-bg-hover));
257
+ --xh-action-bg-pressed: var(--xh-tree-select-action-bg-active, var(--xh-_action-variant-bg-pressed));
258
+ --xh-action-fg-rest: var(--xh-tree-select-action-fg, var(--xh-fg-muted));
259
+ --xh-action-fg-hover: var(--xh-tree-select-action-fg-hover, var(--xh-fg-default));
260
+ --xh-action-border-rest: transparent;
261
+ --xh-action-highlight-rest: transparent;
262
+ --xh-action-shadow-rest: none;
263
+ --xh-action-shadow-hover: none;
264
+ --xh-action-shadow-pressed: none;
265
+ --xh-action-font-size: var(--xh-tree-select-action-font-size, var(--xh-text-secondary-size));
266
+ /* 配方尚未把 profile 字形档灌进 --xh-icon-size,钮内字形先按 field-inset 档逐钮取值 */
267
+ --xh-icon-size: var(--xh-_action-profile-glyph-size);
268
+
269
+ border-width: 0;
270
+ }
271
+
272
+ /* 没值时连接层给的是 hidden 属性;家族那条 display 会把 UA 规则盖掉 */
300
273
  [data-scope='tree-select'][data-part='clear-trigger'][hidden] {
301
274
  display: none;
302
275
  }
303
276
 
304
- [data-scope='tree-select'][data-part='clear-trigger']:hover {
305
- background: var(--xh-tree-select-action-bg-hover, var(--xh-bg-subtle-hover));
306
- color: var(--xh-tree-select-action-fg-hover, var(--xh-fg-default));
307
- }
308
-
309
- [data-scope='tree-select'][data-part='clear-trigger']:active {
310
- background: var(--xh-tree-select-action-bg-active, var(--xh-bg-subtle-active));
311
- scale: var(--xh-motion-scale-press);
312
- }
313
-
314
- /* 清空按钮退出了 Tab 序列(tabindex=-1),正常操作走不到这里;
315
- 留着是给程序化聚焦兜底,落焦时得看得见 */
316
- [data-scope='tree-select'][data-part='clear-trigger']:focus-visible {
317
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
318
- outline-offset: var(--xh-ring-offset);
319
- }
320
-
321
277
  [data-scope='tree-select'][data-part='content'] {
278
+ position: relative;
322
279
  display: flex;
323
280
  flex-direction: column;
324
- min-inline-size: var(--xh-tree-select-content-min-w, var(--xh-overlay-min-w));
325
- max-inline-size: var(--xh-tree-select-content-max-w, var(--xh-overlay-max-w));
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)),
284
+ var(--xh-_tree-select-available-w)
285
+ );
286
+ /* 静态档与落位后的可用宽度取小:窄视口下面板伸出去,最右侧的内容就点不到了 */
287
+ max-inline-size: min(var(--xh-tree-select-content-max-w, var(--xh-overlay-max-w)), var(--xh-_tree-select-available-w));
326
288
  max-block-size: min(var(--xh-tree-select-content-max-h, var(--xh-overlay-max-h)), var(--xh-_tree-select-available-h));
327
289
  padding-block: var(--xh-tree-select-content-py, var(--xh-space-1));
328
290
  padding-inline: var(--xh-tree-select-content-px, var(--xh-space-1));
329
- border: var(--xh-stroke-thin) solid var(--xh-tree-select-content-border, var(--xh-border-default));
330
- border-radius: var(--xh-tree-select-content-radius, var(--xh-shape-surface));
331
- background: var(--xh-tree-select-content-bg, var(--xh-bg-surface));
332
- color: var(--xh-tree-select-content-fg, var(--xh-fg-default));
333
- box-shadow: var(--xh-tree-select-content-shadow, var(--xh-elevation-floating));
291
+ border: var(--xh-stroke-thin) solid var(--xh-tree-select-content-border, var(--xh-material-frosted-border));
292
+ 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));
334
298
  /* 两轴一起给:深层节点靠缩进往行末推,横向溢出是常态,得够得着 */
335
299
  overflow: auto;
300
+ /* 浮层滚动面:滚到头不把页面一起带走 */
301
+ overscroll-behavior: contain;
336
302
  pointer-events: auto;
337
303
  }
338
304
 
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
+
339
317
  /* 这里刻意没有 [data-part='content'][hidden] { display: none },与 dialog 一致:
340
318
  收起态连接层会给 content 打 hidden,而上面那条 display 正是要盖掉 UA 的
341
319
  [hidden]{display:none}——退场动画得先能播。presence 读的是 content 的 animationName,
@@ -348,13 +326,15 @@
348
326
  }
349
327
 
350
328
  [data-scope='tree-select'][data-part='content'][data-state='open'] {
351
- animation: xh-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
329
+ animation: xh-overlay-slide-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong);
330
+ /* 只在开着的这一档挂:合成层随 data-state 一起收走,收起后不再占着 */
331
+ will-change: opacity, translate;
352
332
  }
353
333
 
354
334
  /* 退场:收起那一帧 hidden 由退场闸门押后,这里才播得出来。forwards 把末态钉住,
355
335
  免得动画结束到真收起之间闪一下原样 */
356
336
  [data-scope='tree-select'][data-part='content'][data-state='closed'] {
357
- animation: xh-pop-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
337
+ animation: xh-overlay-slide-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
358
338
  }
359
339
 
360
340
  /* 边框、底色与滚动都归 content,树本身只把行竖着排开 */
@@ -368,11 +348,56 @@
368
348
  display: none;
369
349
  }
370
350
 
371
- /* 焦点落在树容器上只有一种情形:指针打开、还没有任何行当锚点。那一刻它认领着 Tab 位,
372
- 环画在内沿,用户才看得出键盘此刻在哪儿;有锚点时环长在行上,容器不再画 */
373
- [data-scope='tree-select'][data-part='tree'][tabindex='0']:focus-visible {
374
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
375
- outline-offset: calc(-1 * var(--xh-ring-width));
351
+ /* 空态占位:待在 content 里、tree 的兄弟(role=tree 只许拥有 treeitem 与 group)。
352
+ 盒与阴影归 content,这里只画一句居中的次要文字 */
353
+ [data-scope='tree-select'][data-part='empty'] {
354
+ display: block;
355
+ flex: none;
356
+ padding-block: var(--xh-tree-select-empty-py, var(--xh-space-3));
357
+ padding-inline: var(--xh-tree-select-empty-px, var(--xh-_tree-select-row-px));
358
+ color: var(--xh-tree-select-empty-fg, var(--xh-material-frosted-fg-muted));
359
+ font-size: var(--xh-tree-select-empty-font-size, var(--xh-_tree-select-font-size));
360
+ line-height: var(--xh-leading-none);
361
+ text-align: center;
362
+ }
363
+
364
+ /* 上面给了 display,UA 的 [hidden]{display:none} 已被盖掉,这里补回来 */
365
+ [data-scope='tree-select'][data-part='empty'][hidden] {
366
+ display: none;
367
+ }
368
+
369
+ /* 在途占位:与空态占位同一个位置、同一副观感,两者不同屏 */
370
+ [data-scope='tree-select'][data-part='loading'] {
371
+ display: block;
372
+ flex: none;
373
+ padding-block: var(--xh-tree-select-loading-py, var(--xh-space-3));
374
+ padding-inline: var(--xh-tree-select-loading-px, var(--xh-_tree-select-row-px));
375
+ color: var(--xh-tree-select-loading-fg, var(--xh-material-frosted-fg-muted));
376
+ font-size: var(--xh-tree-select-loading-font-size, var(--xh-_tree-select-font-size));
377
+ line-height: var(--xh-leading-none);
378
+ text-align: center;
379
+ }
380
+
381
+ [data-scope='tree-select'][data-part='loading'][hidden] {
382
+ display: none;
383
+ }
384
+
385
+ /* 浮层底部的操作区:排在树下面,与树之间用一条分隔线断开;不参与收缩,行再多也压不扁它 */
386
+ [data-scope='tree-select'][data-part='footer'] {
387
+ display: flex;
388
+ flex: none;
389
+ align-items: center;
390
+ gap: var(--xh-tree-select-footer-gap, var(--xh-space-2));
391
+ padding-block: var(--xh-tree-select-footer-py, var(--xh-space-2));
392
+ padding-inline: var(--xh-tree-select-footer-px, var(--xh-space-2));
393
+ border-block-start: var(--xh-stroke-thin) solid var(--xh-tree-select-footer-border, var(--xh-material-frosted-separator));
394
+ color: var(--xh-tree-select-footer-fg, var(--xh-material-frosted-fg-muted));
395
+ font-size: var(--xh-tree-select-footer-font-size, var(--xh-text-secondary-size));
396
+ }
397
+
398
+ /* 上面给了 display,UA 的 [hidden]{display:none} 已被盖掉,这里补回来 */
399
+ [data-scope='tree-select'][data-part='footer'][hidden] {
400
+ display: none;
376
401
  }
377
402
 
378
403
  [data-scope='tree-select'][data-part='tree']:not([tabindex='0']):focus-visible {
@@ -399,110 +424,176 @@
399
424
  outline: none;
400
425
  }
401
426
 
402
- [data-scope='tree-select'][data-part='branch']:focus-visible > [data-scope='tree-select'][data-part='branch-control'] {
403
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
404
- /* 往内收:行是撑满宽度的,环画在外沿会被容器裁掉 */
405
- outline-offset: calc(-1 * var(--xh-ring-width));
406
- }
407
-
408
- /* 行:叶子与分支共用同一套盒子,两种行在视觉上必须齐平 */
427
+ /* 行:叶子与分支共用同一套盒子,两种行在视觉上必须齐平。网格、尺寸档、悬停 / 高亮 100、按下 200 与
428
+ 选中标记由 Collection Item 家族按 overlay 语境给(选中 = 透明底 + 行尾对号,正文保持 rest 色与常规
429
+ 字重),公开 TreeSelect 槽只映射到家族合同。data-highlighted 跟的是连接层记的焦点行(指针与键盘共用),
430
+ 环只在真正的键盘焦点上接回,见下 */
409
431
  [data-scope='tree-select'][data-part='item'],
410
432
  [data-scope='tree-select'][data-part='branch-control'] {
411
- display: flex;
412
- align-items: center;
413
- gap: var(--xh-tree-select-row-gap, var(--xh-_tree-select-gap));
414
- padding-block: var(--xh-tree-select-row-py, var(--xh-_tree-select-row-py));
415
- padding-inline: var(--xh-tree-select-row-px, var(--xh-_tree-select-row-px));
416
- border-radius: var(--xh-tree-select-row-radius, var(--xh-shape-control));
417
- color: var(--xh-tree-select-row-fg, var(--xh-fg-default));
418
- font-size: var(--xh-tree-select-row-font-size, var(--xh-_tree-select-font-size));
433
+ --xh-collection-block-padding: var(--xh-tree-select-item-py, var(--xh-_tree-select-row-py));
434
+ --xh-collection-inline-padding: var(--xh-tree-select-item-px, var(--xh-_tree-select-row-px));
435
+ --xh-collection-gap: var(--xh-tree-select-item-gap, var(--xh-_tree-select-gap));
436
+ --xh-collection-font-size: var(--xh-tree-select-item-font-size, var(--xh-_tree-select-font-size));
437
+ --xh-collection-radius: var(--xh-tree-select-item-radius, var(--xh-shape-control));
438
+ --xh-collection-glyph-size: var(--xh-tree-select-icon-size, var(--xh-_collection-glyph-size));
439
+ --xh-collection-fg-rest: var(--xh-tree-select-item-fg, var(--xh-material-frosted-fg));
440
+ --xh-collection-fg-hover: var(--xh-tree-select-item-fg, var(--xh-material-frosted-fg));
441
+ --xh-collection-fg-keyboard-highlight: var(--xh-tree-select-item-fg, var(--xh-material-frosted-fg));
442
+ --xh-collection-fg-selected: var(--xh-tree-select-item-fg-selected, var(--xh-tree-select-item-fg, var(--xh-material-frosted-fg)));
443
+ --xh-collection-fg-selected-highlight: var(--xh-tree-select-item-fg-selected, var(--xh-tree-select-item-fg, var(--xh-material-frosted-fg)));
444
+ --xh-collection-fg-selected-hover: var(--xh-tree-select-item-fg-selected, var(--xh-tree-select-item-fg, var(--xh-material-frosted-fg)));
445
+ --xh-collection-fg-selected-pressed: var(--xh-tree-select-item-fg-selected, var(--xh-tree-select-item-fg, var(--xh-material-frosted-fg)));
446
+ --xh-collection-fg-pressed: var(--xh-tree-select-item-fg, var(--xh-material-frosted-fg));
447
+ --xh-collection-font-weight-selected: var(--xh-tree-select-item-font-weight-selected, var(--xh-font-weight-regular));
448
+ --xh-collection-font-weight-selected-highlight: var(--xh-tree-select-item-font-weight-selected, var(--xh-font-weight-regular));
449
+ --xh-collection-font-weight-selected-hover: var(--xh-tree-select-item-font-weight-selected, var(--xh-font-weight-regular));
450
+ --xh-collection-font-weight-selected-pressed: var(--xh-tree-select-item-font-weight-selected, var(--xh-font-weight-regular));
451
+ --xh-collection-bg-hover: var(--xh-tree-select-item-bg-hover, var(--xh-bg-subtle));
452
+ --xh-collection-bg-keyboard-highlight: var(--xh-tree-select-item-bg-hover, var(--xh-bg-subtle));
453
+ --xh-collection-bg-selected-highlight: var(--xh-tree-select-item-bg-hover, var(--xh-bg-subtle));
454
+ --xh-collection-bg-selected-hover: var(--xh-tree-select-item-bg-hover, var(--xh-bg-subtle));
455
+ --xh-collection-bg-pressed: var(--xh-tree-select-item-bg-pressed, var(--xh-bg-subtle-hover));
456
+ --xh-collection-bg-selected-pressed: var(--xh-tree-select-item-bg-pressed, var(--xh-bg-subtle-hover));
457
+ --xh-collection-outline-keyboard-highlight: transparent;
458
+ --xh-collection-outline-selected-highlight: transparent;
459
+ --xh-collection-indicator-fg-rest: var(--xh-tree-select-item-check-fg, var(--xh-tree-select-item-indicator-fg, var(--xh-_tree-select-accent)));
460
+ --xh-collection-indicator-fg-checked: var(--xh-tree-select-item-check-fg, var(--xh-tree-select-item-indicator-fg, var(--xh-_tree-select-accent)));
461
+ --xh-collection-indicator-fg-selected: var(--xh-tree-select-item-check-fg, var(--xh-tree-select-item-indicator-fg, var(--xh-_tree-select-accent)));
462
+ --xh-collection-indicator-fg-selected-hover: var(--xh-tree-select-item-check-fg, var(--xh-tree-select-item-indicator-fg, var(--xh-_tree-select-accent)));
463
+ --xh-collection-indicator-fg-selected-highlight: var(--xh-tree-select-item-check-fg, var(--xh-tree-select-item-indicator-fg, var(--xh-_tree-select-accent)));
464
+ --xh-collection-indicator-fg-selected-pressed: var(--xh-tree-select-item-check-fg, var(--xh-tree-select-item-indicator-fg, var(--xh-_tree-select-accent)));
465
+
419
466
  /* 行高留出文字的上下伸展空间,行内那层 overflow:hidden 才不会把字削掉 */
420
- line-height: var(--xh-tree-select-row-leading, var(--xh-leading-normal));
467
+ line-height: var(--xh-tree-select-item-leading, var(--xh-leading-normal));
421
468
  white-space: nowrap;
422
- cursor: pointer;
423
469
  user-select: none;
424
- transition:
425
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
426
- color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
427
470
  }
428
471
 
472
+ /* 家族给了 display,UA 的 [hidden]{display:none} 已被盖掉,这里补回来 */
429
473
  [data-scope='tree-select'][data-part='item'][hidden],
430
474
  [data-scope='tree-select'][data-part='branch-control'][hidden] {
431
475
  display: none;
432
476
  }
433
477
 
434
- /* 悬停与键盘锚点共用中性灰轻档:data-highlighted 跟的是连接层记的焦点行,
435
- 与选中互相独立,键盘落焦另有聚焦环标记位置 */
436
- [data-scope='tree-select'][data-part='item']:not([data-disabled]):is(:hover, [data-highlighted]),
437
- [data-scope='tree-select'][data-part='branch-control']:not([data-disabled]):is(:hover, [data-highlighted]) {
438
- background: var(--xh-tree-select-row-bg-hover, var(--xh-bg-subtle));
478
+ /* 叶子在家族网格的首列放一格与分支箭头等宽的占位:两种行的文字起点才对得齐;
479
+ 选中标记独立固定在行尾(indicator 列),不再同时承担层级缩进 */
480
+ [data-scope='tree-select'][data-part='item']::before {
481
+ content: '';
482
+ grid-column: prefix;
483
+ grid-row: 1 / span 2;
484
+ inline-size: var(--xh-tree-select-branch-indicator-size, var(--xh-control-indicator-size));
485
+ margin-inline-end: var(--xh-tree-select-item-gap, var(--xh-_tree-select-gap));
439
486
  }
440
487
 
441
- /* 选中只换文字色与字重,配合行尾的 ✓ 标记;底色整条通道留给高亮,
442
- 两者因此能同时看见 */
443
- [data-scope='tree-select'][data-part='item'][data-selected],
444
- [data-scope='tree-select'][data-part='branch-control'][data-selected] {
445
- color: var(--xh-tree-select-row-fg-selected, var(--xh-_tree-select-selected-fg));
446
- font-weight: var(--xh-tree-select-row-selected-font-weight, var(--xh-font-weight-medium));
447
- }
488
+ /* 键盘焦点出现时环才接回并立即到位,不压在背景过渡的中间帧上:叶子自己承载焦点,
489
+ 分支的焦点在 branch 上、环画在它那一行 */
490
+ [data-scope='tree-select'][data-part='item']:focus-visible:not(:hover),
491
+ [data-scope='tree-select'][data-part='branch']:focus-visible > [data-scope='tree-select'][data-part='branch-control'] {
492
+ --xh-collection-outline-keyboard-highlight: var(--xh-ring-focus);
493
+ --xh-collection-outline-selected-highlight: var(--xh-ring-focus);
448
494
 
449
- /* 叶子自己就是 treeitem,环直接画在它身上 */
450
- [data-scope='tree-select'][data-part='item']:focus-visible {
451
- outline: var(--xh-ring-width) solid var(--xh-ring-focus);
452
- outline-offset: calc(-1 * var(--xh-ring-width));
495
+ transition: none;
453
496
  }
454
497
 
455
- /* 禁用走 aria-disabled 而非原生 disabled:节点仍要可聚焦(它还得当方向键的起点),
456
- 所以只能靠 data-disabled 表达,写 :disabled 永远不命中 */
457
- [data-scope='tree-select'][data-part='item'][data-disabled],
498
+ /* 分支行不是 treeitem 本体(aria-disabled branch 上),禁用面按连接层的 data-disabled 映射到家族槽:
499
+ 字色降级、悬停 / 高亮 / 按下不换面、光标 not-allowed */
458
500
  [data-scope='tree-select'][data-part='branch-control'][data-disabled] {
459
- color: var(--xh-fg-disabled);
460
- cursor: not-allowed;
501
+ --xh-collection-fg-rest: var(--xh-fg-disabled);
502
+ --xh-collection-fg-hover: var(--xh-fg-disabled);
503
+ --xh-collection-fg-keyboard-highlight: var(--xh-fg-disabled);
504
+ --xh-collection-fg-pressed: var(--xh-fg-disabled);
505
+ --xh-collection-bg-hover: transparent;
506
+ --xh-collection-bg-keyboard-highlight: transparent;
507
+ --xh-collection-bg-pressed: transparent;
508
+ --xh-collection-cursor-rest: not-allowed;
509
+ --xh-collection-cursor-hover: not-allowed;
510
+ --xh-collection-cursor-keyboard-highlight: not-allowed;
511
+ --xh-collection-cursor-pressed: not-allowed;
512
+ }
513
+
514
+ /* 懒分支取数失败不预设错误面(文案与重试归作者结构接线):家族按 data-error 给的告警面在这里映射回常态 */
515
+ [data-scope='tree-select'][data-part='branch-control'][data-error] {
516
+ --xh-collection-bg-error: transparent;
517
+ --xh-collection-fg-error: var(--xh-material-frosted-fg);
518
+ --xh-collection-description-fg-error: var(--xh-material-frosted-fg-muted);
519
+ --xh-collection-indicator-fg-error: var(--xh-_tree-select-accent);
520
+ --xh-collection-outline-error: transparent;
461
521
  }
462
522
 
523
+ /* 取数失败的行仍可激活(Enter / 点行触发重试),而家族的悬停、高亮与按下三段都排除了 [data-error]:
524
+ 这里在家族解算点上把同一阶梯接回(行槽以 item 取名、叶子与分支行共用,登记见 check-spacing-slots):
525
+ 悬停与键盘高亮 100 档、按下 200 档并走按下时长,
526
+ 键盘焦点环随 branch 的 :focus-visible 画在这一行;禁用分支不进这三段 */
527
+ [data-scope='tree-select'][data-part='branch-control'][data-error]:not([data-disabled]):is(:hover, [data-highlighted]) {
528
+ --xh-_collection-bg: var(--xh-tree-select-item-bg-hover, var(--xh-bg-subtle));
529
+ }
530
+
531
+ [data-scope='tree-select'][data-part='branch']:focus-visible > [data-scope='tree-select'][data-part='branch-control'][data-error]:not([data-disabled]) {
532
+ --xh-_collection-bg: var(--xh-tree-select-item-bg-hover, var(--xh-bg-subtle));
533
+ --xh-_collection-outline: var(--xh-ring-focus);
534
+ }
535
+
536
+ [data-scope='tree-select'][data-part='branch-control'][data-error]:not([data-disabled]):is(:active, [data-pressed]) {
537
+ --xh-_collection-bg: var(--xh-tree-select-item-bg-pressed, var(--xh-bg-subtle-hover));
538
+
539
+ transition-duration: var(--xh-motion-duration-press);
540
+ transition-timing-function: var(--xh-motion-ease-press);
541
+ }
542
+
543
+ /* 正文落在家族网格的 text 列;网格项默认最小尺寸是内容尺寸,不归零则长文本把行撑破、省略号轮不上 */
463
544
  [data-scope='tree-select'][data-part='item-text'],
464
545
  [data-scope='tree-select'][data-part='branch-text'] {
465
- flex: 1;
466
546
  min-inline-size: 0;
467
547
  overflow: hidden;
468
548
  white-space: nowrap;
469
549
  text-overflow: ellipsis;
470
550
  }
471
551
 
472
- /* 勾选标记只在选中项上出现;未选中时占位不塌,切换选中不引起行宽抖动 */
552
+ /* 勾选标记落在家族网格的 indicator 列;未选中时占位不塌,切换选中不引起行宽抖动。
553
+ 尺寸槽按各自部件取名:行内的勾与展开箭头分别是 item- 与 branch- 段,
554
+ 触发器上那枚箭头是 --xh-tree-select-indicator-*,三处各调各的。
555
+ 盒里的对号 / 半选杠是指示符(§6.5):在自己身上把 --xh-icon-size 改接到盒的尺,兜底字形与作者
556
+ 塞进盒里的 XhIcon 读同一把尺、与盒同边长;不读家族按档下发到行的 --xh-icon-size——那支只管
557
+ 作者放进行里的图标,按图标档取的 20px 会比 16 / 14 的盒还大 */
473
558
  [data-scope='tree-select'][data-part='item-indicator'] {
559
+ --xh-icon-size: var(--xh-tree-select-item-indicator-size, var(--xh-control-indicator-size));
560
+
474
561
  display: inline-flex;
475
- flex: none;
476
562
  align-items: center;
477
563
  justify-content: center;
478
- inline-size: var(--xh-tree-select-indicator-size, var(--xh-control-indicator-size));
479
- color: var(--xh-tree-select-item-indicator-fg, var(--xh-_tone, var(--xh-fg-brand)));
480
- visibility: hidden;
564
+ inline-size: var(--xh-tree-select-item-indicator-size, var(--xh-control-indicator-size));
565
+ block-size: var(--xh-tree-select-item-indicator-size, var(--xh-control-indicator-size));
481
566
  }
482
567
 
483
568
  [data-scope='tree-select'][data-part='item-indicator'][hidden] {
484
569
  display: none;
485
570
  }
486
571
 
487
- [data-scope='tree-select'][data-part='item-indicator'][data-selected] {
572
+ /* 家族只按行的 aria-selected 显示对号:分支行的 aria-selected 在 branch 上、半选没有选中语义,
573
+ 这两档按标记自身的 data-selected / data-indeterminate 显形 */
574
+ [data-scope='tree-select'][data-part='item-indicator']:is([data-selected], [data-indeterminate]) {
488
575
  visibility: visible;
489
576
  }
490
577
 
578
+ /* 分支行的禁用不在行的 aria-disabled 上(那在 branch 上),标记按自身的 data-disabled 降级 */
491
579
  [data-scope='tree-select'][data-part='item-indicator'][data-disabled] {
492
580
  color: var(--xh-fg-disabled);
493
581
  }
494
582
 
495
583
  /* 展开箭头与方向指示符:两者都可能被作者用作「点这里展开」的把手,
496
- 所以尺寸与叶子的勾选标记一致,两种行的文字起点才对得齐 */
584
+ 所以尺寸与叶子的勾选标记一致,两种行的文字起点才对得齐。
585
+ 盒里的 chevron 是指示符(§6.5):与盒同边长,--xh-icon-size 在自己身上改接到盒的尺 */
497
586
  [data-scope='tree-select'][data-part='branch-trigger'],
498
587
  [data-scope='tree-select'][data-part='branch-indicator'] {
588
+ --xh-icon-size: var(--xh-tree-select-branch-indicator-size, var(--xh-control-indicator-size));
589
+
499
590
  display: inline-flex;
500
591
  flex: none;
501
592
  align-items: center;
502
593
  justify-content: center;
503
- inline-size: var(--xh-tree-select-indicator-size, var(--xh-control-indicator-size));
594
+ inline-size: var(--xh-tree-select-branch-indicator-size, var(--xh-control-indicator-size));
504
595
  color: var(--xh-tree-select-branch-indicator-fg, var(--xh-fg-subtle));
505
- transition: rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
596
+ transition: rotate var(--xh-motion-duration-micro) var(--xh-motion-ease-enter-strong);
506
597
  }
507
598
 
508
599
  [data-scope='tree-select'][data-part='branch-trigger'][hidden],
@@ -542,11 +633,14 @@
542
633
  }
543
634
 
544
635
  /* 收起态的唯一开关:display 盖掉了 UA 的 [hidden]{display:none},漏了这条整棵子树就收不起来 */
545
- [data-scope='tree-select'][data-part='branch-content'][hidden] {
636
+ [data-scope='tree-select'][data-part='branch-content'][hidden],
637
+ [data-scope='tree-select'][data-part='branch-content'][data-state='closed'] {
546
638
  display: none;
547
639
  }
548
640
 
549
- /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中 */
641
+ /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中。
642
+ 尺寸读各部件的 --xh-icon-size:触发器箭头与清除钮的叉按图标档,行尾对号 / 半选杠与展开 chevron
643
+ 读部件自己改接过的盒尺、与盒同边长(见上面两块) */
550
644
  [data-scope='tree-select'][data-part='indicator']:empty::before {
551
645
  content: '';
552
646
  display: inline-block;
@@ -569,6 +663,11 @@
569
663
  mask: var(--xh-glyph-mark-check) center / contain no-repeat;
570
664
  }
571
665
 
666
+ [data-scope='tree-select'][data-part='item-indicator'][data-indeterminate]:not([data-selected]):empty::before {
667
+ -webkit-mask-image: var(--xh-glyph-mark-minus);
668
+ mask-image: var(--xh-glyph-mark-minus);
669
+ }
670
+
572
671
  [data-scope='tree-select'][data-part='branch-trigger']:empty::before,
573
672
  [data-scope='tree-select'][data-part='branch-indicator']:empty::before {
574
673
  content: '';
@@ -591,4 +690,12 @@
591
690
  -webkit-mask: var(--xh-glyph-mark-close) center / contain no-repeat;
592
691
  mask: var(--xh-glyph-mark-close) center / contain no-repeat;
593
692
  }
693
+
694
+ @media print {
695
+ /* 打印:定位层与遮罩是交互期才存在的东西,落到纸上只会盖住底下的正文。
696
+ 收起写在这份皮肤里而不是 reset:reset 层排在组件层之前,组件层里任何一条 display 都压得住它。 */
697
+ [data-scope='tree-select'][data-part='positioner'] {
698
+ display: none;
699
+ }
700
+ }
594
701
  }