@greypan/web-ui 7.0.0 → 8.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 (70) hide show
  1. package/README.CN.md +80 -13
  2. package/README.md +80 -13
  3. package/dist/components/autocomplete/index.d.ts +17 -5
  4. package/dist/components/autocomplete/index.js +136 -101
  5. package/dist/components/autocomplete/index.js.map +1 -1
  6. package/dist/components/autocomplete/style.js +1 -1
  7. package/dist/components/autocomplete/style.js.map +1 -1
  8. package/dist/components/avatar/index.js +4 -4
  9. package/dist/components/button/index.js +15 -15
  10. package/dist/components/button-group/index.js +5 -5
  11. package/dist/components/checkbox/index.js +11 -11
  12. package/dist/components/checkbox-group/index.js +8 -8
  13. package/dist/components/context-menu/index.js +4 -1
  14. package/dist/components/context-menu/index.js.map +1 -1
  15. package/dist/components/dialog/index.js +11 -8
  16. package/dist/components/dialog/index.js.map +1 -1
  17. package/dist/components/drawer/index.js +11 -8
  18. package/dist/components/drawer/index.js.map +1 -1
  19. package/dist/components/dropdown/index.js +4 -1
  20. package/dist/components/dropdown/index.js.map +1 -1
  21. package/dist/components/editable-text/index.d.ts +471 -0
  22. package/dist/components/editable-text/index.js +206 -0
  23. package/dist/components/editable-text/index.js.map +1 -0
  24. package/dist/components/editable-text/style.js +6 -0
  25. package/dist/components/editable-text/style.js.map +1 -0
  26. package/dist/components/image-preview/index.js +21 -18
  27. package/dist/components/image-preview/index.js.map +1 -1
  28. package/dist/components/index.d.ts +1 -0
  29. package/dist/components/index.js +39 -38
  30. package/dist/components/input/index.d.ts +2 -0
  31. package/dist/components/input/index.js +11 -5
  32. package/dist/components/input/index.js.map +1 -1
  33. package/dist/components/input-number/index.js +15 -15
  34. package/dist/components/layout/index.js +7 -7
  35. package/dist/components/layout/style.js +1 -1
  36. package/dist/components/layout/style.js.map +1 -1
  37. package/dist/components/popover/index.js +13 -10
  38. package/dist/components/popover/index.js.map +1 -1
  39. package/dist/components/radio/index.js +9 -9
  40. package/dist/components/radio-group/index.js +8 -8
  41. package/dist/components/segmented/index.d.ts +4 -1
  42. package/dist/components/segmented/index.js +42 -29
  43. package/dist/components/segmented/index.js.map +1 -1
  44. package/dist/components/segmented/style.js +1 -1
  45. package/dist/components/segmented/style.js.map +1 -1
  46. package/dist/components/segmented-trigger/style.js +1 -1
  47. package/dist/components/segmented-trigger/style.js.map +1 -1
  48. package/dist/components/select/index.js +35 -32
  49. package/dist/components/select/index.js.map +1 -1
  50. package/dist/components/slider/index.js +11 -11
  51. package/dist/components/switch/index.js +12 -12
  52. package/dist/components/textarea/index.js +5 -5
  53. package/dist/components/theme/index.d.ts +1 -3
  54. package/dist/components/theme/index.js +51 -54
  55. package/dist/components/theme/index.js.map +1 -1
  56. package/dist/components/theme/style.js +1 -1
  57. package/dist/components/theme/style.js.map +1 -1
  58. package/dist/components/toast/toast.js +13 -13
  59. package/dist/components/tooltip/index.js +12 -9
  60. package/dist/components/tooltip/index.js.map +1 -1
  61. package/dist/shared/option-portal/combobox-shell.js +47 -5
  62. package/dist/shared/option-portal/combobox-shell.js.map +1 -1
  63. package/dist/shared/option-portal/index.js +9 -9
  64. package/dist/shared/overlay/anchored-panel.js +2 -2
  65. package/dist/shared/overlay/anchored-panel.js.map +1 -1
  66. package/dist/shared/overlay/open-overlay.js +38 -22
  67. package/dist/shared/overlay/open-overlay.js.map +1 -1
  68. package/dist/shared/theme/root-sync.js +42 -0
  69. package/dist/shared/theme/root-sync.js.map +1 -0
  70. package/package.json +1 -1
package/README.CN.md CHANGED
@@ -245,6 +245,7 @@ dropdown、tooltip)不需要它。
245
245
  | --------------------- | --------------------------------------------------------- |
246
246
  | **表单控件** | [`<web-ui-input>`](#web-ui-input) |
247
247
  | | [`<web-ui-textarea>`](#web-ui-textarea) |
248
+ | | [`<web-ui-editable-text>`](#web-ui-editable-text) |
248
249
  | | [`<web-ui-input-number>`](#web-ui-input-number) |
249
250
  | | [`<web-ui-select>`](#web-ui-select) |
250
251
  | | [`<web-ui-autocomplete>`](#web-ui-autocomplete) |
@@ -307,6 +308,8 @@ dropdown、tooltip)不需要它。
307
308
 
308
309
  **事件:** `input`, `change`, `focus`, `blur`
309
310
 
311
+ **方法:** `focus()`, `blur()` —— 委托到内部原生 input(宿主自身不可聚焦)
312
+
310
313
  **插槽:** `prefix`, `default`, `suffix`
311
314
 
312
315
  **CSS 自定义属性:**
@@ -351,6 +354,41 @@ dropdown、tooltip)不需要它。
351
354
  | `--wui-textarea-width` | `200px` | 文本域宽度 |
352
355
  | `--wui-textarea-clear-color` | `var(--wui-color-text-tertiary)` | 清除按钮颜色 |
353
356
 
357
+ #### `<web-ui-editable-text>`
358
+
359
+ 行内纯文本编辑器:点击文字就地编辑,`Enter` 与 `blur` 提交,`Escape` 取消。文本层与编辑层共用一个盒,进入编辑态不会移动任何一个像素。
360
+
361
+ | 属性 | 类型 | 默认值 | 说明 |
362
+ | ------------- | --------- | ------- | --------------------------------------------------------------------------------------------------------- |
363
+ | `value` | `string` | `''` | 当前值;首次连接时捕获声明式初值作为 `form.reset()` 默认,连接后修改 attribute 或 property 均不更新该初值 |
364
+ | `placeholder` | `string` | `''` | 值为空时显示的占位文本 |
365
+ | `name` | `string` | `''` | 表单字段名 |
366
+ | `disabled` | `boolean` | `false` | 禁用状态;只影响行为,不做视觉置灰 |
367
+ | `aria-label` | `string` | — | 无障碍标签 |
368
+
369
+ **事件:** `input`(每次输入)、`change`(提交)、`cancel`(取消;与原生 `<dialog>` 的 `cancel` 同名,不冒泡、不组合,只在组件本身派发)。React 没有覆盖 `cancel` 的合成事件,须用 `addEventListener('cancel', ...)` 监听
370
+
371
+ **方法:** `focus()`, `blur()`, `select()`
372
+
373
+ 点击时光标落在点击处;键盘聚焦时落在文本末尾。`Enter` 与 `blur` 均提交草稿并恰好派发一次 `change`:`Enter` 不插入换行,并把焦点交还宿主;`blur` 不干预焦点,焦点留在用户移往的位置。只有 `Escape` 取消:值回到进入编辑时的状态,派发 `cancel` 而不派发 `change`,焦点交还宿主,且按键由编辑层消费,外层浮层(抽屉、菜单)不会因同一次按键关闭。`cancel` 不冒泡也不组合,只在组件本身派发:组件被投映在浮层 shadow 内时(如 drawer 标题),它不会触达浮层的原生 `cancel` 关闭管线,监听一律挂在组件本身。值里已有的换行仍按多行渲染,只是不能再靠输入 `Enter` 增加换行。空值继续显示 placeholder;编辑层始终按自身内容撑高,因此空草稿或纯空格草稿在宿主自身塌缩的场合(flex 项 `min-width: 0`、表格单元格)也仍有承接光标的位置。
374
+
375
+ `select()` 进入编辑态并全选内容;已在编辑态时只重新全选。`disabled` 时与 `focus()` 一样不产生效果。
376
+
377
+ 宿主是行内级盒子:未设宽度时随内容伸缩,折行后高度按行数增长。字体、颜色、文本对齐与空白处理全部继承外部上下文,因此编辑前它就是一段普通文字。
378
+
379
+ 光标颜色默认跟随共享语义 token `--wui-color-accent`;在宿主或任意祖先上设置该 token 即可整体改色。两层对长无空格串(连续字母数字)按同一组断行点折行:超出盒宽的长串在盒内断开,文字态不再溢出宿主盒。
380
+
381
+ **CSS 自定义属性:**
382
+
383
+ | 属性 | 默认值 | 说明 |
384
+ | ----------------------------------- | ---------- | ---------------------------------------------------------------- |
385
+ | `--wui-editable-text-white-space` | `pre-wrap` | 两层的空白处理方式 |
386
+ | `--wui-editable-text-overflow-wrap` | `anywhere` | 两层的断词处理方式;默认让长无空格串在盒内断开,与原生编辑层一致 |
387
+
388
+ 共用盒子带来两个约束:`line-height` 需不小于 `1`,更紧凑时原生编辑层内容会高出自身盒子,文字被顶偏 1px;`nowrap` 加固定宽度时,超出盒宽的文案在文字态溢出显示、在编辑态于盒内滚动。
389
+
390
+ **交互变更:** `blur` 恢复提交:`Enter` 与 `blur` 均提交草稿,只有 `Escape` 取消,值回到进入编辑时的状态。`Enter` 仍不插入换行。`cancel` 事件不再冒泡:此前冒泡的自定义 `cancel` 会经 slot 进入外层浮层 shadow 内的 `<dialog>`(`composed` 与否都会),被当成关闭请求一并关掉(issue #159);监听请挂在组件本身。
391
+
354
392
  #### `<web-ui-input-number>`
355
393
 
356
394
  数字输入框,支持步进按钮和键盘操作。
@@ -429,10 +467,26 @@ Portal 面板创建时会镜像 host 上解析后的这些变量;更新 host
429
467
 
430
468
  **事件:** `input`, `change`, `focus`, `blur`, `open-change` (`CustomEvent<{ open: boolean }>`)
431
469
 
432
- **插槽:** `default`(投影 `<web-ui-option>` 元素)、`empty`(替换无匹配空态;默认回退为“无匹配选项”)
470
+ **方法:** `focus()`, `blur()`
471
+
472
+ **插槽:** `default`(投影 `<web-ui-option>` 元素)、`trigger`(自定义触发器内容——替换默认输入框)、`empty`(替换无匹配空态;默认回退为“无匹配选项”)
433
473
 
434
474
  键入时按 label 过滤候选(`contains` 或 `prefix`,`none` 关闭过滤)。选择 option 时文本回填为该项 label,`selected-value` 暴露该项的 value;`change` 在选择提交时触发。支持 ArrowDown/ArrowUp/Enter/Escape 键盘导航。
435
475
 
476
+ **触发器:** 默认触发器是 shadow 内的 `web-ui-input`。把任意可编辑组件放进 `trigger` slot 即可替换它——包装 div 继续承载 combobox ARIA,并以 `data-custom-trigger` 标记当前使用自定义触发器,浮层始终以触发器元素为锚点。组件的 `focus()` / `blur()` 委托到当前生效的触发器:`web-ui-input` 与 `web-ui-textarea` 会把焦点落到内部原生控件;自定义触发器没有自己的 focus 重定向时,按宿主自身聚焦。
477
+
478
+ ```html
479
+ <web-ui-autocomplete placeholder="描述问题">
480
+ <web-ui-textarea slot="trigger" rows="3"></web-ui-textarea>
481
+ <web-ui-option value="bug" label="缺陷"></web-ui-option>
482
+ <web-ui-option value="feature" label="需求"></web-ui-option>
483
+ </web-ui-autocomplete>
484
+ ```
485
+
486
+ 自定义触发器与默认触发器共用同一份契约:字符串 `value` 承载文本、可聚焦,并派发组件委托监听的事件(`input`、`click`、`focus`、`blur`)。`web-ui-input` 与 `web-ui-textarea` 开箱即用;自定义元素只要暴露字符串 `value` property 即可接入。包装 div 不占 tab 位(`tabindex="-1"`):顺序焦点归触发器自身,自定义触发器必须可聚焦,键盘用户才能到达 combobox。
487
+
488
+ 多行触发器(可编辑元素为 `<textarea>`)保留 Enter 换行语义:Enter 不会选中高亮项,也不会提交 custom value,关闭面板用 Escape 或 blur。选择 option 仍会把该项 label 回写到触发器。单行自定义触发器保持默认的 Enter 语义。面板打开时 ArrowUp/ArrowDown 适用同一例外:方向键移动文本光标而不导航候选,该状态下键盘无法导航 option——用指针点击选择。面板关闭时 ArrowDown/ArrowUp 仍可打开面板。
489
+
436
490
  启用 `allow-custom-value` 后,无匹配且无活动 option 时,Enter 会把当前输入原文作为 custom value 提交并关闭面板;`change` 会触发,`selected-value` 保持为空。组件不会自动创建 option,也不会 trim 原文。命中禁用 option 的文本不会绕过禁用语义,也不会派生为已选 option。
437
491
 
438
492
  通过 `<div slot="empty">…</div>` 自定义静态、非交互的空态内容。Portal 渲染时该节点会迁入浮层,关闭后恢复到宿主。
@@ -544,23 +598,30 @@ Portal 面板创建时会镜像 host 上解析后的这些变量;更新 host
544
598
 
545
599
  分段控制——单选按钮组。
546
600
 
547
- | 属性 | 类型 | 默认值 | 说明 |
548
- | ---------- | --------- | ------- | ---------------- |
549
- | `value` | `string` | `''` | 当前选中值 |
550
- | `name` | `string` | `''` | 表单字段名 |
551
- | `disabled` | `boolean` | `false` | 禁用全部 trigger |
552
- | `required` | `boolean` | `false` | 必填校验 |
601
+ | 属性 | 类型 | 默认值 | 说明 |
602
+ | ---------- | ------------------- | ------- | ---------------------- |
603
+ | `value` | `string` | `''` | 当前选中值 |
604
+ | `name` | `string` | `''` | 表单字段名 |
605
+ | `disabled` | `boolean` | `false` | 禁用全部 trigger |
606
+ | `required` | `boolean` | `false` | 必填校验 |
607
+ | `variant` | `inset` \| `raised` | `inset` | 轨道与静止指示器的材质 |
553
608
 
554
609
  **事件:** `input`, `change`
555
610
 
556
611
  **插槽:** `default`(投影 `<web-ui-segmented-trigger>` 元素)
557
612
 
613
+ **变体:** `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`。
614
+
558
615
  与原生 `<form>` 集成(通过 `ElementInternals`)。
559
616
 
560
617
  根据 `value` 同步子 trigger 的 `checked` 状态。`disabled` 提供继承的有效禁用状态,不改写 trigger 自身的 `disabled` 属性。直接设 `value` 不派发事件。
561
618
 
562
619
  按住当前选中项横向拖拽可滑动指示器,松手吸附到最近选项(快速抛掷按速度切换)。触摸面声明 `touch-action: none`,拖拽激活期间阻止 `touchmove` 默认滚动,避免 iOS Safari 中断手势。
563
620
 
621
+ **按压态:** 轨道在所有状态下都是不透明实体面(`--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` 淡回实色灰底。
622
+
623
+ **文字色:** 选中与未选中项文字一律保持 `--wui-color-text-secondary`——`inset`/`raised` 两个变体一致,静止态与按压/拖拽态一致。选中态只由指示器承担,任何标签都不着 `--wui-color-accent`。
624
+
564
625
  #### `<web-ui-checkbox-group>`
565
626
 
566
627
  多选复选框组。
@@ -706,7 +767,7 @@ web-ui-radio-group {
706
767
 
707
768
  使用原生 `<dialog>`,`@cancel` 阻止默认关闭行为。除非存在 `no-escape-close`,否则 Escape 调用 `close()`;除非存在 `no-backdrop-close`,否则点击遮罩关闭。启用 `controlled` 后,两者都只派发关闭请求而不自关闭。
708
769
 
709
- > **Escape 归属**:Escape 由共享仲裁者统一判定,一次按键只关闭**最内层**的已打开浮层(popover、select、autocomplete、dropdown、context-menu、drawer、dialog 都参与)。例如在 drawer 内打开 select,第一次 Escape 只关 select,第二次才关 drawer。互不嵌套的并列浮层按打开顺序关闭最上层。`image-preview` 同样参与:它的原生 `<dialog>` 会登记进同一个仲裁者,Escape 按层级判定;组件的 `cancel` handler 只是拦掉原生的瞬时关闭,把 top layer 保留到退场过渡结束。
770
+ > **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
771
 
711
772
  **CSS 自定义属性:**
712
773
 
@@ -1241,6 +1302,8 @@ WebUiSpinner.hide() // 隐藏
1241
1302
 
1242
1303
  `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
1304
 
1305
+ 侧边栏卡片表面使用 `--wui-color-surface-sidebar`:浅色与共享的 `--wui-color-surface-overlay` 同值,深色比 `--wui-color-page` 浅一档、与 `--wui-color-surface` 同级。它独立成 token,是因为 dialog、drawer 和 toast 共用 `--wui-color-surface-overlay`,不随侧边栏一起抬升。
1306
+
1244
1307
  **CSS 自定义属性:**
1245
1308
 
1246
1309
  | 属性 | 默认值 | 说明 |
@@ -1308,7 +1371,6 @@ SVG 线条绘制动画,基于 `stroke-dashoffset`。直接在原元素上动
1308
1371
  | ------------ | --------------------------------- | ---------- | -------------------------- |
1309
1372
  | `appearance` | `'light' \| 'dark' \| 'system'` | `'light'` | 配色方案 |
1310
1373
  | `motion` | `'full' \| 'reduced' \| 'system'` | `'system'` | 当前嵌套主题范围的动效偏好 |
1311
- | `transition` | `boolean` | `false` | 配色变化时的圆形揭示动画 |
1312
1374
 
1313
1375
  **方法:** `getOverlayRoot()` — 返回该主题拥有的 theme-owned overlay root
1314
1376
 
@@ -1316,10 +1378,14 @@ SVG 线条绘制动画,基于 `stroke-dashoffset`。直接在原元素上动
1316
1378
 
1317
1379
  在其子树中定义基础、颜色、层级、阴影和动效 token。`motion="system"` 跟随 `prefers-reduced-motion`;使用 `motion="reduced"` 降低当前作用域动效,或在嵌套主题中使用 `motion="full"` 恢复默认 token。System 配色模式跟随 `prefers-color-scheme`。
1318
1380
 
1319
- 设置 `transition` 布尔属性后,使用 View Transitions API 播放配色变化;移除属性即关闭,框架动态切换时必须绑定 boolean property。根主题揭示整页;嵌套主题只揭示自己的 capture box。圆心优先取最近一次 pointerdown 坐标,否则回退主题盒或视口中心;深浅两个方向反向播放。不支持的浏览器、reduced-motion 作用域、时长为 0 以及同一 flight 内已有未完成请求都会立即落地新 appearance;当前版本对 `appearance="system"` 的 OS 深浅翻转不做动画。
1381
+ 配色变化是否使用 View Transitions API 播放动画,由当前主题范围的 `motion` 档位决定:`full` 始终播放揭示,`reduced` 直接落地新 appearance、不播放揭示,`system` 跟随 `prefers-reduced-motion`。根主题揭示整页;嵌套主题只揭示自己的 capture box。圆心优先取最近一次 pointerdown 坐标,否则回退主题盒或视口中心;深浅两个方向反向播放。不支持的浏览器、reduced-motion 作用域、时长为 0 以及同一 flight 内已有未完成请求都会立即落地新 appearance;当前版本对 `appearance="system"` 的 OS 深浅翻转不做动画。
1382
+
1383
+ **从已移除的 `transition` 属性迁移:** 删掉该属性,改用 `motion` 表达意图。原先带 `transition` 的主题在默认 `motion="system"` 下揭示行为不变;需要在系统偏好 reduce 时仍播放揭示,则加 `motion="full"`。原先不带 `transition` 的主题现在默认就会揭示,若要保持无动画直接切换,请显式设置 `motion="reduced"`。
1320
1384
 
1321
1385
  主题宿主使用 `display: contents` 且不绘制任何背景:组件库不在宿主页面画背景,嵌入方对主题子树背后的表面保留完全控制权。自定义属性仍可靠继承到 slotted 内容。
1322
1386
 
1387
+ **根节点 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 上只写这一个自定义属性,不写其它内容。
1388
+
1323
1389
  **基础 token:**
1324
1390
 
1325
1391
  | 属性 | 默认值 | 说明 |
@@ -1373,13 +1439,14 @@ SVG 线条绘制动画,基于 `stroke-dashoffset`。直接在原元素上动
1373
1439
  | `--wui-color-surface-raised` | `#f2f2f7` | `#2c2c2e` | 抬升表面 |
1374
1440
  | `--wui-color-surface-control` | `#dfdfdf` | `#3a3a3c` | 中性可交互控件表面 |
1375
1441
  | `--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 和浮动面板表面 |
1442
+ | `--wui-color-surface-menu` | `rgb(246 246 246 / 0.82)` | `rgb(44 44 46 / 0.78)` | Menu 和浮动面板表面 |
1377
1443
  | `--wui-color-surface-glass` | `rgb(250 250 250 / 0.34)` | `rgb(44 44 46 / 0.42)` | 液态玻璃表面 |
1378
1444
  | `--wui-color-surface-glass-hover` | `color-mix(... text 6%, surface-glass)` | `color-mix(... text 6%, surface-glass)` | Glass 完整悬停背景 |
1379
1445
  | `--wui-color-surface-glass-active` | `color-mix(... text 15%, surface-glass)` | `color-mix(... text 15%, surface-glass)` | Glass 完整按下背景 |
1380
1446
  | `--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 选中滑块表面 |
1447
+ | `--wui-color-surface-sidebar` | `rgb(246 246 246 / 0.82)` | `rgb(44 44 46 / 0.8)` | 侧边栏面板表面 |
1448
+ | `--wui-color-surface-segmented` | `#e5e5ea` | `#3a3a3c` | Segmented 指示器表面 |
1449
+ | `--wui-color-surface-selected` | `#fff` | `#5c5c5e` | 选中表面 |
1383
1450
  | `--wui-color-text` | `#1b1b1b` | `#e9eaea` | 主要文本 |
1384
1451
  | `--wui-color-text-secondary` | `#6a6a6a` | `#a1a1a6` | 次要文本 |
1385
1452
  | `--wui-color-text-tertiary` | `color-mix(in srgb, var(--wui-color-text) 35%, transparent)` | `color-mix(in srgb, var(--wui-color-text) 42%, transparent)` | 三级文本和弱意图图标 |
package/README.md CHANGED
@@ -275,6 +275,7 @@ All form controls participate in native `FormData`, constraint validation, `form
275
275
  | ---------------------- | --------------------------------------------------------- |
276
276
  | **Form Controls** | [`<web-ui-input>`](#web-ui-input) |
277
277
  | | [`<web-ui-textarea>`](#web-ui-textarea) |
278
+ | | [`<web-ui-editable-text>`](#web-ui-editable-text) |
278
279
  | | [`<web-ui-input-number>`](#web-ui-input-number) |
279
280
  | | [`<web-ui-select>`](#web-ui-select) |
280
281
  | | [`<web-ui-autocomplete>`](#web-ui-autocomplete) |
@@ -337,6 +338,8 @@ Text input with clearable, prefix/suffix slots.
337
338
 
338
339
  **Events:** `input`, `change`, `focus`, `blur`
339
340
 
341
+ **Methods:** `focus()`, `blur()` — delegate to the internal native input (the host itself is not focusable)
342
+
340
343
  **Slots:** `prefix`, `default`, `suffix`
341
344
 
342
345
  **CSS Custom Properties:**
@@ -381,6 +384,41 @@ Multi-line text input with auto-resize.
381
384
  | `--wui-textarea-width` | `200px` | Textarea width |
382
385
  | `--wui-textarea-clear-color` | `var(--wui-color-text-tertiary)` | Clear button color |
383
386
 
387
+ #### `<web-ui-editable-text>`
388
+
389
+ Inline plain-text editor: click the text to edit in place, `Enter` and `blur` commit, `Escape` cancels. The text layer and the editing layer share one box, so entering edit mode does not move a single pixel.
390
+
391
+ | Attribute | Type | Default | Description |
392
+ | ------------- | --------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
393
+ | `value` | `string` | `''` | Current value; the declarative value captured on first connection becomes the `form.reset()` default; later attribute or property changes do not update it |
394
+ | `placeholder` | `string` | `''` | Text shown while the value is empty |
395
+ | `name` | `string` | `''` | Form field name |
396
+ | `disabled` | `boolean` | `false` | Disabled state; behavior only, no visual dimming |
397
+ | `aria-label` | `string` | — | Accessible label |
398
+
399
+ **Events:** `input` (per keystroke), `change` (commit), `cancel` (cancel; shares its name with the native `<dialog>` `cancel`, so it neither bubbles nor crosses shadow boundaries and is dispatched on the component itself only). React has no synthetic event covering `cancel`: listen with `addEventListener('cancel', ...)`
400
+
401
+ **Methods:** `focus()`, `blur()`, `select()`
402
+
403
+ Clicking places the caret at the clicked offset; keyboard focus places it at the end of the text. `Enter` and `blur` both commit the draft and dispatch `change` exactly once; `Enter` does not insert a newline and returns focus to the host, while `blur` leaves the focus wherever the user moved it. `Escape` alone cancels: the value returns to what it was when editing started, `cancel` is dispatched with no `change`, focus returns to the host, and the key is consumed by the editing layer, so an outer overlay (drawer, menu) is not closed by the same press. The `cancel` event neither bubbles nor crosses shadow boundaries and is dispatched on the component itself only: when the component is projected inside an overlay's shadow root, such as a drawer title, it never reaches the overlay's native `cancel` close pipeline, so listen on the component itself. A value that already contains newlines still renders as several lines; only typing `Enter` no longer adds one. An empty value keeps showing the placeholder, and the editing layer always sizes itself to its own content, so an empty or whitespace-only draft still has room for the caret even where the host itself collapses (a flex item with `min-width: 0`, a table cell).
404
+
405
+ `select()` enters edit mode with the whole content selected, and re-selects it when already editing. While `disabled` it does nothing, matching `focus()`.
406
+
407
+ The host is an inline-level box: it sizes to its content unless a width is set, and grows with wrapped lines. Font, color, text alignment and white space are inherited from the surrounding context, so the component reads as ordinary text until it is edited.
408
+
409
+ The caret follows the shared `--wui-color-accent` semantic token by default; set that token on the host or any ancestor to recolor it. Both layers wrap a long unbroken string (letters and digits with no spaces) at the same points, so a token wider than the box breaks inside the box instead of overflowing it while idle.
410
+
411
+ **CSS Custom Properties:**
412
+
413
+ | Property | Default | Description |
414
+ | ----------------------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------- |
415
+ | `--wui-editable-text-white-space` | `pre-wrap` | White-space handling of both text layers |
416
+ | `--wui-editable-text-overflow-wrap` | `anywhere` | Overflow-wrap handling of both text layers; the default breaks a long unbroken string inside the box, matching the native editing layer |
417
+
418
+ The shared box comes with two constraints: keep `line-height` at `1` or above, because below that the native editing layer grows taller than its own box and shifts the text by a pixel; and with `nowrap` plus a fixed width, text wider than the box overflows while idle and scrolls inside the box while editing.
419
+
420
+ **Interaction change:** `blur` commits again: both `Enter` and `blur` commit the draft, and only `Escape` cancels, restoring the value from the moment editing started. `Enter` still does not insert a newline. The `cancel` event no longer bubbles: a bubbling custom `cancel` used to travel through the slot into the `<dialog>` inside an outer overlay's shadow root (`composed` made no difference) and was treated as a close request, closing the overlay along with the edit (issue #159). Listen on the component itself.
421
+
384
422
  #### `<web-ui-input-number>`
385
423
 
386
424
  Numeric input with step buttons and keyboard control.
@@ -461,10 +499,26 @@ Editable combobox with input filtering and single option selection.
461
499
 
462
500
  **Events:** `input`, `change`, `focus`, `blur`, `open-change` (`CustomEvent<{ open: boolean }>`)
463
501
 
464
- **Slots:** `default` (project `<web-ui-option>` elements), `empty` (replace the “no matches” state; falls back to “No matches”)
502
+ **Methods:** `focus()`, `blur()`
503
+
504
+ **Slots:** `default` (project `<web-ui-option>` elements), `trigger` (custom trigger content — replaces the default input), `empty` (replace the “no matches” state; falls back to “No matches”)
465
505
 
466
506
  Typing filters the option list by label (`contains` or `prefix`, or `none` to disable filtering). Selecting an option fills the input with its label and exposes the option's value via `selected-value`; `change` fires on selection commit. Supports ArrowDown/ArrowUp/Enter/Escape keyboard navigation.
467
507
 
508
+ **Trigger:** the default trigger is an internal `web-ui-input`. Put any editable component in the `trigger` slot to replace it — the wrapper keeps the combobox ARIA and marks itself with `data-custom-trigger`, and the dropdown stays anchored to the trigger element. Programmatic `focus()` / `blur()` delegate to the active trigger: `web-ui-input` and `web-ui-textarea` move focus to their native control, while a custom trigger without its own focus redirection is focused as the host itself.
509
+
510
+ ```html
511
+ <web-ui-autocomplete placeholder="Describe the issue">
512
+ <web-ui-textarea slot="trigger" rows="3"></web-ui-textarea>
513
+ <web-ui-option value="bug" label="Bug"></web-ui-option>
514
+ <web-ui-option value="feature" label="Feature"></web-ui-option>
515
+ </web-ui-autocomplete>
516
+ ```
517
+
518
+ The custom trigger exposes the same contract as the default one: a string `value` for the text, focusable, and the events the component delegates from it (`input`, `click`, `focus`, `blur`). `web-ui-input` and `web-ui-textarea` satisfy it as-is; a custom element is usable when it exposes a string `value` property. The wrapper div is not a tab stop (`tabindex="-1"`): sequential focus belongs to the trigger itself, so a custom trigger has to be focusable for keyboard users to reach the combobox.
519
+
520
+ Multiline triggers (a trigger whose editable element is a `<textarea>`) keep Enter for newlines: Enter never selects the highlighted option and never commits a custom value, so close the panel with Escape or blur. Selecting an option still writes its label back to the trigger. A single-line custom trigger keeps the default Enter behavior. ArrowUp/ArrowDown follow the same exception while the panel is open: they move the text caret instead of navigating options, so keyboard option navigation is unavailable in that state — select with a pointer click. With the panel closed, ArrowDown/ArrowUp still open it.
521
+
468
522
  When `allow-custom-value` is enabled, pressing Enter with no active option and no matching candidate commits the raw input as a custom value and closes the panel. `change` fires and `selected-value` remains empty. The component does not create an option automatically or trim the raw value. Text matching a disabled option cannot bypass the disabled state or derive as a selected option.
469
523
 
470
524
  Use `<div slot="empty">…</div>` for static, non-interactive empty-state content. Portal rendering moves this node into the floating panel and restores it to the host after close.
@@ -578,23 +632,30 @@ Uses `role="switch"` and `aria-checked`. Pointer events for pressed visual state
578
632
 
579
633
  Segmented control — single-select button group.
580
634
 
581
- | Attribute | Type | Default | Description |
582
- | ---------- | --------- | ------- | ---------------------- |
583
- | `value` | `string` | `''` | Selected trigger value |
584
- | `name` | `string` | `''` | Form field name |
585
- | `disabled` | `boolean` | `false` | Disables all triggers |
586
- | `required` | `boolean` | `false` | Required validation |
635
+ | Attribute | Type | Default | Description |
636
+ | ---------- | ------------------- | ------- | ------------------------------------ |
637
+ | `value` | `string` | `''` | Selected trigger value |
638
+ | `name` | `string` | `''` | Form field name |
639
+ | `disabled` | `boolean` | `false` | Disables all triggers |
640
+ | `required` | `boolean` | `false` | Required validation |
641
+ | `variant` | `inset` \| `raised` | `inset` | Track and resting indicator material |
587
642
 
588
643
  **Events:** `input`, `change`
589
644
 
590
645
  **Slots:** `default` (project `<web-ui-segmented-trigger>` elements)
591
646
 
647
+ **Variants:** `inset` (default, sunken) renders an opaque `--wui-color-surface` track with a constant 1px ring and drop shadow, and a solid `--wui-color-surface-segmented` resting indicator that reads as embedded in the track. In light appearance that token equals `--wui-color-page`, so the track sits flush with the page and the recess reads from the ring and shadow alone; in dark appearance `--wui-color-surface` is the one-step-above-page elevation, so the track keeps lifting off the page. `raised` renders the classic flat `--wui-color-surface-segmented` track (no ring, no shadow) with a solid `--wui-color-surface-selected` resting indicator carrying a soft shadow that floats above the track. In both variants the pressed/dragged indicator is transparent glass — backdrop blur, ring fading in over 80ms, 1.5x scale — easing back to the resting surface on release. Illegal values fall back to `inset`.
648
+
592
649
  Form-associated: integrates with native `<form>` via `ElementInternals`.
593
650
 
594
651
  Manages child trigger `checked` state based on `value`. `disabled` supplies inherited effective disabled state without changing a trigger's own `disabled` property. Setting `value` directly does not dispatch `input`/`change`.
595
652
 
596
653
  Press the selected segment and drag horizontally to slide the indicator; on release it snaps to the nearest option (a fast flick switches by velocity). Touch surfaces declare `touch-action: none` and suppress `touchmove` defaulting during an active drag, so iOS Safari does not interrupt the gesture.
597
654
 
655
+ **Pressed state:** the track is an opaque surface (`--wui-color-surface`, page-colored in light appearance) with no backdrop filter in every state — its 1px ring and drop shadow never change. The indicator is the layer that turns to glass. At rest it is a solid `--wui-color-surface-segmented` surface with all glass output off. While pressed or dragging it gains backdrop blur, the glass ring fading in over 80ms, and a 1.5x scale over a fully transparent background: the indicator samples its blur entirely from inside the track, and against the previous glass track a fully transparent indicator measured only +0.5 gray levels on a uniform dark backdrop while striped backdrops showed through the sample (std 1.89 gray levels inside the indicator). The opaque track makes that sample uniform (std 0), which is what gives the pressed thumb a perceptible body — no tint and no edge-decoration suppression are needed. The background never fades in — it lands on the first pressed frame — and on release the indicator eases back to the solid gray rest surface over `--wui-duration-press`.
656
+
657
+ **Label color:** every label stays `--wui-color-text-secondary` — checked or not, `inset` or `raised`, at rest and while a press or drag is active. Selection is carried by the indicator alone; no label takes the accent color.
658
+
598
659
  #### `<web-ui-checkbox-group>`
599
660
 
600
661
  Checkbox group managing multiple selection.
@@ -742,7 +803,7 @@ Modal dialog using native `<dialog>` with `showModal()`.
742
803
 
743
804
  Uses native `<dialog>` with `@cancel` prevention. Escape calls `close()` unless `no-escape-close` is present. Click on backdrop closes dialog unless `no-backdrop-close` is present. With `controlled`, both only emit the close request instead.
744
805
 
745
- > **Escape ownership:** Escape is arbitrated by a single shared owner, so one keypress closes only the **innermost** open overlay (popover, select, autocomplete, dropdown, context-menu, drawer and dialog all take part). With a select open inside a drawer, the first Escape closes the select and only the second closes the drawer. Sibling overlays that do not nest fall back to open order, closing the most recently opened one. `image-preview` takes part as well: its native `<dialog>` is registered with the same arbiter, so Escape is decided by layer order; the component's `cancel` handler only vetoes the native instant close, keeping the top layer until the exit transition finishes.
806
+ > **Escape ownership:** Escape is arbitrated by a single shared owner, so one keypress closes only the **innermost** open overlay (popover, select, autocomplete, dropdown, context-menu, drawer and dialog all take part). With a select open inside a drawer, the first Escape closes the select and only the second closes the drawer. Sibling overlays that do not nest fall back to open order, closing the most recently opened one. A panel that is still playing its exit transition stays on screen and still catches the Escape, but any overlay that is still open outranks it, so one keypress still closes exactly one layer. `image-preview` takes part as well: its native `<dialog>` is registered with the same arbiter, so Escape is decided by layer order; the component's `cancel` handler only vetoes the native instant close, keeping the top layer until the exit transition finishes.
746
807
 
747
808
  **CSS Custom Properties:**
748
809
 
@@ -1277,6 +1338,8 @@ At `640px` and below, the sidebar becomes a `web-ui-drawer` with its built-in gl
1277
1338
 
1278
1339
  `header-glow` adds a pointer-transparent decorative glow behind header-slot content and the mobile toggle. It is a Header background rather than a foreground layer, so slotted content remains above it. Override its color with `--wui-layout-header-glow-color` (default: `--wui-color-page`). The glow concentration and spread are controlled by the internal variable `--wui-layout-header-glow-height` (default: `150%`); increase for stronger coverage, decrease for a subtler effect. Layout layers are ordered as Header (`10`) < Auxiliary (`20`) < Banner (`30`) < Tabbar (`40`) < Sidebar (`50`).
1279
1340
 
1341
+ The sidebar card surface is `--wui-color-surface-sidebar`. In light mode it carries the same value as the shared `--wui-color-surface-overlay`; in dark mode it sits one notch above `--wui-color-page`, level with `--wui-color-surface`. It is a separate token because dialog, drawer and toast share `--wui-color-surface-overlay` and are not elevated along with the sidebar.
1342
+
1280
1343
  **CSS Custom Properties:**
1281
1344
 
1282
1345
  | Property | Default | Description |
@@ -1344,7 +1407,6 @@ Theme provider defining CSS custom property tokens.
1344
1407
  | ------------ | --------------------------------- | ---------- | --------------------------------------------- |
1345
1408
  | `appearance` | `'light' \| 'dark' \| 'system'` | `'light'` | Color scheme |
1346
1409
  | `motion` | `'full' \| 'reduced' \| 'system'` | `'system'` | Motion preference for this nested theme scope |
1347
- | `transition` | `boolean` | `false` | Circular reveal on appearance changes |
1348
1410
 
1349
1411
  **Methods:** `getOverlayRoot()` — returns this theme-owned overlay root
1350
1412
 
@@ -1352,10 +1414,14 @@ Theme provider defining CSS custom property tokens.
1352
1414
 
1353
1415
  Defines foundation, color, layer, shadow, and motion tokens for its subtree. `motion="system"` follows `prefers-reduced-motion`; use `motion="reduced"` to reduce animation in a scope or `motion="full"` in a nested theme to restore normal token values. System appearance follows `prefers-color-scheme`.
1354
1416
 
1355
- Set the `transition` boolean attribute to animate appearance changes with the View Transitions API. Removing the attribute disables the reveal; framework bindings must write the boolean property when toggling it dynamically. A root theme reveals the whole page; a nested theme reveals only its own capture box. The origin is the last pointer-down position when available, otherwise the theme box or viewport center. Dark-to-light and light-to-dark directions are reversed. Unsupported browsers, reduced-motion scopes, zero durations, and another request already in the same flight fall back to applying the new appearance immediately; `appearance="system"` does not animate OS light/dark changes in this version.
1417
+ Appearance changes are animated with the View Transitions API according to this scope's `motion`: `full` always reveals, `reduced` applies the new appearance immediately with no reveal, and `system` follows `prefers-reduced-motion`. A root theme reveals the whole page; a nested theme reveals only its own capture box. The origin is the last pointer-down position when available, otherwise the theme box or viewport center. Dark-to-light and light-to-dark directions are reversed. Unsupported browsers, reduced-motion scopes, zero durations, and another request already in the same flight fall back to applying the new appearance immediately; `appearance="system"` does not animate OS light/dark changes in this version.
1418
+
1419
+ **Migration from the removed `transition` attribute:** drop it and express the intent with `motion` instead. A theme that carried `transition` keeps the same reveal under the default `motion="system"`; add `motion="full"` to reveal even when the system prefers reduced motion. A theme that did not carry it now reveals by default, so pass `motion="reduced"` to keep appearance changes instant.
1356
1420
 
1357
1421
  The host uses `display: contents` and does not paint any background: the library never draws on the host page, so the embedding application keeps full control of the surface behind the themed subtree. Custom properties still inherit to slotted content reliably.
1358
1422
 
1423
+ **Root page color sync:** the outermost active theme also mirrors its computed `--wui-color-page` onto `document.documentElement` as an inline custom property. That closes the one place the token cannot reach on its own: custom properties stop inheriting at the document element, so a consumer rule such as `body { background: var(--wui-color-page) }` only colors the scrollable area, while the canvas behind an overscroll bounce (macOS rubber-banding) stays at the user-agent background. With the mirrored property the bounce area follows the theme too. The write happens when a theme connects and whenever its appearance changes, including `appearance="system"` following an OS light/dark flip; the value written is the theme's computed value, so an override of `--wui-color-page` on the host is mirrored as it stands. **Not live:** the component does not observe the host's own style changes, so a runtime change to that property on the host — an inline style edit, a class toggle, a host-level media query — reaches the root only at the next write trigger, which is a connect, an `appearance` change to a different value, or an OS light/dark flip; re-assigning `appearance` to the value it already has is not a trigger, because the update is skipped when nothing changed. For a page color that has to change independently of the theme's appearance, own the variable yourself on `:root` or `body` with `!important`, which outranks this inline write, or drive the color through `appearance`. Only the outermost theme writes: nested themes keep their own scope and never touch the root value. Ownership follows connect order and passes to the next connected theme when the outermost one disconnects; when the last theme disconnects the value is deliberately kept rather than removed, so a page that swaps themes does not flash back to the user-agent background in between. The library writes exactly this one custom property on the document element and nothing else there.
1424
+
1359
1425
  **Foundation tokens:**
1360
1426
 
1361
1427
  | Property | Default | Description |
@@ -1409,13 +1475,14 @@ The host uses `display: contents` and does not paint any background: the library
1409
1475
  | `--wui-color-surface-raised` | `#f2f2f7` | `#2c2c2e` | Raised surface |
1410
1476
  | `--wui-color-surface-control` | `#dfdfdf` | `#3a3a3c` | Neutral control surface |
1411
1477
  | `--wui-color-surface-track` | `#e5e5ea` | `#444446` | Slider and switch track surface |
1412
- | `--wui-color-surface-menu` | `rgb(246 246 246 / 0.82)` | `rgb(30 30 32 / 0.92)` | Menu and floating panel surface |
1478
+ | `--wui-color-surface-menu` | `rgb(246 246 246 / 0.82)` | `rgb(44 44 46 / 0.78)` | Menu and floating panel surface |
1413
1479
  | `--wui-color-surface-glass` | `rgb(250 250 250 / 0.34)` | `rgb(44 44 46 / 0.42)` | Liquid glass surface |
1414
1480
  | `--wui-color-surface-glass-hover` | `color-mix(... text 6%, surface-glass)` | `color-mix(... text 6%, surface-glass)` | Full glass hover background |
1415
1481
  | `--wui-color-surface-glass-active` | `color-mix(... text 15%, surface-glass)` | `color-mix(... text 15%, surface-glass)` | Full glass pressed background |
1416
1482
  | `--wui-color-surface-overlay` | `rgb(246 246 246 / 0.82)` | `rgb(32 34 34 / 0.9)` | Translucent overlay surface |
1417
- | `--wui-color-surface-segmented` | `#e5e5ea` | `#3a3a3c` | Segmented track surface |
1418
- | `--wui-color-surface-selected` | `#fff` | `#5c5c5e` | Segmented selected indicator |
1483
+ | `--wui-color-surface-sidebar` | `rgb(246 246 246 / 0.82)` | `rgb(44 44 46 / 0.8)` | Sidebar panel surface |
1484
+ | `--wui-color-surface-segmented` | `#e5e5ea` | `#3a3a3c` | Segmented indicator surface |
1485
+ | `--wui-color-surface-selected` | `#fff` | `#5c5c5e` | Selected surface |
1419
1486
  | `--wui-color-text` | `#1b1b1b` | `#e9eaea` | Primary text |
1420
1487
  | `--wui-color-text-secondary` | `#6a6a6a` | `#a1a1a6` | Secondary text |
1421
1488
  | `--wui-color-text-tertiary` | `color-mix(in srgb, var(--wui-color-text) 35%, transparent)` | `color-mix(in srgb, var(--wui-color-text) 42%, transparent)` | Tertiary text and quiet icons |
@@ -373,6 +373,12 @@ declare const WebUiAutocomplete_base: {
373
373
  * 可编辑输入框:`value` 即当前输入文本(表单值),键入时按 `filter` 模式过滤候选,
374
374
  * 选择 option 时文本回填为该项 label,`selected-value` 暴露该项的 value。
375
375
  * `allow-custom-value` 开启后,Enter 可把不匹配候选的原文作为 custom value 显式提交。
376
+ *
377
+ * 触发器跟随 select 的 wrapper div 模式:`slot[name="trigger"]` 可替换默认触发器
378
+ * (shadow 内的 `web-ui-input`);多行触发器(如 `web-ui-textarea`)保留 Enter 换行语义,
379
+ * 关闭面板用 Escape 或 blur。
380
+ * tab 位只属于触发器自身:默认触发器是 shadow 内 input,自定义触发器由消费者提供
381
+ * 可聚焦元素(README 契约);包装 div 恒为 tabindex="-1",不占顺序焦点,避免双 tab 位。
376
382
  */
377
383
  export declare class WebUiAutocomplete extends WebUiAutocomplete_base {
378
384
  static styles: import('lit').CSSResult[];
@@ -403,6 +409,7 @@ export declare class WebUiAutocomplete extends WebUiAutocomplete_base {
403
409
  private _isOpen;
404
410
  private _activeIndex;
405
411
  private _focused;
412
+ private _hasTriggerSlot;
406
413
  private static _nextInstanceId;
407
414
  private readonly _idPrefix;
408
415
  private readonly _a11yOptionIds;
@@ -412,14 +419,19 @@ export declare class WebUiAutocomplete extends WebUiAutocomplete_base {
412
419
  private _portalContent?;
413
420
  private readonly _scrollLock;
414
421
  private readonly _optionPortal;
422
+ private readonly _trigger;
415
423
  private readonly _overlay;
416
424
  private readonly _panel;
417
425
  get isOpen(): boolean;
418
426
  get open(): boolean;
427
+ focus(options?: FocusOptions): void;
428
+ blur(): void;
419
429
  private _onClickOutside;
420
430
  private _onFocusOut;
421
431
  private _handlePanelPointerDown;
422
432
  connectedCallback(): void;
433
+ private _onTriggerSlotChange;
434
+ private _syncTriggerSlot;
423
435
  attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
424
436
  disconnectedCallback(): void;
425
437
  firstUpdated(): void;
@@ -451,11 +463,11 @@ export declare class WebUiAutocomplete extends WebUiAutocomplete_base {
451
463
  private _findExactOption;
452
464
  private _hasExactOption;
453
465
  private _selectCustomValue;
454
- private _onInput;
455
- private _onInnerChange;
456
- private _onFocus;
457
- private _onInputClick;
458
- private _onBlur;
466
+ private _onTriggerInput;
467
+ private _onTriggerClick;
468
+ private _onTriggerFocusIn;
469
+ private _onTriggerFocusOut;
470
+ private _isSlottedFocus;
459
471
  private readonly _openController;
460
472
  private _open;
461
473
  private _close;