@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
package/css/tabs.css CHANGED
@@ -43,8 +43,11 @@
43
43
  --xh-_tabs-accent-text: var(--xh-_tone-fg, var(--xh-fg-brand-strong));
44
44
 
45
45
  /* 翻页钮的盖底:标签带放不下时它浮在两端,挪到它底下的那截标签要被盖住。line / card 档的标签带是透明的,
46
- 盖底取 surface;坐在别的面上(画布、淡底面板)时由使用者槽换成所在的面。segment 档取轨道的淡底 */
46
+ 盖底取 surface;坐在别的面上(画布、淡底面板)时由使用者槽换成所在的面。segment 档取轨道的淡底。
47
+ 悬停与按下同样要盖得住:淡底阶梯取不透明档,line / card 在白底上 100 → 200,segment 在轨道上 200 → 300 */
47
48
  --xh-_tabs-scroll-trigger-bg: var(--xh-bg-surface);
49
+ --xh-_tabs-scroll-trigger-bg-hover: var(--xh-bg-subtle-opaque);
50
+ --xh-_tabs-scroll-trigger-bg-pressed: var(--xh-bg-subtle-hover-opaque);
48
51
 
49
52
  display: flex;
50
53
  flex-direction: column;
@@ -124,7 +127,9 @@
124
127
  --xh-_tabs-trigger-bg-active-hover: var(--xh-_tone-subtle, var(--xh-bg-surface-raised));
125
128
  --xh-_tabs-trigger-border-active: var(--xh-_tone-border, var(--xh-border-default));
126
129
  --xh-_tabs-accent-text: var(--xh-_tone-fg, var(--xh-fg-default));
127
- --xh-_tabs-scroll-trigger-bg: var(--xh-bg-subtle);
130
+ --xh-_tabs-scroll-trigger-bg: var(--xh-bg-subtle-opaque);
131
+ --xh-_tabs-scroll-trigger-bg-hover: var(--xh-bg-subtle-hover-opaque);
132
+ --xh-_tabs-scroll-trigger-bg-pressed: var(--xh-bg-subtle-active-opaque);
128
133
  }
129
134
 
130
135
  /* 一行排不下不折行:标签整条沿主轴位移露出被裁掉的那截,两端的翻页钮与滚轮驱动,位移量由机器写在
@@ -242,7 +247,14 @@
242
247
  flex: 0 0 auto;
243
248
  }
244
249
 
245
- /* segment 档是一条轨道里的格子(与 Segmented 同构):格子按可用宽度等分 */
250
+ /* 竖排标签的高就是控件高,不随标签带压缩:使用者限了高、标签带放不下时,靠位移露出被裁掉的那截(与横排同一套),
251
+ 而不是把每一枚压扁到只剩一行字高 */
252
+ [data-scope='tabs'][data-part='root'][data-orientation='vertical']
253
+ [data-scope='tabs'][data-part='trigger'] {
254
+ flex-shrink: 0;
255
+ }
256
+
257
+ /* segment 档是一条轨道里的格子(与 RadioGroup 的 segmented 形态同构):格子按可用宽度等分 */
246
258
  [data-scope='tabs'][data-part='root'][data-orientation='horizontal'][data-variant='segment']
247
259
  [data-scope='tabs'][data-part='trigger'] {
248
260
  flex: 1 1 0;
@@ -459,17 +471,26 @@
459
471
  --xh-_tabs-indicator-y: 0;
460
472
  --xh-_tabs-indicator-w: 0;
461
473
  --xh-_tabs-indicator-h: 0;
474
+ --xh-_tabs-indicator-stretch: 0;
462
475
 
476
+ /* 摆在行首,落点交给 transform(只走合成),长度跟着标签宽走。标签带挪动时整条孩子
477
+ 共用的 translate 叠在它之上,两者互不覆盖,过渡也只挂在 transform 上 */
463
478
  position: absolute;
464
479
  inset-block-end: 0;
465
- inset-inline-start: var(--xh-_tabs-indicator-x);
480
+ inset-inline-start: 0;
466
481
  inline-size: var(--xh-_tabs-indicator-w);
467
482
  block-size: var(--xh-tabs-indicator-thickness, var(--xh-stroke-thick));
468
483
  border-radius: var(--xh-tabs-indicator-radius, var(--xh-shape-pill));
469
484
  background: var(--xh-tabs-indicator-color, var(--xh-_tabs-accent));
485
+ transform: translate(var(--xh-_tabs-indicator-x), 0);
470
486
  transition:
471
- inset-inline-start var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous),
472
- inline-size var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous);
487
+ transform var(--xh-motion-duration-move) var(--xh-motion-ease-continuous),
488
+ inline-size var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
489
+ }
490
+
491
+ /* transform 只认物理方向:RTL 下行首在右边,起始缘量出的距离往左推 */
492
+ [data-scope='tabs'][data-part='indicator']:dir(rtl) {
493
+ transform: translate(calc(-1 * var(--xh-_tabs-indicator-x)), 0);
473
494
  }
474
495
 
475
496
  /* 卡片变体已经由卡片面表达状态,不再叠一条下划线。 */
@@ -478,14 +499,14 @@
478
499
  }
479
500
 
480
501
  /* segment 档(有滑块开关):部件就是那块白色抬起的滑块面,四个几何量都取、整块跟着滑——
481
- surface-raised 底 + border-default 描边 + raised 影三件与 Segmented 的滑块同一套;
502
+ surface-raised 底 + border-default 描边 + raised 影三件与 RadioGroup segmented 形态的滑块同一套;
482
503
  描边吃进盒里(border-box),面与标签仍是同一块矩形。标签压在它上面,点击一律交给标签。
483
504
  用连续档而不是进场那条:滑块是被推过去的,起步就该有速度;四个几何量同时走,标签宽不等时才不会先变形再挪位 */
484
505
  [data-scope='tabs'][data-part='indicator'][data-variant='segment'] {
485
506
  inset-block-end: auto;
486
507
  inset-inline-end: auto;
487
- inset-inline-start: var(--xh-_tabs-indicator-x);
488
- inset-block-start: var(--xh-_tabs-indicator-y);
508
+ inset-inline-start: 0;
509
+ inset-block-start: 0;
489
510
  inline-size: var(--xh-_tabs-indicator-w);
490
511
  block-size: var(--xh-_tabs-indicator-h);
491
512
  box-sizing: border-box;
@@ -494,11 +515,15 @@
494
515
  background: var(--xh-tabs-trigger-bg-active, var(--xh-_tone-subtle, var(--xh-bg-surface-raised)));
495
516
  box-shadow: var(--xh-tabs-trigger-shadow-active, var(--xh-elevation-raised));
496
517
  pointer-events: none;
518
+ transform: translate(var(--xh-_tabs-indicator-x), var(--xh-_tabs-indicator-y));
497
519
  transition:
498
- inset-inline-start var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous),
499
- inset-block-start var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous),
500
- inline-size var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous),
501
- block-size var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous);
520
+ transform var(--xh-motion-duration-move) var(--xh-motion-ease-continuous),
521
+ inline-size var(--xh-motion-duration-move) var(--xh-motion-ease-continuous),
522
+ block-size var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
523
+ }
524
+
525
+ [data-scope='tabs'][data-part='indicator'][data-variant='segment']:dir(rtl) {
526
+ transform: translate(calc(-1 * var(--xh-_tabs-indicator-x)), var(--xh-_tabs-indicator-y));
502
527
  }
503
528
 
504
529
  /* line 档没放 indicator 部件时,选中标签自画一条静态指示线(导航当前页):
@@ -543,12 +568,32 @@
543
568
  inset-block-end: auto;
544
569
  inset-inline-start: auto;
545
570
  inset-inline-end: 0;
546
- inset-block-start: var(--xh-_tabs-indicator-y);
571
+ inset-block-start: 0;
547
572
  block-size: var(--xh-_tabs-indicator-h);
548
573
  inline-size: var(--xh-tabs-indicator-thickness, var(--xh-stroke-thick));
574
+ transform: translate(0, var(--xh-_tabs-indicator-y));
549
575
  transition:
550
- inset-block-start var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous),
551
- block-size var(--xh-motion-duration-enter) var(--xh-motion-ease-continuous);
576
+ transform var(--xh-motion-duration-move) var(--xh-motion-ease-continuous),
577
+ block-size var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
578
+ }
579
+
580
+ /* 首次落位与同一项的重量(窗口缩放、换上正式字体)直接到位,只有换项才滑:连接层在这一落点投影
581
+ data-instant。选择器带上排布与形态,压得过上面按它们单写的过渡 */
582
+ [data-scope='tabs'][data-part='indicator'][data-orientation][data-variant][data-instant] {
583
+ transition: none;
584
+ }
585
+
586
+ /* 液态档:两沿由弹簧逐帧推着走(连接层每一帧写好盒子),几何量不再补过渡(选择器带上排布与形态,压得过上面按它们单写的过渡);
587
+ 被拉长时在另一个方向上压扁,表达体积守恒,拉长到目标的两倍时压到 --xh-motion-scale-squash */
588
+ :where([data-material='liquid']) [data-scope='tabs'][data-part='indicator'][data-orientation][data-variant] {
589
+ --xh-_tabs-indicator-squash: max(var(--xh-motion-scale-squash), calc(1 - var(--xh-_tabs-indicator-stretch) * (1 - var(--xh-motion-scale-squash))));
590
+
591
+ scale: 1 var(--xh-_tabs-indicator-squash);
592
+ transition: none;
593
+ }
594
+
595
+ :where([data-material='liquid']) [data-scope='tabs'][data-part='indicator'][data-orientation='vertical'][data-variant] {
596
+ scale: var(--xh-_tabs-indicator-squash) 1;
552
597
  }
553
598
 
554
599
  /* 标签之间的细线:横排时是一条竖线,竖排时躺平 */
@@ -571,11 +616,13 @@
571
616
  /* 标签带放不下时浮在两端的单图标钮,接 Action Control icon 档 ghost 面(连接层投影 profile / variant / size):
572
617
  盒取标签同高的正方形,四态面、0.97 按压与粗指针 44px 命中区由家族配方给。绝对定位贴在标签带两端、
573
618
  不随标签位移;挪到它底下的那截标签由静息底盖住(透明 ghost 面盖不住字,静息 / 焦点 / 禁用三列换成盖底槽,
574
- 悬停与按下仍走 ghost 的阶梯——line / card 在白底上 100 → 200,segment 由轨道下发 200 → 300) */
619
+ 悬停与按下换成盖底档的不透明阶梯——line / card 在白底上 100 → 200,segment 在轨道上 200 → 300) */
575
620
  [data-scope='tabs'][data-part='prev-trigger'],
576
621
  [data-scope='tabs'][data-part='next-trigger'] {
577
622
  --xh-action-visual-size: var(--xh-tabs-trigger-h, var(--xh-_tabs-trigger-h));
578
623
  --xh-action-bg-rest: var(--xh-tabs-scroll-trigger-bg, var(--xh-_tabs-scroll-trigger-bg));
624
+ --xh-action-bg-hover: var(--xh-_tabs-scroll-trigger-bg-hover);
625
+ --xh-action-bg-pressed: var(--xh-_tabs-scroll-trigger-bg-pressed);
579
626
  --xh-action-bg-focus-visible: var(--xh-tabs-scroll-trigger-bg, var(--xh-_tabs-scroll-trigger-bg));
580
627
  --xh-action-bg-disabled: var(--xh-tabs-scroll-trigger-bg, var(--xh-_tabs-scroll-trigger-bg));
581
628
  --xh-action-fg-rest: var(--xh-tabs-scroll-trigger-fg, var(--xh-fg-muted));
@@ -661,15 +708,159 @@
661
708
  mask-image: var(--xh-glyph-mark-chevron-down);
662
709
  }
663
710
 
711
+ /* —— 「更多」钮 —— */
712
+ /* 放了「更多」钮:钮排在标签带之后、同一行(竖排是同一列的列尾)。它不能放进标签带——tablist 只收 tab,
713
+ 所以是 root 的孩子;root 换成两轨网格,标签带占能伸缩的那一轨、钮占按内容的那一轨,面板等其余孩子横跨两轨排在
714
+ 下面(竖排排在右侧)。钮收着(hidden)时那一轨是 0,标签带铺满;钮与标签带之间的间距挂在钮自己身上,
715
+ 收着时一并让开。标签带的可用长度因此随钮的有无变:放不放得下由机器按让回钮那一截算,钮不会自己把自己留住 */
716
+ [data-scope='tabs'][data-part='root']:has(> [data-scope='tabs'][data-part='overflow-trigger']) {
717
+ display: grid;
718
+ grid-template-columns: minmax(0, 1fr) auto;
719
+ column-gap: 0;
720
+ row-gap: var(--xh-tabs-gap, var(--xh-stack-gap-md));
721
+ }
722
+
723
+ [data-scope='tabs'][data-part='root']:has(> [data-scope='tabs'][data-part='overflow-trigger']) > * {
724
+ grid-column: 1 / -1;
725
+ }
726
+
727
+ [data-scope='tabs'][data-part='root']:has(> [data-scope='tabs'][data-part='overflow-trigger'])
728
+ > [data-scope='tabs'][data-part='list'] {
729
+ grid-row: 1;
730
+ grid-column: 1;
731
+ }
732
+
733
+ [data-scope='tabs'][data-part='root']:has(> [data-scope='tabs'][data-part='overflow-trigger'])
734
+ > [data-scope='tabs'][data-part='overflow-trigger'] {
735
+ grid-row: 1;
736
+ grid-column: 2;
737
+ align-self: center;
738
+ margin-inline-start: var(--xh-space-1);
739
+ }
740
+
741
+ /* 竖排:标签带与钮上下叠成左侧一列(钮在列尾),面板占右侧那一轨、跨两行 */
742
+ [data-scope='tabs'][data-part='root'][data-orientation='vertical']:has(> [data-scope='tabs'][data-part='overflow-trigger']) {
743
+ grid-template-rows: minmax(0, 1fr) auto;
744
+ grid-template-columns: auto minmax(0, 1fr);
745
+ column-gap: var(--xh-tabs-gap, var(--xh-stack-gap-md));
746
+ row-gap: 0;
747
+ }
748
+
749
+ [data-scope='tabs'][data-part='root'][data-orientation='vertical']:has(> [data-scope='tabs'][data-part='overflow-trigger']) > * {
750
+ grid-row: 1 / -1;
751
+ grid-column: 2;
752
+ }
753
+
754
+ [data-scope='tabs'][data-part='root'][data-orientation='vertical']:has(> [data-scope='tabs'][data-part='overflow-trigger'])
755
+ > [data-scope='tabs'][data-part='list'] {
756
+ grid-row: 1;
757
+ grid-column: 1;
758
+ }
759
+
760
+ [data-scope='tabs'][data-part='root'][data-orientation='vertical']:has(> [data-scope='tabs'][data-part='overflow-trigger'])
761
+ > [data-scope='tabs'][data-part='overflow-trigger'] {
762
+ grid-row: 2;
763
+ grid-column: 1;
764
+ align-self: auto;
765
+ margin-inline-start: 0;
766
+ margin-block-start: var(--xh-space-1);
767
+ }
768
+
769
+ /* 钮本身:接 Action Control icon 档 ghost 面(连接层投影 profile / variant / size),盒取标签同高的正方形,
770
+ 与两端翻页钮同一身份;四态面、0.97 按压、焦点环与粗指针 44px 命中区由家族配方给。它坐在 root 的面上
771
+ (不在标签带里),承载面阶梯走家族缺省的白底 100 → 200;字取次要色,与静息的标签、翻页钮同档 */
772
+ [data-scope='tabs'][data-part='overflow-trigger'] {
773
+ --xh-action-visual-size: var(--xh-tabs-trigger-h, var(--xh-_tabs-trigger-h));
774
+ --xh-action-fg-rest: var(--xh-fg-muted);
775
+ /* 作者塞进来的图标跟着 icon 档走,与皮肤画的兜底字形同一把尺 */
776
+ --xh-icon-size: var(--xh-_action-profile-glyph-size);
777
+
778
+ touch-action: manipulation;
779
+ }
780
+
781
+ /* 竖排:与竖排的翻页钮一样躺平成横贯标签带那一列的一条 */
782
+ [data-scope='tabs'][data-part='overflow-trigger'][data-orientation='vertical'] {
783
+ inline-size: auto;
784
+ }
785
+
786
+ /* 下拉开着:与悬停同档的中性面,不用品牌色、不另画环;列的是哪几个标签由下拉本身说明 */
787
+ [data-scope='tabs'][data-part='overflow-trigger'][data-state='open'] {
788
+ --xh-action-bg-rest: var(--xh-_action-variant-bg-hover);
789
+ --xh-action-bg-focus-visible: var(--xh-_action-variant-bg-hover);
790
+ }
791
+
792
+ /* 没写内容就由皮肤画兜底字形:横排三点(与工具条的「更多」同一枚)。作者塞了自己的图标这条即不命中 */
793
+ [data-scope='tabs'][data-part='overflow-trigger']:empty::before {
794
+ content: '';
795
+ display: inline-block;
796
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
797
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
798
+ background-color: currentColor;
799
+ -webkit-mask: var(--xh-glyph-mark-ellipsis) center / contain no-repeat;
800
+ mask: var(--xh-glyph-mark-ellipsis) center / contain no-repeat;
801
+ }
802
+
803
+ /* —— 关闭钮 —— */
804
+ /* 关闭钮与所属标签平级、紧跟其后(不嵌进 role=tab 的按钮里),画面上收进标签面的行尾:标签在行尾让出一格
805
+ (钮的边长 + 与行尾的留白 + 与文字的间距),钮以负的起始外边距退回这一格,结束外边距补回同样的留白,
806
+ 下一枚标签的起点与没有关闭钮时一致。留白取标签与钮高度差的一半,钮在标签里上下左右都居中对齐。
807
+ 面归 Action Control icon 档 ghost 面、xs 档(连接层投影):四态面、0.97 按压与粗指针命中区由家族配方给,
808
+ 这里只管落位与字色。悬停落在钮上时标签自己回到静息面,只有钮亮起,按下去关的是哪一个一目了然 */
809
+ [data-scope='tabs'][data-part='root'] {
810
+ --xh-_tabs-close-size: var(--xh-control-action-size);
811
+ --xh-_tabs-close-inset: calc((var(--xh-tabs-trigger-h, var(--xh-_tabs-trigger-h)) - var(--xh-_tabs-close-size)) / 2);
812
+ /* 钮退回去的距离:横排退过自己、行尾留白与标签带间距;竖排退过半个标签高、半个钮高与标签带间距 */
813
+ --xh-_tabs-close-offset: calc(var(--xh-_tabs-close-size) + var(--xh-_tabs-close-inset) + var(--xh-tabs-list-gap, var(--xh-_tabs-list-gap)));
814
+ --xh-_tabs-close-offset-block: calc((var(--xh-tabs-trigger-h, var(--xh-_tabs-trigger-h)) + var(--xh-_tabs-close-size)) / 2 + var(--xh-tabs-list-gap, var(--xh-_tabs-list-gap)));
815
+ }
816
+
817
+ [data-scope='tabs'][data-part='trigger']:has(+ [data-scope='tabs'][data-part='close-trigger']:not([hidden])) {
818
+ padding-inline-end: calc(var(--xh-_tabs-close-size) + var(--xh-_tabs-close-inset) + var(--xh-space-1));
819
+ }
820
+
821
+ [data-scope='tabs'][data-part='close-trigger'] {
822
+ --xh-action-fg-rest: var(--xh-fg-muted);
823
+ /* 作者塞进来的图标跟着 xs 档走,与皮肤画的兜底字形同一把尺 */
824
+ --xh-icon-size: var(--xh-_action-profile-glyph-size);
825
+
826
+ flex: none;
827
+ /* segment 档的标签抬了一层压在滑动的抬起面上,钮排在标签之后、同一层即压在标签上 */
828
+ z-index: 1;
829
+ margin-inline-start: calc(-1 * var(--xh-_tabs-close-offset));
830
+ margin-inline-end: var(--xh-_tabs-close-inset);
831
+ }
832
+
833
+ /* 竖排:标签上下排开,钮退回的是上一行——起始外边距改在块向,行向贴到行尾 */
834
+ [data-scope='tabs'][data-part='root'][data-orientation='vertical'] [data-scope='tabs'][data-part='close-trigger'] {
835
+ align-self: flex-end;
836
+ margin-block-start: calc(-1 * var(--xh-_tabs-close-offset-block));
837
+ margin-block-end: var(--xh-_tabs-close-inset);
838
+ margin-inline-start: 0;
839
+ }
840
+
841
+ /* 没写内容就由皮肤画一枚叉;作者塞了自己的图形这一条即不命中 */
842
+ [data-scope='tabs'][data-part='close-trigger']:empty::before {
843
+ content: '';
844
+ display: inline-block;
845
+ inline-size: var(--xh-icon-size, var(--xh-glyph-size-text));
846
+ block-size: var(--xh-icon-size, var(--xh-glyph-size-text));
847
+ vertical-align: var(--xh-glyph-baseline-shift);
848
+ background-color: currentColor;
849
+ -webkit-mask: var(--xh-glyph-mark-close) center / contain no-repeat;
850
+ mask: var(--xh-glyph-mark-close) center / contain no-repeat;
851
+ }
852
+
664
853
  /* 声明过任何 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去。
665
854
  写在文件末尾:兜底必须排在该部件所有 display 声明之后 */
666
855
  [data-scope='tabs'][data-part='root'][hidden],
667
856
  [data-scope='tabs'][data-part='list'][hidden],
668
857
  [data-scope='tabs'][data-part='trigger'][hidden],
858
+ [data-scope='tabs'][data-part='close-trigger'][hidden],
669
859
  [data-scope='tabs'][data-part='indicator'][hidden],
670
860
  [data-scope='tabs'][data-part='separator'][hidden],
671
861
  [data-scope='tabs'][data-part='prev-trigger'][hidden],
672
862
  [data-scope='tabs'][data-part='next-trigger'][hidden],
863
+ [data-scope='tabs'][data-part='overflow-trigger'][hidden],
673
864
  [data-scope='tabs'][data-part='tab-drag-trigger'][hidden] {
674
865
  display: none;
675
866
  }
@@ -723,10 +914,31 @@
723
914
  text-decoration: underline;
724
915
  }
725
916
 
726
- /* 纸上翻不了页,两只钮不上纸 */
917
+ /* 纸上翻不了页、关不了标签、也弹不出下拉,这几只钮不上纸 */
727
918
  [data-scope='tabs'][data-part='prev-trigger'],
728
- [data-scope='tabs'][data-part='next-trigger'] {
919
+ [data-scope='tabs'][data-part='next-trigger'],
920
+ [data-scope='tabs'][data-part='overflow-trigger'],
921
+ [data-scope='tabs'][data-part='close-trigger'] {
729
922
  display: none;
730
923
  }
731
924
  }
925
+
926
+ @media (forced-colors: active) {
927
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
928
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
929
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
930
+ 禁用取 GrayText */
931
+ [data-scope='tabs']:is([data-part='prev-trigger'], [data-part='next-trigger'], [data-part='close-trigger'], [data-part='overflow-trigger']):empty::before {
932
+ forced-color-adjust: none;
933
+ background-color: ButtonText;
934
+ }
935
+
936
+ [data-scope='tabs']:is([data-part='prev-trigger'], [data-part='next-trigger'], [data-part='close-trigger'], [data-part='overflow-trigger']):not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
937
+ background-color: HighlightText;
938
+ }
939
+
940
+ [data-scope='tabs']:is([data-part='prev-trigger'], [data-part='next-trigger'], [data-part='close-trigger'], [data-part='overflow-trigger'])[data-disabled]:empty::before {
941
+ background-color: GrayText;
942
+ }
943
+ }
732
944
  }
package/css/tag-group.css CHANGED
@@ -5,6 +5,9 @@
5
5
 
6
6
  /* 定义 tag group 样式。 */
7
7
 
8
+ /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
9
+ @import '../family/motion.css';
10
+
8
11
  @layer xihan.components {
9
12
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
10
13
  [data-scope='tag-group'] {
@@ -61,10 +64,23 @@
61
64
  标签这时是拿焦点的那个节点,按下与松手之间要有回应;选中与锚点的过渡也挂在这一层 */
62
65
  [data-scope='tag-group'][data-part='list'] > [data-scope='tag'][data-part='root'] {
63
66
  user-select: none;
67
+ /* 增删一枚、其余标签换到新位置时,由列表动效先反向补偿、再沿 translate 这条滑过去 */
64
68
  transition:
65
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
69
+ background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
66
70
  color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
67
- scale var(--xh-motion-duration-release) var(--xh-motion-ease-release);
71
+ scale var(--xh-motion-duration-release) var(--xh-motion-ease-release),
72
+ translate var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
73
+ }
74
+
75
+ /* 新加的标签原地弹出(横排胶囊不用竖向上浮),同一批按到达顺序错开;首帧就在的直接呈现 */
76
+ [data-scope='tag-group'][data-part='list']:not([data-instant]) > [data-scope='tag'][data-part='root']:not([data-instant], [data-state='closed']) {
77
+ animation: xh-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong) backwards;
78
+ animation-delay: calc(var(--xh-_stagger-index, 0) * var(--xh-motion-stagger-step));
79
+ }
80
+
81
+ /* 删掉的标签:替身留在原处淡出,播完即移除 */
82
+ [data-scope='tag-group'][data-part='list'] > [data-scope='tag'][data-part='root'][data-state='closed'] {
83
+ animation: xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
68
84
  }
69
85
 
70
86
  /* 参与选中的标签才是点击目标,只是标记的那些不给手势暗示 */
@@ -195,11 +211,19 @@
195
211
  }
196
212
 
197
213
  @media (forced-colors: active) {
198
- /* 选中只由对号表达:遮罩出来的那块底色用系统高亮色画,按下时标签整枚反成 Highlight,对号换成反白 */
214
+ /* 选中只由对号表达:遮罩出来的那块底色退出强制换色、用系统高亮色画,按下时标签整枚反成 Highlight,对号换成反白;
215
+ 禁用的标签取 GrayText */
199
216
  [data-scope='tag-group'][data-part='item-indicator']:empty::before {
217
+ forced-color-adjust: none;
200
218
  background-color: Highlight;
201
219
  }
202
220
 
221
+ [data-scope='tag-group'][data-part='list']
222
+ > [data-scope='tag'][data-part='root'][data-disabled]
223
+ [data-scope='tag-group'][data-part='item-indicator']:empty::before {
224
+ background-color: GrayText;
225
+ }
226
+
203
227
  [data-scope='tag-group'][data-part='list']
204
228
  > [data-scope='tag'][data-part='root'][data-selectable]:not([data-disabled]):not([data-variant='solid']):is(:active, [data-pressed])
205
229
  [data-scope='tag-group'][data-part='item-indicator']:empty::before {
package/css/tag.css CHANGED
@@ -5,6 +5,9 @@
5
5
 
6
6
  /* 定义 tag 样式。 */
7
7
 
8
+ /* 共享关键帧由 family/motion.css 提供,本皮肤 @import 它,单独引入仍成立。 */
9
+ @import '../family/motion.css';
10
+
8
11
  @layer xihan.components {
9
12
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
10
13
  [data-scope='tag'] {
@@ -51,6 +54,12 @@
51
54
  display: none;
52
55
  }
53
56
 
57
+ /* 关掉的标签先原地淡出,播完连接层才写 hidden。
58
+ 标签组与选择框的标签行里删掉的那一枚,由列表动效放回原处的替身也是 closed,同一段淡出 */
59
+ [data-scope='tag'][data-part='root'][data-state='closed'] {
60
+ animation: xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
61
+ }
62
+
54
63
  /* 文字过长时截断,关闭钮才始终留在标签里 */
55
64
  [data-scope='tag'][data-part='label'] {
56
65
  overflow: hidden;
@@ -81,7 +90,7 @@
81
90
  line-height: var(--xh-leading-none);
82
91
  cursor: pointer;
83
92
  transition:
84
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
93
+ background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
85
94
  scale var(--xh-motion-duration-release) var(--xh-motion-ease-release);
86
95
  }
87
96
 
@@ -161,6 +170,21 @@
161
170
  color: var(--xh-tag-fg, var(--xh-_tone-on, var(--xh-fg-on-brand)));
162
171
  }
163
172
 
173
+ /* 实心档是一块彩色面(连接层打 data-xh-ink-surface):面内的图标与作者内容按这块底取墨色。
174
+ 底色随语气与禁用换,这里跟着换 */
175
+ [data-scope='tag'][data-part='root'][data-xh-ink-surface] {
176
+ --xh-ink-surface: var(--xh-tag-bg, var(--xh-bg-brand));
177
+ }
178
+
179
+ [data-scope='tag'][data-part='root'][data-xh-ink-surface][data-tone] {
180
+ --xh-ink-surface: var(--xh-tag-bg, var(--xh-_tone, var(--xh-bg-brand)));
181
+ }
182
+
183
+ [data-scope='tag'][data-part='root'][data-xh-ink-surface][data-disabled] {
184
+ /* 置灰面是墨色淡底:选墨只看分量、不看透明度,取它的不透明档 */
185
+ --xh-ink-surface: var(--xh-tag-bg-disabled, var(--xh-bg-muted-opaque));
186
+ }
187
+
164
188
  [data-scope='tag'][data-part='root'][data-variant='subtle'] {
165
189
  border-color: var(--xh-tag-border, var(--xh-material-soft-border));
166
190
  background: var(--xh-tag-bg, var(--xh-material-soft-bg));
@@ -244,17 +268,6 @@
244
268
  }
245
269
  }
246
270
 
247
- /*
248
- * 高对比档:关闭钮的按压面常态靠从 currentColor 兑出来的底色表出,这一档里底色被统一换成 Canvas,
249
- * 按住那一下与静息塌成同一个样子;用系统高亮反色画回来,兜底字形读 currentColor、自己跟着换
250
- */
251
- @media (forced-colors: active) {
252
- [data-scope='tag'][data-part='close-trigger']:not(:disabled):is(:active, [data-pressed]) {
253
- background: Highlight;
254
- color: HighlightText;
255
- }
256
- }
257
-
258
271
  /* 没写内容就由皮肤画兜底字形;作者往部件里塞了自己的图形,这几条即不命中 */
259
272
  [data-scope='tag'][data-part='close-trigger']:empty::before {
260
273
  content: '';
@@ -266,4 +279,32 @@
266
279
  -webkit-mask: var(--xh-glyph-mark-close) center / contain no-repeat;
267
280
  mask: var(--xh-glyph-mark-close) center / contain no-repeat;
268
281
  }
282
+
283
+ /*
284
+ * 高对比档:关闭钮的按压面常态靠从 currentColor 兑出来的底色表出,这一档里底色被统一换成 Canvas,
285
+ * 按住那一下与静息塌成同一个样子;用系统高亮反色画回来。
286
+ * 兜底字形是 mask 挖出来的一块底色,同样会被换成 Canvas 而消失:退出强制换色、直接写系统色
287
+ * (不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色)——
288
+ * 标签面上取 CanvasText,按住时跟着按压面换 HighlightText,禁用取 GrayText
289
+ */
290
+ @media (forced-colors: active) {
291
+ [data-scope='tag'][data-part='close-trigger']:not(:disabled):is(:active, [data-pressed]) {
292
+ background: Highlight;
293
+ color: HighlightText;
294
+ }
295
+
296
+ [data-scope='tag'][data-part='close-trigger']:empty::before {
297
+ forced-color-adjust: none;
298
+ background-color: CanvasText;
299
+ }
300
+
301
+ [data-scope='tag'][data-part='close-trigger']:not(:disabled):is(:active, [data-pressed]):empty::before {
302
+ background-color: HighlightText;
303
+ }
304
+
305
+ [data-scope='tag'][data-part='root'][data-disabled] [data-scope='tag'][data-part='close-trigger']:empty::before,
306
+ [data-scope='tag'][data-part='close-trigger']:disabled:empty::before {
307
+ background-color: GrayText;
308
+ }
309
+ }
269
310
  }
@@ -6,6 +6,8 @@
6
6
  /* TagsInput 独立皮肤同时传递引入字段视觉盒与字段内动作;full bundle 递归去重。 */
7
7
  @import '../family/field-chrome.css';
8
8
  @import '../family/action-control.css';
9
+ /* 标签的进场与退场取共享关键帧。 */
10
+ @import '../family/motion.css';
9
11
 
10
12
  @layer xihan.components {
11
13
  /* 皮肤在场的标记:开发模式下按 scope 探一次,取不到就说明这份皮肤没被引入。 */
@@ -113,6 +115,8 @@
113
115
  上限取滚动面最小的那一档,与页面上其它滚动面用同一把尺 */
114
116
  max-block-size: var(--xh-tags-input-control-max-h, var(--xh-viewport-h-sm));
115
117
  overflow-y: auto;
118
+ /* 删掉的标签由替身绝对定位在原处播完退场,定位基准是这个框(随框内滚动一起走) */
119
+ position: relative;
116
120
  }
117
121
 
118
122
  [data-scope='tags-input'][data-part='control'][hidden] {
@@ -137,7 +141,7 @@
137
141
 
138
142
  /* 撑满剩余宽度由家族的 flex: 1 1 auto 给,这里只换起算宽 */
139
143
  flex-basis: var(--xh-tags-input-input-basis, 6rem);
140
- min-inline-size: var(--xh-tags-input-input-min-w, 4rem);
144
+ min-inline-size: var(--xh-tags-input-input-min-w, var(--xh-control-input-min-w));
141
145
  /* 行盒收到标签那一行的高度以内,输入框不去顶高整个框 */
142
146
  block-size: auto;
143
147
  line-height: var(--xh-leading-tight);
@@ -151,6 +155,20 @@
151
155
  flex: none;
152
156
  align-items: center;
153
157
  max-inline-size: 100%;
158
+ /* 前面的标签增删、其余标签换到新位置时,由列表动效先反向补偿、再沿这条过渡滑过去 */
159
+ transition: translate var(--xh-motion-duration-move) var(--xh-motion-ease-continuous);
160
+ }
161
+
162
+ /* 新落下的标签原地弹出,同一批按到达顺序错开;首帧就在的直接呈现(自己或 control 带 data-instant)。
163
+ 横排胶囊不用竖向上浮:与来源方向不符,还会探出字段外壳的底边 */
164
+ [data-scope='tags-input'][data-part='item']:not([data-instant], [data-instant] > *) {
165
+ animation: xh-pop-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter-strong) backwards;
166
+ animation-delay: calc(var(--xh-_stagger-index, 0) * var(--xh-motion-stagger-step));
167
+ }
168
+
169
+ /* 删掉的标签:替身留在原处淡出,播完即移除 */
170
+ [data-scope='tags-input'][data-part='item'][data-state='closed'] {
171
+ animation: xh-fade-out var(--xh-motion-duration-exit) var(--xh-motion-ease-exit) forwards;
154
172
  }
155
173
 
156
174
  [data-scope='tags-input'][data-part='item'][hidden] {
@@ -259,11 +277,11 @@
259
277
  mask: var(--xh-glyph-mark-close) center / contain no-repeat;
260
278
  }
261
279
 
262
- /* 计数:排在控件行下面靠尾端的一行小字,与标题分列 root 的两端 */
280
+ /* 计数:排在控件行下面靠尾端的一行小字,与标题分列 root 的两端。它是次级标注,取说明档字号,不随控件档 */
263
281
  [data-scope='tags-input'][data-part='count'] {
264
282
  align-self: flex-end;
265
283
  color: var(--xh-tags-input-count-fg, var(--xh-fg-muted));
266
- font-size: var(--xh-tags-input-count-font-size, var(--xh-_tags-input-item-font-size));
284
+ font-size: var(--xh-tags-input-count-font-size, var(--xh-text-caption-size));
267
285
  font-variant-numeric: tabular-nums;
268
286
  line-height: var(--xh-leading-normal);
269
287
  }
@@ -282,4 +300,23 @@
282
300
  [data-scope='tags-input'][data-part='count'][data-disabled] {
283
301
  color: var(--xh-tags-input-count-fg-disabled, var(--xh-fg-disabled));
284
302
  }
303
+
304
+ @media (forced-colors: active) {
305
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
306
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
307
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
308
+ 禁用取 GrayText */
309
+ [data-scope='tags-input'][data-part='clear-trigger']:empty::before {
310
+ forced-color-adjust: none;
311
+ background-color: ButtonText;
312
+ }
313
+
314
+ [data-scope='tags-input'][data-part='clear-trigger']:not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
315
+ background-color: HighlightText;
316
+ }
317
+
318
+ [data-scope='tags-input'][data-part='clear-trigger'][data-disabled]:empty::before {
319
+ background-color: GrayText;
320
+ }
321
+ }
285
322
  }
@@ -175,4 +175,23 @@
175
175
  [data-scope='text-field'][data-part='count'][data-disabled] {
176
176
  color: var(--xh-text-field-count-fg-disabled, var(--xh-fg-disabled));
177
177
  }
178
+
179
+ @media (forced-colors: active) {
180
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
181
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
182
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
183
+ 禁用取 GrayText */
184
+ [data-scope='text-field'][data-part='clear-trigger']:empty::before {
185
+ forced-color-adjust: none;
186
+ background-color: ButtonText;
187
+ }
188
+
189
+ [data-scope='text-field'][data-part='clear-trigger']:not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
190
+ background-color: HighlightText;
191
+ }
192
+
193
+ [data-scope='text-field'][data-part='clear-trigger'][data-disabled]:empty::before {
194
+ background-color: GrayText;
195
+ }
196
+ }
178
197
  }
@@ -139,7 +139,7 @@
139
139
  /* 触屏上连点两下不该被当成缩放手势 */
140
140
  touch-action: manipulation;
141
141
  transition:
142
- background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
142
+ background-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
143
143
  color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
144
144
  }
145
145
 
@@ -255,4 +255,23 @@
255
255
  white-space: nowrap;
256
256
  pointer-events: none;
257
257
  }
258
+
259
+ @media (forced-colors: active) {
260
+ /* 兜底字形是 mask 挖出来的一块底色,这一档里底色被系统换成 Canvas,字形跟着消失。
261
+ 字形退出强制换色、直接写系统色(不能取 currentColor:退出强制换色的伪元素读到的是被系统换掉之前的作者色):
262
+ 按钮面上取 ButtonText,悬停与按下时家族把按钮涂成 Highlight、字形换 HighlightText;
263
+ 禁用取 GrayText */
264
+ [data-scope='time-field'][data-part='clear-trigger']:empty::before {
265
+ forced-color-adjust: none;
266
+ background-color: ButtonText;
267
+ }
268
+
269
+ [data-scope='time-field'][data-part='clear-trigger']:not([data-disabled]):not([data-loading]):is(:hover, :active, [data-pressed]):empty::before {
270
+ background-color: HighlightText;
271
+ }
272
+
273
+ [data-scope='time-field'][data-part='clear-trigger'][data-disabled]:empty::before {
274
+ background-color: GrayText;
275
+ }
276
+ }
258
277
  }