@greypan/web-ui 7.0.0 → 8.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 (120) hide show
  1. package/README.CN.md +109 -24
  2. package/README.md +149 -63
  3. package/dist/assets/menu-portal.js +1 -1
  4. package/dist/assets/menu-portal.js.map +1 -1
  5. package/dist/assets/selection-control.js +1 -1
  6. package/dist/assets/selection-control.js.map +1 -1
  7. package/dist/components/autocomplete/index.d.ts +17 -5
  8. package/dist/components/autocomplete/index.js +138 -103
  9. package/dist/components/autocomplete/index.js.map +1 -1
  10. package/dist/components/autocomplete/style.js +1 -1
  11. package/dist/components/autocomplete/style.js.map +1 -1
  12. package/dist/components/avatar/index.js +4 -4
  13. package/dist/components/badge/style.js.map +1 -1
  14. package/dist/components/button/index.d.ts +368 -1
  15. package/dist/components/button/index.js +61 -41
  16. package/dist/components/button/index.js.map +1 -1
  17. package/dist/components/button/style.js +1 -1
  18. package/dist/components/button/style.js.map +1 -1
  19. package/dist/components/button-group/index.js +5 -5
  20. package/dist/components/checkbox/index.d.ts +3 -0
  21. package/dist/components/checkbox/index.js +30 -32
  22. package/dist/components/checkbox/index.js.map +1 -1
  23. package/dist/components/checkbox/style.js +1 -1
  24. package/dist/components/checkbox/style.js.map +1 -1
  25. package/dist/components/checkbox-group/index.js +4 -4
  26. package/dist/components/context-menu/index.js +4 -1
  27. package/dist/components/context-menu/index.js.map +1 -1
  28. package/dist/components/dialog/index.js +11 -8
  29. package/dist/components/dialog/index.js.map +1 -1
  30. package/dist/components/drawer/index.js +11 -8
  31. package/dist/components/drawer/index.js.map +1 -1
  32. package/dist/components/dropdown/index.js +4 -1
  33. package/dist/components/dropdown/index.js.map +1 -1
  34. package/dist/components/dropdown-item/style.js +1 -1
  35. package/dist/components/dropdown-item/style.js.map +1 -1
  36. package/dist/components/editable-text/index.d.ts +473 -0
  37. package/dist/components/editable-text/index.js +226 -0
  38. package/dist/components/editable-text/index.js.map +1 -0
  39. package/dist/components/editable-text/style.js +6 -0
  40. package/dist/components/editable-text/style.js.map +1 -0
  41. package/dist/components/icon/index.js +7 -7
  42. package/dist/components/icon/index.js.map +1 -1
  43. package/dist/components/image-preview/index.js +21 -18
  44. package/dist/components/image-preview/index.js.map +1 -1
  45. package/dist/components/index.d.ts +1 -0
  46. package/dist/components/index.js +39 -38
  47. package/dist/components/input/index.d.ts +9 -0
  48. package/dist/components/input/index.js +41 -30
  49. package/dist/components/input/index.js.map +1 -1
  50. package/dist/components/input-number/index.d.ts +1 -0
  51. package/dist/components/input-number/index.js +26 -24
  52. package/dist/components/input-number/index.js.map +1 -1
  53. package/dist/components/layout/index.js +8 -8
  54. package/dist/components/layout/index.js.map +1 -1
  55. package/dist/components/layout/style.js +1 -1
  56. package/dist/components/layout/style.js.map +1 -1
  57. package/dist/components/option/style.js +1 -1
  58. package/dist/components/option/style.js.map +1 -1
  59. package/dist/components/popover/index.js +13 -10
  60. package/dist/components/popover/index.js.map +1 -1
  61. package/dist/components/popover/style.js +1 -1
  62. package/dist/components/popover/style.js.map +1 -1
  63. package/dist/components/radio/index.d.ts +3 -0
  64. package/dist/components/radio/index.js +22 -18
  65. package/dist/components/radio/index.js.map +1 -1
  66. package/dist/components/radio-group/index.js +4 -4
  67. package/dist/components/segmented/index.d.ts +4 -1
  68. package/dist/components/segmented/index.js +42 -29
  69. package/dist/components/segmented/index.js.map +1 -1
  70. package/dist/components/segmented/style.js +1 -1
  71. package/dist/components/segmented/style.js.map +1 -1
  72. package/dist/components/segmented-trigger/style.js +1 -1
  73. package/dist/components/segmented-trigger/style.js.map +1 -1
  74. package/dist/components/select/index.js +35 -32
  75. package/dist/components/select/index.js.map +1 -1
  76. package/dist/components/select/style.js +1 -1
  77. package/dist/components/select/style.js.map +1 -1
  78. package/dist/components/slider/index.js +11 -11
  79. package/dist/components/switch/index.d.ts +1 -0
  80. package/dist/components/switch/index.js +34 -32
  81. package/dist/components/switch/index.js.map +1 -1
  82. package/dist/components/textarea/index.d.ts +7 -1
  83. package/dist/components/textarea/index.js +46 -44
  84. package/dist/components/textarea/index.js.map +1 -1
  85. package/dist/components/textarea/style.js.map +1 -1
  86. package/dist/components/theme/index.d.ts +10 -3
  87. package/dist/components/theme/index.js +133 -93
  88. package/dist/components/theme/index.js.map +1 -1
  89. package/dist/components/theme/style.js +1 -1
  90. package/dist/components/theme/style.js.map +1 -1
  91. package/dist/components/toast/toast.js +7 -7
  92. package/dist/components/toast/toast.js.map +1 -1
  93. package/dist/components/tooltip/index.js +12 -9
  94. package/dist/components/tooltip/index.js.map +1 -1
  95. package/dist/components/tooltip/style.js +1 -1
  96. package/dist/components/tooltip/style.js.map +1 -1
  97. package/dist/icons/generated/akar-icons-check.d.ts +6 -0
  98. package/dist/icons/generated/akar-icons-check.js +10 -0
  99. package/dist/icons/generated/akar-icons-check.js.map +1 -0
  100. package/dist/icons/generated/bi-check.d.ts +6 -0
  101. package/dist/icons/generated/bi-check.js +10 -0
  102. package/dist/icons/generated/bi-check.js.map +1 -0
  103. package/dist/icons/index.d.ts +2 -0
  104. package/dist/icons/index.js +3 -1
  105. package/dist/shared/form-association/field-id.js +9 -0
  106. package/dist/shared/form-association/field-id.js.map +1 -0
  107. package/dist/shared/form-association/index.js +9 -1
  108. package/dist/shared/form-association/index.js.map +1 -1
  109. package/dist/shared/label-emptiness/index.js +45 -0
  110. package/dist/shared/label-emptiness/index.js.map +1 -0
  111. package/dist/shared/option-portal/combobox-shell.js +47 -5
  112. package/dist/shared/option-portal/combobox-shell.js.map +1 -1
  113. package/dist/shared/option-portal/index.js +9 -9
  114. package/dist/shared/overlay/anchored-panel.js +2 -2
  115. package/dist/shared/overlay/anchored-panel.js.map +1 -1
  116. package/dist/shared/overlay/open-overlay.js +38 -22
  117. package/dist/shared/overlay/open-overlay.js.map +1 -1
  118. package/dist/shared/theme/root-sync.js +48 -0
  119. package/dist/shared/theme/root-sync.js.map +1 -0
  120. package/package.json +3 -1
package/README.CN.md CHANGED
@@ -239,12 +239,15 @@ dropdown、tooltip)不需要它。
239
239
 
240
240
  所有表单控件均参与原生 `FormData`、约束校验、`form.reset()` 和浏览器表单状态恢复。控件会在**首次连接且声明式属性完成初始化后**捕获一次重置默认值;之后的运行时 property 更新不会改写该默认值。祖先 `fieldset` 的禁用状态会禁用交互和校验,但不会改写控件公开的 `disabled` 属性。对于 checkbox/radio group,父 group 是提交、重置和状态恢复的唯一所有者;被管理的子项不会独立提交或恢复状态。
241
241
 
242
+ `web-ui-button` 同样声明为 form-associated,使宿主可以持有外层 form 的 form owner 并转发 `submit`/`reset` 激活;它不向 `FormData` 贡献值。
243
+
242
244
  ## 所有组件
243
245
 
244
246
  | 分类 | 组件 |
245
247
  | --------------------- | --------------------------------------------------------- |
246
248
  | **表单控件** | [`<web-ui-input>`](#web-ui-input) |
247
249
  | | [`<web-ui-textarea>`](#web-ui-textarea) |
250
+ | | [`<web-ui-editable-text>`](#web-ui-editable-text) |
248
251
  | | [`<web-ui-input-number>`](#web-ui-input-number) |
249
252
  | | [`<web-ui-select>`](#web-ui-select) |
250
253
  | | [`<web-ui-autocomplete>`](#web-ui-autocomplete) |
@@ -307,6 +310,8 @@ dropdown、tooltip)不需要它。
307
310
 
308
311
  **事件:** `input`, `change`, `focus`, `blur`
309
312
 
313
+ **方法:** `focus()`, `blur()`, `select()`,委托到 shadow 内的原生 input;宿主自身不可聚焦。`disabled` 时 `focus()` 与 `select()` 不产生效果,`readonly` 仍允许聚焦和选中。
314
+
310
315
  **插槽:** `prefix`, `default`, `suffix`
311
316
 
312
317
  **CSS 自定义属性:**
@@ -340,7 +345,7 @@ dropdown、tooltip)不需要它。
340
345
 
341
346
  **事件:** `input`, `change`, `focus`, `blur`
342
347
 
343
- **方法:** `focus()`, `blur()`, `select()`
348
+ **方法:** `focus()`, `blur()`, `select()`,委托到 shadow 内的原生 textarea。`disabled` 时 `focus()` 与 `select()` 不产生效果,`readonly` 仍允许聚焦和选中。
344
349
 
345
350
  **插槽:** `prefix`, `suffix`
346
351
 
@@ -351,6 +356,42 @@ dropdown、tooltip)不需要它。
351
356
  | `--wui-textarea-width` | `200px` | 文本域宽度 |
352
357
  | `--wui-textarea-clear-color` | `var(--wui-color-text-tertiary)` | 清除按钮颜色 |
353
358
 
359
+ #### `<web-ui-editable-text>`
360
+
361
+ 行内纯文本编辑器:点击文字就地编辑,`Enter` 与 `blur` 提交,`Escape` 取消。文本层与编辑层共用一个盒,进入编辑态不会移动任何一个像素。
362
+
363
+ | 属性 | 类型 | 默认值 | 说明 |
364
+ | ------------- | --------- | ------- | --------------------------------------------------------------------------------------------------------- |
365
+ | `value` | `string` | `''` | 当前值;首次连接时捕获声明式初值作为 `form.reset()` 默认,连接后修改 attribute 或 property 均不更新该初值 |
366
+ | `placeholder` | `string` | `''` | 值为空时显示的占位文本 |
367
+ | `name` | `string` | `''` | 表单字段名 |
368
+ | `disabled` | `boolean` | `false` | 禁用状态;只影响行为,不做视觉置灰 |
369
+ | `readonly` | `boolean` | `false` | 只读状态;仍可聚焦、全选和复制,但不能编辑或提交 `change` |
370
+ | `aria-label` | `string` | — | 无障碍标签 |
371
+
372
+ **事件:** `input`(每次输入)、`change`(提交)、`cancel`(取消;与原生 `<dialog>` 的 `cancel` 同名,不冒泡、不组合,只在组件本身派发)。React 没有覆盖 `cancel` 的合成事件,须用 `addEventListener('cancel', ...)` 监听
373
+
374
+ **方法:** `focus()`, `blur()`, `select()`
375
+
376
+ 点击时光标落在点击处;键盘聚焦时落在文本末尾。`Enter` 与 `blur` 均提交草稿并恰好派发一次 `change`:`Enter` 不插入换行,并把焦点交还宿主;`blur` 不干预焦点,焦点留在用户移往的位置。只有 `Escape` 取消:值回到进入编辑时的状态,派发 `cancel` 而不派发 `change`,焦点交还宿主,且按键由编辑层消费,外层浮层(抽屉、菜单)不会因同一次按键关闭。`cancel` 不冒泡也不组合,只在组件本身派发:组件被投映在浮层 shadow 内时(如 drawer 标题),它不会触达浮层的原生 `cancel` 关闭管线,监听一律挂在组件本身。值里已有的换行仍按多行渲染,只是不能再靠输入 `Enter` 增加换行。空值继续显示 placeholder;编辑层始终按自身内容撑高,因此空草稿或纯空格草稿在宿主自身塌缩的场合(flex 项 `min-width: 0`、表格单元格)也仍有承接光标的位置。
377
+
378
+ `select()` 进入编辑态并全选内容;已在编辑态时只重新全选。`disabled` 时与 `focus()` 一样不产生效果。`readonly` 时仍可聚焦和全选,但输入被拒绝,退出编辑态也不派发 `change`。
379
+
380
+ 宿主是行内级盒子:未设宽度时随内容伸缩,折行后高度按行数增长。字体、颜色、文本对齐与空白处理全部继承外部上下文,因此编辑前它就是一段普通文字。
381
+
382
+ 光标颜色默认跟随共享语义 token `--wui-color-accent`;在宿主或任意祖先上设置该 token 即可整体改色。两层对长无空格串(连续字母数字)按同一组断行点折行:超出盒宽的长串在盒内断开,文字态不再溢出宿主盒。
383
+
384
+ **CSS 自定义属性:**
385
+
386
+ | 属性 | 默认值 | 说明 |
387
+ | ----------------------------------- | ---------- | ---------------------------------------------------------------- |
388
+ | `--wui-editable-text-white-space` | `pre-wrap` | 两层的空白处理方式 |
389
+ | `--wui-editable-text-overflow-wrap` | `anywhere` | 两层的断词处理方式;默认让长无空格串在盒内断开,与原生编辑层一致 |
390
+
391
+ 共用盒子带来两个约束:`line-height` 需不小于 `1`,更紧凑时原生编辑层内容会高出自身盒子,文字被顶偏 1px;`nowrap` 加固定宽度时,超出盒宽的文案在文字态溢出显示、在编辑态于盒内滚动。
392
+
393
+ **交互变更:** `blur` 恢复提交:`Enter` 与 `blur` 均提交草稿,只有 `Escape` 取消,值回到进入编辑时的状态。`Enter` 仍不插入换行。`cancel` 事件不再冒泡:此前冒泡的自定义 `cancel` 会经 slot 进入外层浮层 shadow 内的 `<dialog>`(`composed` 与否都会),被当成关闭请求一并关掉(issue #159);监听请挂在组件本身。
394
+
354
395
  #### `<web-ui-input-number>`
355
396
 
356
397
  数字输入框,支持步进按钮和键盘操作。
@@ -429,10 +470,26 @@ Portal 面板创建时会镜像 host 上解析后的这些变量;更新 host
429
470
 
430
471
  **事件:** `input`, `change`, `focus`, `blur`, `open-change` (`CustomEvent<{ open: boolean }>`)
431
472
 
432
- **插槽:** `default`(投影 `<web-ui-option>` 元素)、`empty`(替换无匹配空态;默认回退为“无匹配选项”)
473
+ **方法:** `focus()`, `blur()`
474
+
475
+ **插槽:** `default`(投影 `<web-ui-option>` 元素)、`trigger`(自定义触发器内容——替换默认输入框)、`empty`(替换无匹配空态;默认回退为「无匹配选项」)
433
476
 
434
477
  键入时按 label 过滤候选(`contains` 或 `prefix`,`none` 关闭过滤)。选择 option 时文本回填为该项 label,`selected-value` 暴露该项的 value;`change` 在选择提交时触发。支持 ArrowDown/ArrowUp/Enter/Escape 键盘导航。
435
478
 
479
+ **触发器:** 默认触发器是 shadow 内的 `web-ui-input`。把任意可编辑组件放进 `trigger` slot 即可替换它——包装 div 继续承载 combobox ARIA,并以 `data-custom-trigger` 标记当前使用自定义触发器,浮层始终以触发器元素为锚点。组件的 `focus()` / `blur()` 委托到当前生效的触发器:`web-ui-input` 与 `web-ui-textarea` 会把焦点落到内部原生控件;自定义触发器没有自己的 focus 重定向时,按宿主自身聚焦。
480
+
481
+ ```html
482
+ <web-ui-autocomplete placeholder="描述问题">
483
+ <web-ui-textarea slot="trigger" rows="3"></web-ui-textarea>
484
+ <web-ui-option value="bug" label="缺陷"></web-ui-option>
485
+ <web-ui-option value="feature" label="需求"></web-ui-option>
486
+ </web-ui-autocomplete>
487
+ ```
488
+
489
+ 自定义触发器与默认触发器共用同一份契约:字符串 `value` 承载文本、可聚焦,并派发组件委托监听的事件(`input`、`click`、`focus`、`blur`)。`web-ui-input` 与 `web-ui-textarea` 开箱即用;自定义元素只要暴露字符串 `value` property 即可接入。包装 div 不占 tab 位(`tabindex="-1"`):顺序焦点归触发器自身,自定义触发器必须可聚焦,键盘用户才能到达 combobox。
490
+
491
+ 多行触发器(可编辑元素为 `<textarea>`)保留 Enter 换行语义:Enter 不会选中高亮项,也不会提交 custom value,关闭面板用 Escape 或 blur。选择 option 仍会把该项 label 回写到触发器。单行自定义触发器保持默认的 Enter 语义。面板打开时 ArrowUp/ArrowDown 适用同一例外:方向键移动文本光标而不导航候选,该状态下键盘无法导航 option——用指针点击选择。面板关闭时 ArrowDown/ArrowUp 仍可打开面板。
492
+
436
493
  启用 `allow-custom-value` 后,无匹配且无活动 option 时,Enter 会把当前输入原文作为 custom value 提交并关闭面板;`change` 会触发,`selected-value` 保持为空。组件不会自动创建 option,也不会 trim 原文。命中禁用 option 的文本不会绕过禁用语义,也不会派生为已选 option。
437
494
 
438
495
  通过 `<div slot="empty">…</div>` 自定义静态、非交互的空态内容。Portal 渲染时该节点会迁入浮层,关闭后恢复到宿主。
@@ -499,9 +556,9 @@ Portal 面板创建时会镜像 host 上解析后的这些变量;更新 host
499
556
 
500
557
  使用 `role="checkbox"` 和 `aria-checked`。Enter/Space 键盘切换。
501
558
 
502
- **布局:** 宿主是 inline-flex 盒,高度由内容撑开、不继承页面行高,因此不会在指示器上下留出多余缝隙;`--wui-selection-control-size`(`18px`)决定指示器宽高,宿主与相邻文字的对齐固定为 `vertical-align: middle`。`<web-ui-radio>` 共用同一套契约。
559
+ **布局:** 宿主是 inline-flex 盒,高度由内容撑开、不继承页面行高,因此不会在指示器上下留出多余缝隙;`--wui-selection-control-size`(`18px`)决定指示器宽高,宿主与相邻文字的对齐固定为 `vertical-align: middle`。`<web-ui-radio>` 共用同一套契约。指示器与标签之间的 10px 归标签所有:default slot 没画出东西时(空着,或里面只放了一个视觉隐藏的无障碍名,例如 `.sr-only` 的 span)这段间距一并收起,宿主宽度就等于指示器。空不空按标签盒子的渲染宽度判定,而不是按 slot 被指派了节点判定——无障碍名正是「有指派、不画盒」的那一种;也不能用 `display: none` 收掉标签,那会把这个名字从无障碍树里拿掉。
503
560
 
504
- **选中动画:** 对勾是控件自持的描边路径(不再走 `<web-ui-icon>` 图标资产),外层包 `<web-ui-svg-draw-lines>` 并带 `no-autoplay`,所以挂载时就已勾选的控件显示静态勾。勾选时线条自左向右按恒定笔速画出,时长取 `--wui-duration-trigger`(默认 160ms),在切换当下从已生效的主题解析,和指示器底色那条 transition 落在同一拍;取消时沿同一条路径收回到空白,而不是只淡出;主题范围为 `motion="reduced"` 时两者都跳过,两个状态直接切换。
561
+ **选中动画:** 对勾是 `@/icons` 里的描边资产,由嵌套的 `<web-ui-icon>` 渲染在 `<web-ui-svg-draw-lines>` 内,并带 `no-autoplay`,所以挂载时就已勾选的控件显示静态勾。勾选时线条从左侧尖端起按恒定笔速画出,时长取 `--wui-duration-trigger`(默认 160ms),在切换当下从已生效的主题解析,和指示器底色那条 transition 落在同一拍;取消时沿同一条路径收回到空白,而不是只淡出;主题范围为 `motion="reduced"` 时两者都跳过,两个状态直接切换。描边颜色是 `--wui-color-on-control`,经 `<web-ui-icon>` 的 `--wui-icon-color` 传下去,因为勾选后它背后就是 `--wui-color-accent`。这个结构带来两条约束:资产必须保持描边(`fill: none` + `stroke: currentColor`),换成实心图标 dash 动画照跑却什么都不画,勾会直接出现;并且它按 `<web-ui-icon>` 自己的默认 18px 渲染,宿主够不到图标 shadow root 里的 `<svg>`,因此不随 `--wui-selection-control-size` 放大。
505
562
 
506
563
  **未激活态:** 未选中指示器的底色取 `--wui-color-surface-control`(与中性按钮同一档控件底),深色模式下也能和 `--wui-color-page` 分辨开。触发区内任意位置(指示器、间距或右侧 slot 标签)被 hover 时,该底色再叠 6% 状态层。hover 只在 `(hover: hover) and (pointer: fine)` 设备上生效;没有按下态,已选中和禁用态保持各自底色。`<web-ui-radio>` 共用同一套状态。
507
564
 
@@ -521,7 +578,7 @@ Portal 面板创建时会镜像 host 上解析后的这些变量;更新 host
521
578
 
522
579
  **插槽:** `default`(标签文本)
523
580
 
524
- **布局:** 与 `<web-ui-checkbox>` 共用同一套选择控件盒契约——宿主高度由内容撑开、不继承页面行高,指示器宽高走 `--wui-selection-control-size`,与相邻文字按 `vertical-align: middle` 对齐。
581
+ **布局:** 与 `<web-ui-checkbox>` 共用同一套选择控件盒契约,包括「标签没有画出东西时收起间距」这一条——宿主高度由内容撑开、不继承页面行高,指示器宽高走 `--wui-selection-control-size`,与相邻文字按 `vertical-align: middle` 对齐。
525
582
 
526
583
  #### `<web-ui-switch>`
527
584
 
@@ -544,23 +601,30 @@ Portal 面板创建时会镜像 host 上解析后的这些变量;更新 host
544
601
 
545
602
  分段控制——单选按钮组。
546
603
 
547
- | 属性 | 类型 | 默认值 | 说明 |
548
- | ---------- | --------- | ------- | ---------------- |
549
- | `value` | `string` | `''` | 当前选中值 |
550
- | `name` | `string` | `''` | 表单字段名 |
551
- | `disabled` | `boolean` | `false` | 禁用全部 trigger |
552
- | `required` | `boolean` | `false` | 必填校验 |
604
+ | 属性 | 类型 | 默认值 | 说明 |
605
+ | ---------- | ------------------- | ------- | ---------------------- |
606
+ | `value` | `string` | `''` | 当前选中值 |
607
+ | `name` | `string` | `''` | 表单字段名 |
608
+ | `disabled` | `boolean` | `false` | 禁用全部 trigger |
609
+ | `required` | `boolean` | `false` | 必填校验 |
610
+ | `variant` | `inset` \| `raised` | `inset` | 轨道与静止指示器的材质 |
553
611
 
554
612
  **事件:** `input`, `change`
555
613
 
556
614
  **插槽:** `default`(投影 `<web-ui-segmented-trigger>` 元素)
557
615
 
616
+ **变体:** `inset`(默认,「凹」)是不透明实体轨道(`--wui-color-surface`)——1px 描边环与投影全程常驻,静止指示器是 `--wui-color-surface-segmented` 纯灰底,视觉上嵌入轨道。浅色下该 token 与 `--wui-color-page` 同值,轨道与页面齐平,凹感只由常驻环与投影承担;深色下它是「比页面高一档」的 elevation,轨道依旧浮离页面。`raised`(「凸」)是经典形态:flat 灰轨道(`--wui-color-surface-segmented`,无环无投影),静止指示器是 `--wui-color-surface-selected` 实体底加柔投影,浮起于轨道之上。两个变体的按压/拖拽态一致——指示器转透明玻璃(backdrop blur、80ms 淡入的描边环、1.5x 放大),松开淡回各自静止面。非法值回退 `inset`。
617
+
558
618
  与原生 `<form>` 集成(通过 `ElementInternals`)。
559
619
 
560
620
  根据 `value` 同步子 trigger 的 `checked` 状态。`disabled` 提供继承的有效禁用状态,不改写 trigger 自身的 `disabled` 属性。直接设 `value` 不派发事件。
561
621
 
562
622
  按住当前选中项横向拖拽可滑动指示器,松手吸附到最近选项(快速抛掷按速度切换)。触摸面声明 `touch-action: none`,拖拽激活期间阻止 `touchmove` 默认滚动,避免 iOS Safari 中断手势。
563
623
 
624
+ **按压态:** 轨道在所有状态下都是不透明实体面(`--wui-color-surface`,浅色即 page 同色、无 backdrop filter),1px 描边环与投影全程不变;转为玻璃的是指示器这一层。静止态指示器是 `--wui-color-surface-segmented` 实色底、玻璃输出全部关闭;按压/拖拽态获得 backdrop blur、随 80ms 过渡淡入的玻璃描边环与 1.5x 放大,底色全透明:指示器的 blur 采样面整个落在轨道之内,此前的玻璃轨道上全透明指示器在均匀深底与相邻轨道仅差 +0.5 阶,条纹底实测指示器内部灰度 std 1.89(背景渗透);轨道实体化后同一采样 std 归零,按压态本体的可感知性由实色轨道与放大/环/投影共同承担,不再需要 tint 打底或压制轨道边缘装饰。底色不淡入——按下第一帧即落定;松开时指示器经 `--wui-duration-press` 淡回实色灰底。
625
+
626
+ **文字色:** 选中与未选中项文字一律保持 `--wui-color-text-secondary`——`inset`/`raised` 两个变体一致,静止态与按压/拖拽态一致。选中态只由指示器承担,任何标签都不着 `--wui-color-accent`。
627
+
564
628
  #### `<web-ui-checkbox-group>`
565
629
 
566
630
  多选复选框组。
@@ -652,9 +716,9 @@ web-ui-radio-group {
652
716
 
653
717
  **插槽:** `prefix`, `default`, `suffix`
654
718
 
655
- `submit` 和 `reset` 不会提交或重置组件 Shadow DOM 外祖先 `<form>`。如需外部表单行为,请使用 form-associated 控件。
719
+ `submit` 和 `reset` 在 composed `click` 事件完成派发后,通过宿主的 form owner 转发到其原生 `<form>`;对这次 click 调用 `preventDefault()` 会取消该动作。内部按钮自身仍没有 form owner,因此 `SubmitEvent.submitter` 为 `null`。按钮不向 `FormData` 贡献值。
656
720
 
657
- 禁用和加载状态阻止 `click` 事件。
721
+ 禁用、加载和 `formDisabled` 状态(包括祖先 `fieldset` 导致的禁用)都会阻止 `click` 事件。
658
722
 
659
723
  `icon` 与 `loading` 同时开启时,按钮只渲染 spinner,默认插槽图标不投影。默认 icon 几何保持正方形,`full` 或显式 width 除外。
660
724
 
@@ -682,6 +746,14 @@ web-ui-radio-group {
682
746
 
683
747
  以内部派生的视觉上下文控制按钮组方向,不改写子按钮属性。
684
748
 
749
+ **CSS 自定义属性:**
750
+
751
+ | 属性 | 默认值 | 说明 |
752
+ | ----------------------------------- | ------ | ---------------------------------------------------- |
753
+ | `--wui-button-group-divider-length` | `20px` | 相邻按钮之间分割线的长度;短边恒为 1px,不随该值变化 |
754
+
755
+ 在按钮组宿主上设置。该 token 控制两个方向上的长边:横排改的是分割线高度,竖排改的是宽度。
756
+
685
757
  ---
686
758
 
687
759
  ### 浮层 / 模态
@@ -706,7 +778,7 @@ web-ui-radio-group {
706
778
 
707
779
  使用原生 `<dialog>`,`@cancel` 阻止默认关闭行为。除非存在 `no-escape-close`,否则 Escape 调用 `close()`;除非存在 `no-backdrop-close`,否则点击遮罩关闭。启用 `controlled` 后,两者都只派发关闭请求而不自关闭。
708
780
 
709
- > **Escape 归属**:Escape 由共享仲裁者统一判定,一次按键只关闭**最内层**的已打开浮层(popover、select、autocomplete、dropdown、context-menu、drawer、dialog 都参与)。例如在 drawer 内打开 select,第一次 Escape 只关 select,第二次才关 drawer。互不嵌套的并列浮层按打开顺序关闭最上层。`image-preview` 同样参与:它的原生 `<dialog>` 会登记进同一个仲裁者,Escape 按层级判定;组件的 `cancel` handler 只是拦掉原生的瞬时关闭,把 top layer 保留到退场过渡结束。
781
+ > **Escape 归属**:Escape 由共享仲裁者统一判定,一次按键只关闭**最内层**的已打开浮层(popover、select、autocomplete、dropdown、context-menu、drawer、dialog 都参与)。例如在 drawer 内打开 select,第一次 Escape 只关 select,第二次才关 drawer。互不嵌套的并列浮层按打开顺序关闭最上层。正在播退场过渡的面板仍在场上,也仍由它接住 Escape——但只要还有别的浮层开着,那一次按键仍然归该层,「一次按键关一层」不因此改变。`image-preview` 同样参与:它的原生 `<dialog>` 会登记进同一个仲裁者,Escape 按层级判定;组件的 `cancel` handler 只是拦掉原生的瞬时关闭,把 top layer 保留到退场过渡结束。
710
782
 
711
783
  **CSS 自定义属性:**
712
784
 
@@ -715,7 +787,7 @@ web-ui-radio-group {
715
787
  | `--wui-dialog-width` | `360px` | 对话框宽度 |
716
788
  | `--wui-dialog-max-height` | `90vh` | 对话框最大高度 |
717
789
  | `--wui-dialog-overlay-bg` | `var(--wui-color-backdrop)` | 遮罩背景色 |
718
- | `--wui-dialog-bg` | `var(--wui-color-surface-overlay)` | 玻璃卡片背景色,回退到 `rgb(246 246 246 / 0.88)` |
790
+ | `--wui-dialog-bg` | `var(--wui-color-surface-overlay)` | 玻璃卡片背景色,回退到 `rgb(246 246 246 / 0.82)` |
719
791
  | `--wui-dialog-padding` | `20px 24px 24px` | 对话框表面内边距 |
720
792
  | `--wui-dialog-title-gap` | `16px` | 标题下方间距 |
721
793
  | `--wui-dialog-desc-gap` | `24px` | 正文内容下方间距 |
@@ -1128,6 +1200,8 @@ Hover 模式使用 `pointerenter`/`pointerleave` 加延迟控制。Click 模式
1128
1200
 
1129
1201
  内置 `aria-hidden="true"`。
1130
1202
 
1203
+ **画布:** SVG 的 `viewBox` 取自 `icon.left`/`icon.top`/`icon.width`/`icon.height`,偏移缺省为 `0`,尺寸缺省为 Iconify 规范默认的 `16`。生成的资产都自带画布,因此尺寸兜底只在直接传入原始 `IconifyIcon` 对象时才会生效。
1204
+
1131
1205
  ```js
1132
1206
  import { lucideLoaderCircle } from '@greypan/web-ui/icons'
1133
1207
  html`<web-ui-icon .icon=${lucideLoaderCircle} spin />`
@@ -1241,6 +1315,8 @@ WebUiSpinner.hide() // 隐藏
1241
1315
 
1242
1316
  `header-glow` 会在 header 插槽内容和移动端 Toggle 的背后添加 `pointer-events: none` 的装饰性晕染。它属于 Header 背景而非前景层,因此插槽内容始终位于其上方;可通过 `--wui-layout-header-glow-color` 覆盖颜色,默认值为 `--wui-color-page`。晕染浓度和范围由内部变量 `--wui-layout-header-glow-height`(默认 `150%`)控制;增大可加强覆盖,减小则更柔和。布局层级顺序为 Header(`10`)< Auxiliary(`20`)< Banner(`30`)< Tabbar(`40`)< Sidebar(`50`)。
1243
1317
 
1318
+ 侧边栏卡片表面使用 `--wui-color-surface-sidebar`:浅色保持半透明中性分层,深色比 `--wui-color-page` 浅一档、与 `--wui-color-surface` 同级。它独立成 token,是因为 dialog、drawer 和 toast 共用 `--wui-color-surface-overlay`,可以采用不同表面。
1319
+
1244
1320
  **CSS 自定义属性:**
1245
1321
 
1246
1322
  | 属性 | 默认值 | 说明 |
@@ -1304,11 +1380,11 @@ SVG 线条绘制动画,基于 `stroke-dashoffset`。直接在原元素上动
1304
1380
 
1305
1381
  主题提供者,定义 CSS 自定义属性 token。
1306
1382
 
1307
- | 属性 | 类型 | 默认值 | 说明 |
1308
- | ------------ | --------------------------------- | ---------- | -------------------------- |
1309
- | `appearance` | `'light' \| 'dark' \| 'system'` | `'light'` | 配色方案 |
1310
- | `motion` | `'full' \| 'reduced' \| 'system'` | `'system'` | 当前嵌套主题范围的动效偏好 |
1311
- | `transition` | `boolean` | `false` | 配色变化时的圆形揭示动画 |
1383
+ | 属性 | 类型 | 默认值 | 说明 |
1384
+ | --------------------- | --------------------------------- | ---------- | ------------------------------ |
1385
+ | `appearance` | `'light' \| 'dark' \| 'system'` | `'light'` | 配色方案 |
1386
+ | `resolved-appearance` | `'light' \| 'dark'` | `'light'` | 解析后的配色方案;只读反射输出 |
1387
+ | `motion` | `'full' \| 'reduced' \| 'system'` | `'system'` | 当前嵌套主题范围的动效偏好 |
1312
1388
 
1313
1389
  **方法:** `getOverlayRoot()` — 返回该主题拥有的 theme-owned overlay root
1314
1390
 
@@ -1316,10 +1392,18 @@ SVG 线条绘制动画,基于 `stroke-dashoffset`。直接在原元素上动
1316
1392
 
1317
1393
  在其子树中定义基础、颜色、层级、阴影和动效 token。`motion="system"` 跟随 `prefers-reduced-motion`;使用 `motion="reduced"` 降低当前作用域动效,或在嵌套主题中使用 `motion="full"` 恢复默认 token。System 配色模式跟随 `prefers-color-scheme`。
1318
1394
 
1319
- 设置 `transition` 布尔属性后,使用 View Transitions API 播放配色变化;移除属性即关闭,框架动态切换时必须绑定 boolean property。根主题揭示整页;嵌套主题只揭示自己的 capture box。圆心优先取最近一次 pointerdown 坐标,否则回退主题盒或视口中心;深浅两个方向反向播放。不支持的浏览器、reduced-motion 作用域、时长为 0 以及同一 flight 内已有未完成请求都会立即落地新 appearance;当前版本对 `appearance="system"` 的 OS 深浅翻转不做动画。
1395
+ `resolved-appearance` 是只读的派生宿主属性(同时以 `resolvedAppearance` property 暴露):值恒为 `light` 或 `dark`;显式 `appearance` 直接透传,`appearance="system"` 从 `prefers-color-scheme` 解析,OS 深浅翻转时实时更新。未设置 `appearance` 的主题仍保持 inactive,但解析输出使用默认值 `light`。该 attribute 由组件独占写入并会恢复外部改动,因此消费端可以直接把选择器或 Tailwind custom variant 绑定到 `resolved-appearance`,无需再维护第二份主题状态。
1396
+
1397
+ 配色变化是否使用 View Transitions API 播放动画,由当前主题范围的 `motion` 档位决定:`full` 始终播放揭示,`reduced` 直接落地新 appearance、不播放揭示,`system` 跟随 `prefers-reduced-motion`。根主题揭示整页;嵌套主题只揭示自己的 capture box。圆心优先取最近一次 pointerdown 坐标,否则回退主题盒或视口中心;揭示方向跟随解析后的外观,包括 `appearance="system"` 从 `prefers-color-scheme` 解析出的结果,深浅两个方向反向播放。不支持的浏览器、reduced-motion 作用域、时长为 0 以及同一 flight 内已有未完成请求都会立即落地新 appearance;当前版本对 `appearance="system"` 的 OS 深浅翻转不做动画。
1398
+
1399
+ 揭示进行中,View Transitions 的 rendering-suppression 规则会强制所有 pointer hit-test 指向 document element,因此 CSS `pointer-events` 覆盖无法让底层页面继续交互。主题只在 `pointerdown` 或 wheel 时调用 `skipTransition()`;`pointermove` 与 `pointerup` 永远不会结束揭示,因此发起控件的收尾移动或过渡期间的普通移动都不会打断动画。触发 skip 的事件本身仍按规范以 document element 为目标,但失效窗口到此为止,不会持续满整个揭示时长。
1400
+
1401
+ **从已移除的 `transition` 属性迁移:** 删掉该属性,改用 `motion` 表达意图。原先带 `transition` 的主题在默认 `motion="system"` 下揭示行为不变;需要在系统偏好 reduce 时仍播放揭示,则加 `motion="full"`。原先不带 `transition` 的主题现在默认就会揭示,若要保持无动画直接切换,请显式设置 `motion="reduced"`。
1320
1402
 
1321
1403
  主题宿主使用 `display: contents` 且不绘制任何背景:组件库不在宿主页面画背景,嵌入方对主题子树背后的表面保留完全控制权。自定义属性仍可靠继承到 slotted 内容。
1322
1404
 
1405
+ **根节点 page 色同步:** 最外层 active theme 会把自己计算出的 `--wui-color-page` 以行内自定义属性的形式镜像到 `document.documentElement` 上。它补上的正是该 token 自己到不了的那一处——自定义属性的继承链到 documentElement 就断了,所以消费方写 `body { background: var(--wui-color-page) }` 只能给滚动区上色,橡皮筋回弹(macOS rubber-band)露出的画布区仍是 UA 背景色;有了这个镜像属性,回弹区也跟随主题。写入时机是主题连接时与 appearance 变化时(含 `appearance="system"` 跟随系统深浅翻转);写入的是该主题的计算值,因此消费者在 host 上覆盖 `--wui-color-page` 会被按当前值原样镜像。**非实时:** 组件不观察 host 自身的样式变化,因此运行中改该属性(改行内样式、切 class、host 级媒体查询)不会实时镜像到 root,要等下一个写入触发点——连接、`appearance` 变成另一个值、或系统深浅翻转;把 `appearance` 重新赋成当前值不算触发点,因为值没变时更新会被跳过。页面色需要独立于主题 appearance 变化时,消费者自己在该变量上持有它:在 `:root` 或 `body` 上用 `!important` 定义 `--wui-color-page`,重要声明在层级上压过本模块写在 documentElement 上的行内值;或者让页面色跟随 `appearance` 走。只有最外层主题会写入:嵌套主题保留自己的作用域,不改写 root 值。同步权按 connect 顺序归属,最外层断开时顺延给下一个已连接主题;最后一个主题断开时刻意保留最后值而不是移除,避免主题切换间隙闪回 UA 背景。组件库在 documentElement 上只写这一个自定义属性,不写其它内容。
1406
+
1323
1407
  **基础 token:**
1324
1408
 
1325
1409
  | 属性 | 默认值 | 说明 |
@@ -1373,13 +1457,14 @@ SVG 线条绘制动画,基于 `stroke-dashoffset`。直接在原元素上动
1373
1457
  | `--wui-color-surface-raised` | `#f2f2f7` | `#2c2c2e` | 抬升表面 |
1374
1458
  | `--wui-color-surface-control` | `#dfdfdf` | `#3a3a3c` | 中性可交互控件表面 |
1375
1459
  | `--wui-color-surface-track` | `#e5e5ea` | `#444446` | Slider/Switch 轨道表面 |
1376
- | `--wui-color-surface-menu` | `rgb(246 246 246 / 0.82)` | `rgb(30 30 32 / 0.92)` | Menu 和浮动面板表面 |
1460
+ | `--wui-color-surface-menu` | `rgb(254 254 254 / 0.76)` | `rgb(49 48 50 / 0.74)` | Menu 和浮动面板表面 |
1377
1461
  | `--wui-color-surface-glass` | `rgb(250 250 250 / 0.34)` | `rgb(44 44 46 / 0.42)` | 液态玻璃表面 |
1378
1462
  | `--wui-color-surface-glass-hover` | `color-mix(... text 6%, surface-glass)` | `color-mix(... text 6%, surface-glass)` | Glass 完整悬停背景 |
1379
1463
  | `--wui-color-surface-glass-active` | `color-mix(... text 15%, surface-glass)` | `color-mix(... text 15%, surface-glass)` | Glass 完整按下背景 |
1380
1464
  | `--wui-color-surface-overlay` | `rgb(246 246 246 / 0.82)` | `rgb(32 34 34 / 0.9)` | 半透明浮层表面 |
1381
- | `--wui-color-surface-segmented` | `#e5e5ea` | `#3a3a3c` | Segmented 轨道表面 |
1382
- | `--wui-color-surface-selected` | `#fff` | `#5c5c5e` | Segmented 选中滑块表面 |
1465
+ | `--wui-color-surface-sidebar` | `rgb(233 233 233 / 0.82)` | `rgb(44 44 46 / 0.8)` | 侧边栏面板表面 |
1466
+ | `--wui-color-surface-segmented` | `#e5e5ea` | `#3a3a3c` | Segmented 指示器表面 |
1467
+ | `--wui-color-surface-selected` | `#fff` | `#5c5c5e` | 选中表面 |
1383
1468
  | `--wui-color-text` | `#1b1b1b` | `#e9eaea` | 主要文本 |
1384
1469
  | `--wui-color-text-secondary` | `#6a6a6a` | `#a1a1a6` | 次要文本 |
1385
1470
  | `--wui-color-text-tertiary` | `color-mix(in srgb, var(--wui-color-text) 35%, transparent)` | `color-mix(in srgb, var(--wui-color-text) 42%, transparent)` | 三级文本和弱意图图标 |