@greypan/web-ui 9.1.0 → 10.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.
- package/README.CN.md +67 -28
- package/README.md +69 -28
- package/dist/components/autocomplete/index.js.map +1 -1
- package/dist/components/avatar/index.js +5 -5
- package/dist/components/avatar/index.js.map +1 -1
- package/dist/components/back-top/index.js.map +1 -1
- package/dist/components/badge/index.js +5 -5
- package/dist/components/button/index.js +5 -5
- package/dist/components/button/index.js.map +1 -1
- package/dist/components/checkbox/index.js.map +1 -1
- package/dist/components/context-menu/index.js +65 -65
- package/dist/components/dialog/index.d.ts +29 -4
- package/dist/components/dialog/index.js +26 -13
- package/dist/components/dialog/index.js.map +1 -1
- package/dist/components/dialog/style.js +1 -1
- package/dist/components/dialog/style.js.map +1 -1
- package/dist/components/drawer/index.js.map +1 -1
- package/dist/components/dropdown-item/index.js.map +1 -1
- package/dist/components/editable-text/index.js +4 -4
- package/dist/components/empty/index.js.map +1 -1
- package/dist/components/image-preview/index.js.map +1 -1
- package/dist/components/input/index.js +5 -5
- package/dist/components/input/index.js.map +1 -1
- package/dist/components/input-number/index.js.map +1 -1
- package/dist/components/layout/index.js.map +1 -1
- package/dist/components/option/index.js.map +1 -1
- package/dist/components/select/index.js.map +1 -1
- package/dist/components/switch/index.js.map +1 -1
- package/dist/components/textarea/index.js +8 -8
- package/dist/components/textarea/index.js.map +1 -1
- package/dist/components/toast/toast.js.map +1 -1
- package/dist/scrollbars/index.js.map +1 -1
- package/dist/shared/overlay/overlay.js +33 -33
- package/dist/vite/index.d.ts +28 -0
- package/dist/vite/index.js +16 -0
- package/dist/vite/index.js.map +1 -0
- package/package.json +5 -1
- package/dist/node_modules/.pnpm/@floating-ui_core@1.8.0/node_modules/@floating-ui/core/dist/floating-ui.core.js +0 -246
- package/dist/node_modules/.pnpm/@floating-ui_core@1.8.0/node_modules/@floating-ui/core/dist/floating-ui.core.js.map +0 -1
- package/dist/node_modules/.pnpm/@floating-ui_dom@1.8.0/node_modules/@floating-ui/dom/dist/floating-ui.dom.js +0 -324
- package/dist/node_modules/.pnpm/@floating-ui_dom@1.8.0/node_modules/@floating-ui/dom/dist/floating-ui.dom.js.map +0 -1
- package/dist/node_modules/.pnpm/@floating-ui_utils@0.2.12/node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.js +0 -100
- package/dist/node_modules/.pnpm/@floating-ui_utils@0.2.12/node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.js.map +0 -1
- package/dist/node_modules/.pnpm/@floating-ui_utils@0.2.12/node_modules/@floating-ui/utils/dist/floating-ui.utils.js +0 -102
- package/dist/node_modules/.pnpm/@floating-ui_utils@0.2.12/node_modules/@floating-ui/utils/dist/floating-ui.utils.js.map +0 -1
package/README.CN.md
CHANGED
|
@@ -54,6 +54,35 @@ import { OverlayScrollbarsComponent } from 'overlayscrollbars-vue' // 或 overla
|
|
|
54
54
|
- OverlayScrollbars 只改变它被初始化到的那个元素,子元素保持原生滚动条。要覆盖文档滚动条就初始化
|
|
55
55
|
`document.body`——在那里 `window.scrollY` / `window.scrollTo` 保持原生语义。
|
|
56
56
|
|
|
57
|
+
## Vite
|
|
58
|
+
|
|
59
|
+
`@greypan/web-ui/vite` 导出 `webUiOptimizeDeps`,即需要并进应用 `optimizeDeps.include` 的 web-ui 运行时
|
|
60
|
+
依赖清单。
|
|
61
|
+
|
|
62
|
+
web-ui 的组件从自己的 dist 里带出 `lit`、`@lit/context`、`@floating-ui/dom`。这些包不在应用的
|
|
63
|
+
dependencies 里,Vite 从应用侧解析不到,写裸名会被静默跳过;扫描器直到请求模块时才发现它们,打印
|
|
64
|
+
`optimized dependencies changed. reloading`,那次模块加载随之失败,报 `Failed to fetch dynamically
|
|
65
|
+
imported module`。
|
|
66
|
+
|
|
67
|
+
扫描器跟不进组件图时才需要这份清单,典型场景是路由或页面由虚拟模块加载,比如 file-based routing
|
|
68
|
+
(`vue-router/vite`)或 `import.meta.glob`。启动入口静态 import 到每个组件模块的应用,组件图本来就在启动
|
|
69
|
+
扫描里,不必列出。
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
import { webUiOptimizeDeps } from '@greypan/web-ui/vite'
|
|
73
|
+
|
|
74
|
+
export default {
|
|
75
|
+
optimizeDeps: {
|
|
76
|
+
include: [...webUiOptimizeDeps]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- 条目用 Vite 的 `<pkg> > <dep>` 解析写法,按 web-ui 的依赖树解析,而不是应用的。
|
|
82
|
+
- 直接引用这个导出,不要抄条目,它跟随 web-ui 的运行时依赖。
|
|
83
|
+
- 它只影响 dev 期的依赖预打包,构建不受影响。
|
|
84
|
+
- 改过带清单的配置后清一次 `node_modules/.vite` 再启动。热缓存会把缺失的条目一直藏到 CI,而 CI 没有缓存。
|
|
85
|
+
|
|
57
86
|
## 框架集成
|
|
58
87
|
|
|
59
88
|
### 跨框架 API 约定
|
|
@@ -799,48 +828,58 @@ web-ui-radio-group {
|
|
|
799
828
|
|
|
800
829
|
模态对话框,使用原生 `<dialog>` 的 `showModal()`。
|
|
801
830
|
|
|
802
|
-
| 属性 | 类型 | 默认值 | 说明
|
|
803
|
-
| ------------------- | --------- | ------- |
|
|
804
|
-
| `open` | `boolean` | `false` | 对话框可见性
|
|
805
|
-
| `closable` | `boolean` | `false` | 显示内置关闭按钮
|
|
806
|
-
| `no-scroll-lock` | `boolean` | `false` | 打开时不锁定页面滚动
|
|
807
|
-
| `no-backdrop-close` | `boolean` | `false` | 禁止点击遮罩关闭
|
|
808
|
-
| `no-escape-close` | `boolean` | `false` | 禁止按 Escape 关闭
|
|
809
|
-
| `controlled` | `boolean` | `false` | Escape 与遮罩仅请求 `open=false`,由 Consumer 回写 `open`
|
|
831
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
832
|
+
| ------------------- | --------- | ------- | ----------------------------------------------------------------------------------------------- |
|
|
833
|
+
| `open` | `boolean` | `false` | 对话框可见性 |
|
|
834
|
+
| `closable` | `boolean` | `false` | 显示内置关闭按钮 |
|
|
835
|
+
| `no-scroll-lock` | `boolean` | `false` | 打开时不锁定页面滚动 |
|
|
836
|
+
| `no-backdrop-close` | `boolean` | `false` | 禁止点击遮罩关闭 |
|
|
837
|
+
| `no-escape-close` | `boolean` | `false` | 禁止按 Escape 关闭 |
|
|
838
|
+
| `controlled` | `boolean` | `false` | Escape 与遮罩仅请求 `open=false`,由 Consumer 回写 `open` |
|
|
839
|
+
| `heading` | `string` | `''` | 标题文字;`header` 插槽为空时的 fallback(`headless` 模式下忽略) |
|
|
840
|
+
| `headless` | `boolean` | `false` | 只保留浮层行为;`default` 插槽渲染为卡片内唯一的滚动层,不再渲染内置 header 带、内容区与 footer |
|
|
841
|
+
| `dialog-label` | `string` | `''` | 内部原生 dialog 的 accessible name;`headless` 模式下必填 |
|
|
810
842
|
|
|
811
843
|
**事件:** `open-change` (`CustomEvent<{ open: boolean }>`)。启用 `controlled` 后,Escape 与遮罩点击仅请求 `open=false`,Consumer 写入 `open=false` 前对话框保持打开。程序化 API(`showModal()`/`close()`/直接赋值 `open`)不受影响,始终直通且不派发事件。原生 dialog 关闭(如表单 `method="dialog"`)会被恢复为受控打开状态并重新发出同一请求。
|
|
812
844
|
|
|
813
|
-
**插槽:** `
|
|
845
|
+
**插槽:** `header`, `default`, `footer`。`headless` 下只渲染 `default` 插槽。
|
|
846
|
+
|
|
847
|
+
`header` 槽是卡片的 chrome 带:不参与滚动,自带水平 padding(`--wui-dialog-inline-padding`),内容不会贴到卡片内沿。`heading` 属性是它的 fallback——槽一旦有分配元素就整条接管,否则非空的 `heading` 渲染出 `.wui-dialog-heading`;两者都为空时整条带隐藏。
|
|
848
|
+
|
|
849
|
+
`default` 槽在两种模式下都承载正文。默认模式下它渲染进 `.desc`(卡片的内容区),与 `header` 带、`footer` 并列。`headless` 下整张卡片变成一个滚动层:`default` 槽渲染进 `.wui-dialog-content`,`header` 带、`.desc` 与 `footer` 一概不渲染,`heading` 一并失效。`headless` 与 `<web-ui-drawer>` 是同一套机制——显式布尔属性,不是「往某个插槽里塞东西」的开关。
|
|
850
|
+
|
|
851
|
+
**accessible name:** 设置了 `dialog-label` 时,内部原生 `<dialog>` 直接取它的 `aria-label`;否则在默认模式且 `header` 带可见时,取指向该带的 `aria-labelledby="wui-dialog-heading"`。`headless` 没有可指向的内置标题,所以**名字必须由 Consumer 提供**——`dialog-label`,或自绘的可关联标题元素。
|
|
814
852
|
|
|
815
853
|
**方法:** `showModal()`, `close()`
|
|
816
854
|
|
|
817
855
|
使用原生 `<dialog>`,`@cancel` 阻止默认关闭行为。除非存在 `no-escape-close`,否则 Escape 调用 `close()`;除非存在 `no-backdrop-close`,否则点击遮罩关闭。启用 `controlled` 后,两者都只派发关闭请求而不自关闭。
|
|
818
856
|
|
|
819
|
-
启用 `closable`
|
|
857
|
+
启用 `closable` 后,默认模式与 `headless` 模式都会渲染内置关闭按钮,且与 Escape、遮罩点击走同一条关闭路径(`controlled` 对它同样生效)。两种模式的位置已统一:按钮都绝对定位到卡片右上角,距卡片两沿的偏移由 `--wui-dialog-close-top` / `--wui-dialog-close-right` 控制(默认都是 `20px`;drawer 关闭按钮仍为 `16px`)。只有 `headless` 会把整卡换掉;默认模式下 `header` 带会为按钮留出避让,长内容不会钻到按钮下面。图标与 drawer 关闭按钮同为 `ooui:close`。不启用 `closable` 时什么都不渲染——既没有按钮,也没有多余的包裹层。
|
|
820
858
|
|
|
821
859
|
> **Escape 归属**:Escape 由共享仲裁者统一判定,一次按键只关闭**最内层**的已打开浮层(popover、select、autocomplete、dropdown、context-menu、drawer、dialog 都参与)。例如在 drawer 内打开 select,第一次 Escape 只关 select,第二次才关 drawer。互不嵌套的并列浮层按打开顺序关闭最上层。正在播退场过渡的面板仍在场上,也仍由它接住 Escape——但只要还有别的浮层开着,那一次按键仍然归该层,「一次按键关一层」不因此改变。`image-preview` 同样参与:它的原生 `<dialog>` 会登记进同一个仲裁者,Escape 按层级判定;组件的 `cancel` handler 只是拦掉原生的瞬时关闭,把 top layer 保留到退场过渡结束。
|
|
822
860
|
|
|
823
861
|
**CSS 自定义属性:**
|
|
824
862
|
|
|
825
|
-
| 属性 | 默认值 | 说明
|
|
826
|
-
| --------------------------------- | ------------------------------------------ |
|
|
827
|
-
| `--wui-dialog-width` | `360px` | 对话框宽度
|
|
828
|
-
| `--wui-dialog-max-height` | `90vh` / `min(90vh, 90dvh)`(dvh 引擎) | 内容区(`.desc`)的最大高度,不是整卡高度。两套 fallback:基础规则 `90vh`,`@supports (height: 100dvh)` 内 `min(90vh, 90dvh)`——不支持 dvh 的引擎只拿到 `90vh`。整卡换算见下方说明
|
|
829
|
-
| `--wui-dialog-close-top` | `20px` | `closable` 按钮距卡片顶部的偏移
|
|
830
|
-
| `--wui-dialog-close-right` | `20px` | `closable` 按钮距卡片右沿的偏移
|
|
831
|
-
| `--wui-dialog-overlay-bg` | `var(--wui-color-backdrop)` | 遮罩背景色
|
|
832
|
-
| `--wui-dialog-bg` | `var(--wui-color-surface-overlay)` | 玻璃卡片背景色,回退到 `rgb(248 248 248 / 0.92)`
|
|
833
|
-
| `--wui-dialog-padding` | `20px 24px 24px` |
|
|
834
|
-
| `--wui-dialog-
|
|
835
|
-
| `--wui-dialog-
|
|
836
|
-
| `--wui-dialog-desc-
|
|
837
|
-
| `--wui-dialog-
|
|
838
|
-
| `--wui-dialog-footer-
|
|
839
|
-
| `--wui-dialog-
|
|
840
|
-
|
|
841
|
-
|
|
863
|
+
| 属性 | 默认值 | 说明 |
|
|
864
|
+
| --------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
865
|
+
| `--wui-dialog-width` | `360px` | 对话框宽度 |
|
|
866
|
+
| `--wui-dialog-max-height` | `90vh` / `min(90vh, 90dvh)`(dvh 引擎) | 内容区(`.desc`)的最大高度,不是整卡高度。两套 fallback:基础规则 `90vh`,`@supports (height: 100dvh)` 内 `min(90vh, 90dvh)`——不支持 dvh 的引擎只拿到 `90vh`。整卡换算见下方说明 |
|
|
867
|
+
| `--wui-dialog-close-top` | `20px` | `closable` 按钮距卡片顶部的偏移 |
|
|
868
|
+
| `--wui-dialog-close-right` | `20px` | `closable` 按钮距卡片右沿的偏移 |
|
|
869
|
+
| `--wui-dialog-overlay-bg` | `var(--wui-color-backdrop)` | 遮罩背景色 |
|
|
870
|
+
| `--wui-dialog-bg` | `var(--wui-color-surface-overlay)` | 玻璃卡片背景色,回退到 `rgb(248 248 248 / 0.92)` |
|
|
871
|
+
| `--wui-dialog-padding` | `20px 24px 24px` | 卡片的 block 内边距。inline 分量不再作用于卡片:水平内边距移到了各分区(`.header` / `.desc` / `.wui-dialog-footer`),见 `--wui-dialog-inline-padding` |
|
|
872
|
+
| `--wui-dialog-inline-padding` | `24px` | `header` 带、内容区(`.desc`)与 footer 的水平内边距。卡片自身没有 inline padding,内容区的滚动口(其 border box)因此与卡片内沿重合——滚动条贴边——而内容仍保留这个内缩。默认取自 `--wui-space-6` |
|
|
873
|
+
| `--wui-dialog-title-gap` | `16px` | 标题下方间距 |
|
|
874
|
+
| `--wui-dialog-desc-gap` | `24px` | 正文内容下方间距 |
|
|
875
|
+
| `--wui-dialog-desc-focus-padding` | `6px` | 内容区**block 轴**预留的 focus-ring 余量。`.desc` 既是滚动容器,又因为有一条轴必须非 `visible` 而成了裁剪盒,所以紧贴其上下沿的控件,ring 会被 padding box 裁掉;这个余量覆盖 `5px` 的 ring(`offset: 2` + `width: 3`)。横向的 ring 余量改由 `--wui-dialog-inline-padding` 提供(24px ≫ 5px),所以本 token 只作用于 block 轴——原来的 inline 分量不再生效。成对负 margin 使 chrome 高度保持不变 |
|
|
876
|
+
| `--wui-dialog-footer-gap` | `10px` / horizontal `12px` | Footer 按钮间距 |
|
|
877
|
+
| `--wui-dialog-footer-justify` | `flex-end`(默认)/ `center`(horizontal) | Footer `justify-content`;horizontal 模式下覆盖为 `flex-end` 可右对齐 |
|
|
878
|
+
| `--wui-dialog-scale-enter` | `1.1` | 进场缩放起点:由 `1.1` 收缩到 `1`,退场反向 |
|
|
879
|
+
|
|
880
|
+
> **高度语义**:本 token 限制的是内容区,不是整卡。`header` 带与 footer 仍由内容撑开,超出上限的部分在 `.desc` 内部滚动;整卡本身只由字面量 `100vh` / `100dvh` 兜底,接住「视口太矮、chrome 加内容区上限也放不下」的情况。要给**整卡**定上限的宿主仍需自行量出 chrome(卡片 block 内边距 + header 带 + 间距 + footer)并从 token 里减掉——Interweave 前端三个 dialog 分别减 142、142、158。不再需要这个数的是「让内容区滚起来」:改动前 `.desc` 既没有上限也没有 `overflow`,唯一能滚的是宿主自己撑出来的内层盒子,宿主想让它滚就必须自己算这个高度。
|
|
842
881
|
>
|
|
843
|
-
> **`
|
|
882
|
+
> **`headless` 模式没有 `.desc`**,所以该 token 在这一档不生效。`headless` 下的 `default` 插槽被包进 `.wui-dialog-content`,滚动发生在这一层;卡片在内容装得下时仍贴合内容,装不下时由同一条字面量 `100vh` / `100dvh` 兜底。内置关闭按钮在内容滚动时保持 `--wui-dialog-close-top` / `--wui-dialog-close-right` 给出的位置不变。
|
|
844
883
|
|
|
845
884
|
#### `<web-ui-drawer>`
|
|
846
885
|
|
package/README.md
CHANGED
|
@@ -56,6 +56,37 @@ import { OverlayScrollbarsComponent } from 'overlayscrollbars-react' // or overl
|
|
|
56
56
|
the document scroller, initialize `document.body` — `window.scrollY` / `window.scrollTo` keep their native
|
|
57
57
|
semantics there.
|
|
58
58
|
|
|
59
|
+
## Vite
|
|
60
|
+
|
|
61
|
+
`@greypan/web-ui/vite` exports `webUiOptimizeDeps`, the list of web-ui runtime dependencies an app has to merge
|
|
62
|
+
into `optimizeDeps.include`.
|
|
63
|
+
|
|
64
|
+
web-ui components pull `lit`, `@lit/context` and `@floating-ui/dom` out of web-ui's own dist. Those packages are
|
|
65
|
+
not in the app's dependencies, so Vite cannot resolve them from the app, and a bare name is skipped in silence.
|
|
66
|
+
The scanner then finds them mid-request, prints `optimized dependencies changed. reloading`, and that module load
|
|
67
|
+
fails with `Failed to fetch dynamically imported module`.
|
|
68
|
+
|
|
69
|
+
The list is needed when the scanner cannot follow the component graph: routes or pages loaded through a virtual
|
|
70
|
+
module, such as file-based routing (`vue-router/vite`) or `import.meta.glob`. An app whose entry statically
|
|
71
|
+
imports every component module already pulls the graph into the startup scan and does not need it.
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
import { webUiOptimizeDeps } from '@greypan/web-ui/vite'
|
|
75
|
+
|
|
76
|
+
export default {
|
|
77
|
+
optimizeDeps: {
|
|
78
|
+
include: [...webUiOptimizeDeps]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
- The entries use Vite's `<pkg> > <dep>` resolution form, so they resolve through web-ui's dependency tree
|
|
84
|
+
rather than the app's.
|
|
85
|
+
- Reference the export instead of copying entries. It tracks web-ui's runtime dependencies.
|
|
86
|
+
- It only affects dev-time pre-bundling. Builds are unaffected.
|
|
87
|
+
- After changing a config that carries the list, remove `node_modules/.vite` once and restart. A warm cache
|
|
88
|
+
hides a missing entry until CI, which has no cache.
|
|
89
|
+
|
|
59
90
|
## Framework Setup
|
|
60
91
|
|
|
61
92
|
### Cross-framework API conventions
|
|
@@ -838,48 +869,58 @@ Set it on the group host. The token controls the long edge in both directions, s
|
|
|
838
869
|
|
|
839
870
|
Modal dialog using native `<dialog>` with `showModal()`.
|
|
840
871
|
|
|
841
|
-
| Attribute | Type | Default | Description
|
|
842
|
-
| ------------------- | --------- | ------- |
|
|
843
|
-
| `open` | `boolean` | `false` | Dialog visibility
|
|
844
|
-
| `closable` | `boolean` | `false` | Show the built-in close button
|
|
845
|
-
| `no-scroll-lock` | `boolean` | `false` | Do not lock body scroll when open
|
|
846
|
-
| `no-backdrop-close` | `boolean` | `false` | Do not close on backdrop click
|
|
847
|
-
| `no-escape-close` | `boolean` | `false` | Do not close when Escape is pressed
|
|
848
|
-
| `controlled` | `boolean` | `false` | Escape and backdrop only request `open=false`; the consumer must update `open`
|
|
872
|
+
| Attribute | Type | Default | Description |
|
|
873
|
+
| ------------------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
874
|
+
| `open` | `boolean` | `false` | Dialog visibility |
|
|
875
|
+
| `closable` | `boolean` | `false` | Show the built-in close button |
|
|
876
|
+
| `no-scroll-lock` | `boolean` | `false` | Do not lock body scroll when open |
|
|
877
|
+
| `no-backdrop-close` | `boolean` | `false` | Do not close on backdrop click |
|
|
878
|
+
| `no-escape-close` | `boolean` | `false` | Do not close when Escape is pressed |
|
|
879
|
+
| `controlled` | `boolean` | `false` | Escape and backdrop only request `open=false`; the consumer must update `open` |
|
|
880
|
+
| `heading` | `string` | `''` | Header text; fallback when the `header` slot is empty (ignored in `headless` mode) |
|
|
881
|
+
| `headless` | `boolean` | `false` | Keep only overlay behavior; render the `default` slot as the card's single scroll layer, without the built-in header band, content area or footer |
|
|
882
|
+
| `dialog-label` | `string` | `''` | Accessible name for the internal native dialog; required in `headless` mode |
|
|
849
883
|
|
|
850
884
|
**Events:** `open-change` (`CustomEvent<{ open: boolean }>`). With `controlled`, Escape and backdrop clicks only request `open=false` and the dialog stays open until the consumer writes `open=false`. Programmatic APIs (`showModal()`, `close()`, assigning `open`) stay direct and never emit. Native dialog closure (e.g. form `method="dialog"`) is restored to the controlled open state and re-emits the request.
|
|
851
885
|
|
|
852
|
-
**Slots:** `
|
|
886
|
+
**Slots:** `header`, `default`, `footer`. With `headless`, only the `default` slot is rendered.
|
|
887
|
+
|
|
888
|
+
The `header` slot is the card's chrome band: it does not scroll and it carries its own horizontal padding (`--wui-dialog-inline-padding`), so its content never touches the card edge. The `heading` attribute is its fallback — the slot wins whenever it has assigned elements, otherwise a non-empty `heading` renders a `.wui-dialog-heading`; when neither is present the band is hidden.
|
|
889
|
+
|
|
890
|
+
The `default` slot carries the body in both modes. By default it renders inside `.desc` — the card's content area — alongside the `header` band and the `footer`. With `headless` the whole card becomes one scroll layer: the `default` slot renders into `.wui-dialog-content`, and the `header` band, `.desc` and `footer` are not rendered at all, so `heading` is ignored. `headless` is the same mechanism as `<web-ui-drawer>`: an explicit boolean attribute, not a slot you have to fill to switch modes.
|
|
891
|
+
|
|
892
|
+
**Accessible name:** the internal native `<dialog>` takes `aria-label` from `dialog-label` when that is set; otherwise, in the default mode with a visible `header` band, it takes `aria-labelledby="wui-dialog-heading"` pointing at that band. `headless` renders no built-in title to point at, so **the consumer must supply the name** — `dialog-label`, or their own labelled element.
|
|
853
893
|
|
|
854
894
|
**Methods:** `showModal()`, `close()`
|
|
855
895
|
|
|
856
896
|
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.
|
|
857
897
|
|
|
858
|
-
With `closable`, a built-in close button is rendered in both
|
|
898
|
+
With `closable`, a built-in close button is rendered in both the default and `headless` modes, and it follows the same close path as Escape and backdrop clicks (so `controlled` applies to it identically). Both modes place it the same way: it is absolutely positioned at the card's top-right corner, offset from the card edges by `--wui-dialog-close-top` / `--wui-dialog-close-right` (both default to `20px`; the drawer close button remains at `16px`). Only `headless` replaces the whole card; in the default mode the `header` band reserves room for the button so long header content never runs under it. It uses the same `ooui:close` icon as the drawer close button. Omitting `closable` renders nothing at all — no button and no extra wrapper.
|
|
859
899
|
|
|
860
900
|
> **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.
|
|
861
901
|
|
|
862
902
|
**CSS Custom Properties:**
|
|
863
903
|
|
|
864
|
-
| Property | Default | Description
|
|
865
|
-
| --------------------------------- | -------------------------------------------- |
|
|
866
|
-
| `--wui-dialog-width` | `360px` | Dialog width
|
|
867
|
-
| `--wui-dialog-max-height` | `90vh` / `min(90vh, 90dvh)` (dvh engines) | Max height of the content area (`.desc`), not of the card. Two fallback tiers: `90vh` on the base rule, `min(90vh, 90dvh)` inside `@supports (height: 100dvh)` — an engine without `dvh` support only ever gets `90vh`. See the note below for the chrome conversion
|
|
868
|
-
| `--wui-dialog-close-top` | `20px` | `closable` button offset from the card top
|
|
869
|
-
| `--wui-dialog-close-right` | `20px` | `closable` button offset from the card right edge
|
|
870
|
-
| `--wui-dialog-overlay-bg` | `var(--wui-color-backdrop)` | Backdrop background
|
|
871
|
-
| `--wui-dialog-bg` | `var(--wui-color-surface-overlay)` | Glass card background, falls back to `rgb(248 248 248 / 0.92)`
|
|
872
|
-
| `--wui-dialog-padding` | `20px 24px 24px` |
|
|
873
|
-
| `--wui-dialog-
|
|
874
|
-
| `--wui-dialog-
|
|
875
|
-
| `--wui-dialog-desc-
|
|
876
|
-
| `--wui-dialog-
|
|
877
|
-
| `--wui-dialog-footer-
|
|
878
|
-
| `--wui-dialog-
|
|
879
|
-
|
|
880
|
-
|
|
904
|
+
| Property | Default | Description |
|
|
905
|
+
| --------------------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
906
|
+
| `--wui-dialog-width` | `360px` | Dialog width |
|
|
907
|
+
| `--wui-dialog-max-height` | `90vh` / `min(90vh, 90dvh)` (dvh engines) | Max height of the content area (`.desc`), not of the card. Two fallback tiers: `90vh` on the base rule, `min(90vh, 90dvh)` inside `@supports (height: 100dvh)` — an engine without `dvh` support only ever gets `90vh`. See the note below for the chrome conversion |
|
|
908
|
+
| `--wui-dialog-close-top` | `20px` | `closable` button offset from the card top |
|
|
909
|
+
| `--wui-dialog-close-right` | `20px` | `closable` button offset from the card right edge |
|
|
910
|
+
| `--wui-dialog-overlay-bg` | `var(--wui-color-backdrop)` | Backdrop background |
|
|
911
|
+
| `--wui-dialog-bg` | `var(--wui-color-surface-overlay)` | Glass card background, falls back to `rgb(248 248 248 / 0.92)` |
|
|
912
|
+
| `--wui-dialog-padding` | `20px 24px 24px` | Block padding of the card. Its inline component no longer applies to the card: the horizontal padding moved onto the regions, see `--wui-dialog-inline-padding` |
|
|
913
|
+
| `--wui-dialog-inline-padding` | `24px` | Horizontal padding of the `header` band, the content area (`.desc`) and the footer. The card itself has no inline padding, so the content area's scroll port (its border box) coincides with the card edge — the scrollbar sits flush — while the content keeps this inset. The default comes from `--wui-space-6` |
|
|
914
|
+
| `--wui-dialog-title-gap` | `16px` | Spacing below the title |
|
|
915
|
+
| `--wui-dialog-desc-gap` | `24px` | Spacing below the body content |
|
|
916
|
+
| `--wui-dialog-desc-focus-padding` | `6px` | Focus-ring margin reserved on the **block axis** of the content area. `.desc` is a scroll container and, because one axis must be non-`visible`, a clip box as well, so a control flush against its top or bottom edge would have its ring clipped by the padding box; the margin covers the `5px` ring (`offset: 2` + `width: 3`). The horizontal ring room now comes from `--wui-dialog-inline-padding` (24px ≫ 5px), so this token only affects the block axis — its former inline component no longer applies. The paired negative top margin keeps the chrome height unchanged |
|
|
917
|
+
| `--wui-dialog-footer-gap` | `10px` / `12px` (horizontal) | Spacing between footer buttons |
|
|
918
|
+
| `--wui-dialog-footer-justify` | `flex-end` (default) / `center` (horizontal) | Footer `justify-content`; override to `flex-end` for right-aligned horizontal buttons |
|
|
919
|
+
| `--wui-dialog-scale-enter` | `1.1` | Enter scale start: the panel shrinks in from `1.1` to `1`, and exit reverses it |
|
|
920
|
+
|
|
921
|
+
> **Height semantics:** the token caps the content area, not the card. The `header` band and the footer stay sized by their content, and anything past the cap scrolls inside `.desc`; the card itself is bounded only by a literal `100vh` / `100dvh` backstop, which catches the case where the viewport is too short to fit chrome plus the cap. A host that wants to bound the **whole card** must still measure its own chrome (card padding + title row + gaps + footer) and subtract it from the token — the three dialogs in the Interweave frontend subtract 142, 142 and 158 respectively. What no longer needs that number is making the content area scroll: before, `.desc` had neither a cap nor `overflow`, so the only element that could scroll was a host-sized inner box, and a host that wanted overflow to scroll had to compute that height itself.
|
|
881
922
|
>
|
|
882
|
-
> **`
|
|
923
|
+
> **`headless` mode has no `.desc`**, so the token does not apply there. The `headless` default slot is wrapped in `.wui-dialog-content`, which is the scroll area, and the card stays content-sized until the same literal `100vh` / `100dvh` backstop kicks in; the built-in close button keeps its `--wui-dialog-close-top` / `--wui-dialog-close-right` offset while that content scrolls.
|
|
883
924
|
|
|
884
925
|
#### `<web-ui-drawer>`
|
|
885
926
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/autocomplete/index.ts"],"sourcesContent":["import { html, LitElement, nothing, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\n\nimport '@/components/input'\nimport glass from '@/assets/glass.css?inline'\nimport overlayMotion from '@/assets/overlay-motion.css?inline'\nimport type { WebUiOption } from '@/components/option'\nimport { FormAssociated, defineFormAssociation, FormAssociationController } from '@/shared/form-association'\nimport { normalizeLiteral } from '@/shared/normalize'\nimport { dispatchOpenChangeEvent } from '@/shared/open-state'\nimport {\n createComboboxOpenController,\n createOptionListenerBinding,\n defineComboboxTrigger,\n defineOptionPortal,\n handleComboboxFocusOut,\n nextWrappingIndex\n} from '@/shared/option-portal'\nimport { defineAnchoredPanel } from '@/shared/overlay/anchored-panel'\nimport { defineOpenOverlay } from '@/shared/overlay/open-overlay'\nimport {\n applyOverlayVariables,\n defineOverlayPortal,\n type OverlayContainer,\n type OverlayPortal\n} from '@/shared/overlay/portal'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\nconst FILTER_MODES = ['none', 'prefix', 'contains'] as const\ntype FilterMode = (typeof FILTER_MODES)[number]\n\n// 自定义 trigger 与 select 同判据:必须留在宿主,不随 options 迁入 portal 面板\nfunction isTriggerSlotNode(node: Node): node is Element {\n return node instanceof Element && node.slot === 'trigger'\n}\n\n// 已移除的 empty slot 仍可能留在既有消费者 light DOM;不得把它作为普通内容迁入 portal。\nfunction isEmptySlotNode(node: Node): node is Element {\n return node instanceof Element && node.slot === 'empty'\n}\n\n/**\n * `web-ui-autocomplete`:可输入并过滤候选的单值选择器。\n *\n * 与 `web-ui-select` 共用 `<web-ui-option>` 子项注册协议与浮层能力,但触发区是\n * 可编辑输入框:`value` 即当前输入文本(表单值),键入时按 `filter` 模式过滤候选,\n * 选择 option 时文本回填为该项 label,`selected-value` 暴露该项的 value。\n * `allow-custom-value` 开启后,Enter 可把不匹配候选的原文作为 custom value 显式提交。\n *\n * 触发器跟随 select 的 wrapper div 模式:`slot[name=\"trigger\"]` 可替换默认触发器\n * (shadow 内的 `web-ui-input`);多行触发器(如 `web-ui-textarea`)保留 Enter 换行语义,\n * 关闭面板用 Escape 或 blur。\n * tab 位只属于触发器自身:默认触发器是 shadow 内 input,自定义触发器由消费者提供\n * 可聚焦元素(README 契约);包装 div 恒为 tabindex=\"-1\",不占顺序焦点,避免双 tab 位。\n */\n@customElement('web-ui-autocomplete')\nexport class WebUiAutocomplete extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(overlayMotion), unsafeCSS(style)]\n\n @property({ type: String, reflect: true }) placeholder = ''\n @property({ type: Boolean, reflect: true }) borderless = false\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) readonly = false\n @property({ type: Boolean, reflect: true }) required = false\n @property({ type: Boolean, reflect: true }) portal = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' })\n noScrollLock = false\n @property({ type: Boolean, reflect: true, attribute: 'allow-custom-value' })\n allowCustomValue = false\n @property({ attribute: false }) overlayContainer?: OverlayContainer\n @property({ type: String, reflect: true }) name = ''\n @property({ type: String, attribute: 'aria-label' }) override ariaLabel: string | null = null\n @property({ type: String, attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined\n\n private _filter: FilterMode = 'contains'\n\n @property({ type: String, reflect: true })\n get filter(): FilterMode {\n return this._filter\n }\n set filter(v: string) {\n const old = this._filter\n this._filter = normalizeLiteral(v, FILTER_MODES, 'contains')\n this.requestUpdate('filter', old)\n }\n\n // value 不反射 attribute,attribute 只参与首次声明式初始化。\n @state() private _value = ''\n\n get value(): string {\n return this._value\n }\n set value(v: string) {\n const old = this._value\n this._value = v\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n // selected-value:文本精确匹配 option label 时派生出的 option value(只读反射)。\n @state() private _selectedValue = ''\n\n get selectedValue(): string {\n return this._selectedValue\n }\n\n private _setSelectedValue(value: string) {\n const old = this._selectedValue\n if (old === value && this.getAttribute('selected-value') === value) return\n\n this._selectedValue = value\n this.setAttribute('selected-value', value)\n if (old !== value) this.requestUpdate('selectedValue', old)\n }\n\n private readonly _formAssociation = defineFormAssociation<string>({\n host: this,\n initialize: () => {\n // 同步 HTML 属性中预设的 value,补全访问器模式不处理 attribute 反射。\n const attrValue = this.getAttribute('value')\n if (attrValue !== null) this._value = attrValue\n },\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => this._value,\n getFormState: () => this._value,\n restoreState: state => {\n if (typeof state === 'string') this.value = state\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n @state() private _isOpen = false\n @state() private _activeIndex = -1\n @state() private _focused = false\n @state() private _hasTriggerSlot = false\n\n private static _nextInstanceId = 0\n private readonly _idPrefix = `web-ui-autocomplete-${++WebUiAutocomplete._nextInstanceId}`\n private readonly _a11yOptionIds = new WeakMap<WebUiOption, string>()\n private _nextA11yOptionId = 0\n private _options: WebUiOption[] = []\n private _portal?: OverlayPortal\n private _portalContent?: HTMLElement\n private readonly _scrollLock = defineScrollLockLease().make()\n\n // option 注册表 / portal 同步 / 微任务调度收敛到 shared 模块(select 同款)\n private readonly _optionPortal = defineOptionPortal().make({\n element: this,\n idPrefix: `${this._idPrefix}-option`,\n getPortal: () => this._portal,\n getPortalContent: () => this._portalContent,\n isOpen: () => this.portal && this._isOpen,\n // 默认 slot 内容随面板迁移;trigger 与已移除的 empty slot 留在宿主,不进面板。\n getMigratableNodes: () =>\n Array.from(this.childNodes).filter(node => !isTriggerSlotNode(node) && !isEmptySlotNode(node)),\n hasUpdated: () => this.hasUpdated,\n requestUpdate: () => this.requestUpdate(),\n bindOption: option => this._bindOption(option),\n unbindOption: option => this._unbindOption(option)\n })\n /*\n * 触发器委托层:默认触发器(shadow 内 web-ui-input)与 slot[name=\"trigger\"] 自定义\n * 触发器对组件暴露同一形状,监听统一挂在 trigger 包装 div 上,两条路径不特判。\n */\n private readonly _trigger = defineComboboxTrigger().make({\n getWrapper: () => this.shadowRoot?.querySelector<HTMLElement>('.autocomplete-trigger') ?? null,\n getCustomTrigger: () => {\n const assigned = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"trigger\"]')?.assignedElements()[0]\n return assigned instanceof HTMLElement ? assigned : null\n },\n getFallbackTrigger: () => this.shadowRoot?.querySelector<HTMLElement>('.autocomplete-input') ?? null,\n onInput: value => this._onTriggerInput(value),\n onClick: () => this._onTriggerClick(),\n onFocusIn: event => this._onTriggerFocusIn(event),\n onFocusOut: event => this._onTriggerFocusOut(event)\n })\n /*\n * 开启态浮层承载「我开着吗」(issue #120 Block 1):宿主级 keydown 在 portal 模式下\n * 收不到面板内的 Escape,改由唯一仲裁者在 document 捕获阶段判定最内层。\n * 宿主接口除 requestClose 外只再提供一个 isConnected(仅供惰性回收读取):开启状态是\n * 声明而非询问。\n */\n private readonly _overlay = defineOpenOverlay().make({\n requestClose: () => this._close(),\n isConnected: () => this.isConnected\n })\n private readonly _panel = defineAnchoredPanel().make({\n // 锚点取当前生效的触发器 host:默认触发器或自定义 trigger slot 内容\n getAnchor: () =>\n this._trigger.getTrigger() ?? this.shadowRoot?.querySelector<HTMLElement>('.autocomplete-trigger') ?? null,\n getLocalPanel: () => this.shadowRoot?.querySelector<HTMLElement>('.autocomplete-overlay') ?? null,\n getPositioning: () => ({\n placement: 'bottom-start',\n offset: 4,\n minAnchorWidth: true,\n strategy: this.portal ? 'fixed' : 'absolute'\n }),\n isPortal: () => this.portal,\n createPortal: () => this._createPortal(),\n openOverlay: this._overlay\n })\n\n get isOpen(): boolean {\n return this._isOpen\n }\n\n get open(): boolean {\n return this._isOpen\n }\n\n /*\n * 公共焦点委托:聚焦/失焦当前生效的触发器(默认 web-ui-input 或自定义触发器)。\n * 组件自带 focus 重定向时落到内部原生控件;自定义触发器没有该能力时,\n * 原生 focus() 聚焦 host 自身。无触发器(尚未首渲或已断开)时静默返回。\n */\n override focus(options?: FocusOptions): void {\n this._trigger.focusTrigger(options)\n }\n\n override blur(): void {\n this._trigger.blurTrigger()\n }\n\n private _onClickOutside = (e: MouseEvent) => {\n if (!this._isOpen) return\n\n // 在监听器内部判定:composedPath() 在派发结束后会被清空。\n const isInside = e.composedPath().includes(this) || !!this._panel.getHandle()?.containsEvent(e)\n if (!isInside) this._close()\n }\n\n private _onFocusOut = () => {\n handleComboboxFocusOut(\n this,\n () => this._panel.getHandle(),\n () => this._isOpen,\n () => this._close()\n )\n }\n\n private _handlePanelPointerDown = (e: PointerEvent) => {\n // 非 option 区域不可聚焦;阻止 pointerdown 的默认聚焦行为,保持 combobox 焦点,\n // 使点击 empty state、滚动区和面板 padding 不会被 focusout 误判为外部交互。\n e.preventDefault()\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._optionPortal.bindHost()\n // 宿主可能被上层浮层 portal 迁移(断开重连):shadow 内的包装 div 仍在,重新绑定委托监听\n this._trigger.bind()\n this.addEventListener('keydown', this._onKeydown)\n this.addEventListener('focusout', this._onFocusOut)\n document.addEventListener('click', this._onClickOutside)\n }\n\n private _onTriggerSlotChange = () => {\n this._syncTriggerSlot()\n }\n\n /*\n * 自定义触发器判定必须同步:slotchange 在首次渲染之后才派发,只依赖它会让默认\n * web-ui-input 先渲染一帧再被替换,自定义触发器出现时会闪一下默认输入框。\n * 判据与 portal 迁移排除(isTriggerSlotNode)同源:宿主直接子节点带 slot=\"trigger\"。\n */\n private _syncTriggerSlot() {\n const hasTriggerSlot = Array.from(this.children).some(isTriggerSlotNode)\n if (this._hasTriggerSlot === hasTriggerSlot) return\n this._hasTriggerSlot = hasTriggerSlot\n // 触发器在默认与自定义之间切换:anchor 与 portal 会话按新触发器重建。\n // 首次渲染前尚无 portal 会话,无需重建。\n if (this.hasUpdated) this._reconfigureOverlay()\n }\n\n override attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {\n super.attributeChangedCallback(name, oldValue, newValue)\n if (name === 'selected-value' && this.isConnected && newValue !== this._selectedValue) {\n queueMicrotask(() => {\n if (this.isConnected) this._syncSelectedValue()\n })\n }\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._optionPortal.dispose()\n this._trigger.dispose()\n this.removeEventListener('keydown', this._onKeydown)\n this.removeEventListener('focusout', this._onFocusOut)\n document.removeEventListener('click', this._onClickOutside)\n this._options.forEach(this._unbindOption)\n this._close()\n this._portal = undefined\n this._portalContent = undefined\n // 撤销登记由 _panel.dispose() 完成(句柄归它持有),不再需要单独 dispose 仲裁者。\n this._panel.dispose()\n this._scrollLock.release()\n }\n\n override firstUpdated() {\n this._trigger.bind()\n requestAnimationFrame(() => {\n if (this.isConnected) this._optionPortal.scheduleRefresh()\n })\n }\n\n override willUpdate() {\n this._syncTriggerSlot()\n this._refreshOptions()\n this._optionPortal.ensureOptionIds(this._options)\n this._syncSelectedValue()\n this._applyFilter()\n this._syncOptionA11y()\n this._syncActiveOption()\n }\n\n override updated(changed: PropertyValues) {\n // 文本回写经委托层落到当前触发器(默认 web-ui-input 或自定义触发器)\n this._trigger.setValue(this._value)\n if (changed.has('portal') || changed.has('overlayContainer'))\n requestAnimationFrame(() => this._reconfigureOverlay())\n if (changed.has('noScrollLock')) this._syncScrollLock()\n this.toggleAttribute('focused', this._focused)\n this._syncOpenAttribute()\n this._syncOverlayInert()\n this._syncValidity()\n this._syncOpenWithMatches()\n }\n\n override formDisabledCallback(disabled: boolean) {\n this._formAssociation.setDisabled(disabled)\n if (disabled && this._isOpen) this._close()\n }\n\n private _syncValidity() {\n const internals = this._formAssociation.getInternals()\n if (!internals || typeof internals.setValidity !== 'function') return\n // readonly 与 disabled 一致:值不可由用户修改,视为通过校验(原生 barred-from-validation 语义)\n if (this._isDisabled || this.readonly || !this.required || this._value) internals.setValidity({})\n else internals.setValidity({ valueMissing: true }, '请输入内容')\n }\n\n private _syncOpenAttribute() {\n this.toggleAttribute('open', this._isOpen)\n }\n\n /*\n * 禁用/只读态不响应 Escape。仲裁者仍在(吞掉按键并压掉原生 cancel),只是不走关闭\n * 入口 —— 与旧的「跳过候选」语义不同:旧语义放任事件落到下层浮层,把外层一起关掉。\n * 每次渲染后同步即可覆盖 disabled / readonly 属性与表单禁用三条来源。\n */\n private _syncOverlayInert() {\n this._panel.getHandle()?.setInert(this._isDisabled || this.readonly)\n }\n\n private _syncSelectedValue() {\n const query = this._value.trim().toLowerCase()\n const next = query\n ? (this._options.find(o => !o.disabled && o.label.trim().toLowerCase() === query)?.value ?? '')\n : ''\n this._setSelectedValue(next)\n }\n\n private _syncOptionA11y() {\n this._options.forEach(o => {\n o.setAttribute('role', 'option')\n o.setAttribute('aria-selected', o.value === this._selectedValue ? 'true' : 'false')\n o.setAttribute('aria-hidden', 'true')\n })\n }\n\n private _getA11yOptionId(option: WebUiOption): string {\n let id = this._a11yOptionIds.get(option)\n if (!id) {\n id = `${this._idPrefix}-a11y-option-${++this._nextA11yOptionId}`\n this._a11yOptionIds.set(option, id)\n }\n return id\n }\n\n // 按当前文本与 filter 模式过滤候选,非匹配项以 data-filtered 隐藏并跳过导航\n private _applyFilter() {\n const query = this._value.trim().toLowerCase()\n const mode = this._filter\n this._options.forEach(o => {\n const label = o.label.trim().toLowerCase()\n const match = mode === 'none' || !query || (mode === 'prefix' ? label.startsWith(query) : label.includes(query))\n o.toggleAttribute('data-filtered', !match)\n })\n // 活动项被过滤或禁用时清除键盘激活,避免激活不可见项\n const active = this._options[this._activeIndex]\n if (this._activeIndex >= 0 && (!active || active.hasAttribute('data-filtered') || active.disabled)) {\n this._activeIndex = -1\n this._syncActiveOption()\n }\n }\n\n private _hasMatchingOptions() {\n return this._options.some(option => !option.hasAttribute('data-filtered'))\n }\n\n private _syncOpenWithMatches() {\n if (this._isOpen && !this._hasMatchingOptions()) this._close()\n }\n\n private readonly _optionListeners = createOptionListenerBinding({\n // 惰性解引用:handler 字段声明在后者仍可在调用期取到\n onClick: event => this._handleOptionClick(event),\n onPointerOver: event => this._handleOptionPointerOver(event),\n onPointerDown: event => this._handleOptionPointerDown(event),\n onUpdate: () => this._onOptionUpdate()\n })\n private _bindOption = (option: WebUiOption) => this._optionListeners.bind(option)\n private _unbindOption = (option: WebUiOption) => this._optionListeners.unbind(option)\n\n private _refreshOptions() {\n const activeOption = this._options[this._activeIndex]\n // diff 绑定/解绑与激活索引按 option 身份保持在 shared 内完成\n const next = this._optionPortal.refresh(this._options, activeOption)\n this._options = next.options\n this._activeIndex = next.activeIndex\n }\n\n private _handleOptionClick = (e: Event) => {\n if (!(e.currentTarget instanceof HTMLElement)) return\n const option = e.currentTarget as WebUiOption\n if (option.disabled) return\n this._selectOption(option)\n }\n\n private _onOptionUpdate = () => {\n this._optionPortal.scheduleRefresh()\n }\n\n private _handleOptionPointerOver = (e: PointerEvent) => {\n if (e.pointerType === 'touch') return\n if (!this._isOpen || !(e.currentTarget instanceof HTMLElement)) return\n\n // 指针接管后只保留 :hover,避免键盘激活项与悬停项同时高亮。\n this._activeIndex = -1\n this._syncActiveOption()\n }\n\n private _handleOptionPointerDown = (e: PointerEvent) => {\n // 保持输入框焦点,避免 focusout 在 click 前关闭浮层。\n e.preventDefault()\n }\n\n // slotchange 不冒泡,只能由模板内 slot 自行监听;稳定引用避免 Lit 重挂载\n private _onSlotChange = () => {\n this._optionPortal.scheduleRefresh()\n }\n\n private _onKeydown = (e: KeyboardEvent) => {\n if (this._isDisabled || this.readonly) return\n this._applyFilter()\n this._syncOpenWithMatches()\n\n // Escape 不在本组件处理:由共享仲裁者在 document 捕获阶段归属(issue #120 Block 1)。\n switch (e.key) {\n /*\n * 多行触发器(textarea)的方向键归光标移动:面板打开时不接管、不\n * preventDefault,与 Enter 的 multiline 例外同判据。面板关闭时方向键\n * 仍是打开入口(键盘可达性不依赖指针)。\n */\n case 'ArrowDown':\n case 'ArrowUp': {\n if (this._isOpen && this._trigger.isMultilineEdit(e)) break\n e.preventDefault()\n if (!this._isOpen) this._open(true)\n else this._navigateActive(e.key === 'ArrowDown' ? 1 : -1)\n break\n }\n case 'Enter': {\n /*\n * 面板打开时接管 Enter:选择候选或提交 custom value。\n * 零匹配时面板会被过滤状态收敛关闭,但 allow-custom-value 仍需在关闭态提交。\n * 多行触发器(textarea)保留换行语义,不接管为选中高亮项。\n */\n const canSubmitCustomValue = this._canSubmitCustomValue()\n if (\n !this._trigger.isMultilineEdit(e) &&\n (this._isOpen || (!this._hasMatchingOptions() && canSubmitCustomValue))\n ) {\n e.preventDefault()\n if (this._isOpen) {\n const option = this._options[this._activeIndex]\n if (option && !option.disabled && !option.hasAttribute('data-filtered')) {\n this._selectOption(option)\n return\n }\n\n const exactOption = this._findExactOption(this._value)\n if (exactOption) {\n this._selectOption(exactOption)\n return\n }\n }\n\n if (canSubmitCustomValue) this._selectCustomValue()\n }\n break\n }\n }\n }\n\n private _navigateActive(delta: number) {\n const enabled = this._options.filter(o => !o.disabled && !o.hasAttribute('data-filtered'))\n if (!enabled.length) return\n\n const currentIdx = this._activeIndex >= 0 ? enabled.indexOf(this._options[this._activeIndex]) : delta > 0 ? -1 : 0\n this._activeIndex = this._options.indexOf(enabled[nextWrappingIndex(enabled.length, currentIdx, delta)])\n this._syncActiveOption()\n }\n\n private _setInitialActiveOption() {\n const firstEnabled = this._options.findIndex(o => !o.disabled && !o.hasAttribute('data-filtered'))\n this._activeIndex = firstEnabled\n this._syncActiveOption()\n }\n\n private _syncActiveOption() {\n this._options.forEach((option, index) => option.toggleAttribute('active', index === this._activeIndex))\n }\n\n private _selectOption(option: WebUiOption) {\n if (this.readonly) return\n // value 即输入文本:选择后回填为 option label,selected-value 取 option value\n this.value = option.label\n this._setSelectedValue(option.value)\n this._close()\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private _findExactOption(value: string): WebUiOption | undefined {\n const query = value.trim().toLowerCase()\n if (!query) return undefined\n\n return this._options.find(option => !option.disabled && option.label.trim().toLowerCase() === query)\n }\n\n private _hasExactOption(value: string): boolean {\n const query = value.trim().toLowerCase()\n return this._options.some(option => option.label.trim().toLowerCase() === query)\n }\n\n private _canSubmitCustomValue() {\n return this.allowCustomValue && !!this._value && !this._hasExactOption(this._value)\n }\n\n private _selectCustomValue() {\n // Custom value 的 identity 由消费端管理;组件不隐式创建 option,selected-value 保持派生空值。\n this._close()\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private _onTriggerInput = (value: string) => {\n if (this._isDisabled || this.readonly) return\n if (value === this._value) return\n this.value = value\n this._activeIndex = -1\n this._syncActiveOption()\n this._open()\n }\n\n private _onTriggerClick = () => {\n if (this._isDisabled || this.readonly) return\n this._open()\n }\n\n /*\n * focus/blur 不冒泡,委托层监听等价且冒泡的 focusin/focusout。\n * 自定义触发器位于 light DOM,其焦点变化不会跨 shadow 边界到达宿主,这里补发\n * 公共 focus/blur 兑现 README 事件契约;默认触发器在 shadow 内,原生事件已能\n * 到达宿主,重复派发会让消费端收到两次。\n */\n private _onTriggerFocusIn = (event: FocusEvent) => {\n if (this._isDisabled) return\n this._focused = true\n // composed 与原生 focus 一致(宿主内 retarget):跨 shadow 边界的消费者能收到,\n // 但不 bubbles —— focus/blur 的公共契约本来就不冒泡。\n // relatedTarget 透传:消费端靠它判断焦点来回,补发事件不该丢这个信息\n if (this._isSlottedFocus(event))\n this.dispatchEvent(new FocusEvent('focus', { composed: true, relatedTarget: event.relatedTarget }))\n }\n\n private _onTriggerFocusOut = (event: FocusEvent) => {\n this._focused = false\n if (this._isSlottedFocus(event))\n this.dispatchEvent(new FocusEvent('blur', { composed: true, relatedTarget: event.relatedTarget }))\n }\n\n private _isSlottedFocus(event: FocusEvent): boolean {\n const target = event.target\n return target instanceof Node && !!this.shadowRoot && !this.shadowRoot.contains(target)\n }\n\n // 开合生命周期不变量收敛在 shared combobox-shell;autocomplete 注入\n // readonly guard 与 close 前的 portal 内容收敛钩子\n private readonly _openController = createComboboxOpenController({\n canOpen: () => !this._isDisabled && !this.readonly,\n getIsOpen: () => this._isOpen,\n setIsOpen: open => {\n this._isOpen = open\n },\n dispatchOpenChange: () => this._dispatchOpenChange(),\n syncScrollLock: open => this._syncScrollLock(open),\n isPortal: () => this.portal,\n openOverlay: isKeyboardNavigation => this._openOverlay(isKeyboardNavigation),\n closeOverlay: () => void this._closeOverlay(),\n onOpen: isKeyboardNavigation => {\n if (isKeyboardNavigation) this._setInitialActiveOption()\n else this._syncActiveOption()\n },\n onBeforeClose: () => {\n // Portal 内容可能在当前微任务尚未完成同步;先把 light DOM 中新增或重排的节点\n // 收回当前 content,再关闭并 restore,避免 restoreContent() 按旧 tracking 顺序把\n // 新 option 插到旧 option 前面。\n this._optionPortal.syncPortalContent()\n },\n onAfterClose: () => {\n this._activeIndex = -1\n this._options.forEach(o => o.removeAttribute('active'))\n }\n })\n\n private _open(isKeyboardNavigation = false) {\n this._applyFilter()\n if (!this._hasMatchingOptions()) {\n if (this._isOpen) this._close()\n return\n }\n this._openController.open(isKeyboardNavigation)\n }\n\n private _close() {\n this._openController.close()\n }\n\n private _dispatchOpenChange() {\n dispatchOpenChangeEvent(this, this._isOpen)\n }\n\n private _openOverlay(isKeyboardNavigation = false) {\n this._panel.open(isKeyboardNavigation)\n // 新会话的句柄刚建立,惰性状态需按当前 disabled/readonly 重新同步。\n this._syncOverlayInert()\n }\n\n private async _closeOverlay() {\n const closed = await this._panel.close(() => this._isOpen)\n if (closed) {\n this._portal = undefined\n this._portalContent = undefined\n }\n }\n\n private _reconfigureOverlay() {\n this._portal = undefined\n this._portalContent = undefined\n this._panel.reconfigure(this._isOpen)\n /*\n * reconfigure 重新 claim = 新会话,inert 回到 false。实测这条重推是**冗余**的(摘掉本行\n * 全部用例仍绿):这条路径后面跟着一轮渲染,而 `updated()` 每次渲染都会调\n * `_syncOverlayInert()`。留在这里是不想依赖「这条路径必然跟着一次渲染」这个隐含前提。\n */\n this._syncOverlayInert()\n }\n\n private _syncScrollLock(isOpen = this._isOpen) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n private _createPortal(): OverlayPortal {\n const portal = defineOverlayPortal().make({\n container: this.overlayContainer,\n target: this,\n style: `${glass}\\n${overlayMotion}\\n${style}`,\n className: 'wui-glass autocomplete-overlay portal wui-floating-panel',\n onContentChange: () => {\n this._optionPortal.scheduleRefresh()\n }\n })\n this._portal = portal\n applyOverlayVariables(portal.panel, this, [\n '--wui-overlay-min-width',\n '--wui-autocomplete-max-width',\n '--wui-autocomplete-max-height'\n ])\n portal.panel.setAttribute('aria-hidden', 'true')\n portal.panel.addEventListener('pointerdown', this._handlePanelPointerDown)\n const scroll = document.createElement('div')\n scroll.className = 'autocomplete-scroll'\n const content = document.createElement('div')\n content.className = 'autocomplete-content'\n this._portalContent = content\n scroll.append(content)\n portal.panel.append(scroll)\n for (const node of Array.from(this.childNodes)) {\n // 框架注释锚点(v-if/v-for 占位)必须留在宿主:锚点进面板后 Vue 下次翻转\n // 会以面板内节点为插入基准;portal 的 marker 注释同理不参与迁移。\n if (node instanceof Comment) continue\n // 自定义 trigger 与 select(:461)同判据:留在宿主,不随 options 迁入浮层\n if (isTriggerSlotNode(node)) continue\n // 已移除的 empty slot 不能作为普通内容出现在新 portal 面板中\n if (isEmptySlotNode(node)) continue\n portal.appendContent([node], content)\n }\n return portal\n }\n\n private _getLabelledbyText(): string {\n if (!this.ariaLabelledby) return ''\n\n const root = this.getRootNode()\n if (!(root instanceof Document || root instanceof ShadowRoot)) return ''\n\n return this.ariaLabelledby\n .trim()\n .split(/\\s+/)\n .map(id => {\n const escapedId = typeof CSS !== 'undefined' && CSS.escape ? CSS.escape(id) : id\n return root.querySelector(`#${escapedId}`)?.textContent?.trim() ?? ''\n })\n .filter(Boolean)\n .join(' ')\n }\n\n override render() {\n const listboxId = `${this._idPrefix}-listbox`\n const labelId = `${this._idPrefix}-label`\n const labelledbyText = this._getLabelledbyText()\n const visibleOptions = this._options.filter(option => !option.hasAttribute('data-filtered'))\n const activeOption = this._isOpen && this._activeIndex >= 0 ? this._options[this._activeIndex] : undefined\n const activeDescendant = activeOption ? this._getA11yOptionId(activeOption) : nothing\n\n return html`\n <div class=\"wui-autocomplete-inner\">\n ${labelledbyText ? html`<span class=\"autocomplete-a11y-only\" id=${labelId}>${labelledbyText}</span>` : nothing}\n <div\n class=\"autocomplete-trigger\"\n ?data-custom-trigger=${this._hasTriggerSlot}\n role=\"combobox\"\n aria-expanded=${this._isOpen}\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n aria-controls=${listboxId}\n aria-disabled=${String(this._isDisabled)}\n aria-readonly=${this.readonly ? 'true' : nothing}\n aria-label=${ifDefined(this.ariaLabel)}\n aria-labelledby=${labelledbyText ? labelId : nothing}\n aria-activedescendant=${activeDescendant}\n tabindex=\"-1\"\n >\n <slot name=\"trigger\" @slotchange=${this._onTriggerSlotChange}></slot>\n ${\n !this._hasTriggerSlot\n ? html`<web-ui-input\n class=\"autocomplete-input\"\n .value=${this._value}\n placeholder=${this.placeholder}\n name=${this.name}\n ?borderless=${this.borderless}\n ?disabled=${this._isDisabled}\n ?readonly=${this.readonly}\n ?required=${this.required}\n aria-label=${ifDefined(this.ariaLabel || labelledbyText || undefined)}\n ></web-ui-input>`\n : nothing\n }\n </div>\n <div\n class=\"autocomplete-a11y-listbox\"\n id=${listboxId}\n role=\"listbox\"\n ?hidden=${!this._isOpen}\n aria-hidden=${String(!this._isOpen)}\n >\n ${visibleOptions.map(\n option =>\n html`<div\n id=${this._getA11yOptionId(option)}\n role=\"option\"\n aria-selected=${option.value === this._selectedValue ? 'true' : 'false'}\n aria-hidden=${String(!this._isOpen)}\n aria-disabled=${String(option.disabled)}\n >\n ${option.label}\n </div>`\n )}\n </div>\n <div\n class=\"autocomplete-overlay wui-floating-panel wui-glass\"\n hidden\n aria-hidden=\"true\"\n @pointerdown=${this._handlePanelPointerDown}\n >\n <div class=\"autocomplete-scroll\">\n <div class=\"autocomplete-content\">\n <slot @slotchange=${this._onSlotChange}></slot>\n </div>\n </div>\n </div>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n focus: FocusEvent\n blur: FocusEvent\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-autocomplete': WebUiAutocomplete\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/autocomplete/index.ts"],"sourcesContent":["import '@/components/input'\n\nimport { html, LitElement, nothing, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport overlayMotion from '@/assets/overlay-motion.css?inline'\nimport type { WebUiOption } from '@/components/option'\nimport { FormAssociated, defineFormAssociation, FormAssociationController } from '@/shared/form-association'\nimport { normalizeLiteral } from '@/shared/normalize'\nimport { dispatchOpenChangeEvent } from '@/shared/open-state'\nimport {\n createComboboxOpenController,\n createOptionListenerBinding,\n defineComboboxTrigger,\n defineOptionPortal,\n handleComboboxFocusOut,\n nextWrappingIndex\n} from '@/shared/option-portal'\nimport { defineAnchoredPanel } from '@/shared/overlay/anchored-panel'\nimport { defineOpenOverlay } from '@/shared/overlay/open-overlay'\nimport {\n applyOverlayVariables,\n defineOverlayPortal,\n type OverlayContainer,\n type OverlayPortal\n} from '@/shared/overlay/portal'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\nconst FILTER_MODES = ['none', 'prefix', 'contains'] as const\ntype FilterMode = (typeof FILTER_MODES)[number]\n\n// 自定义 trigger 与 select 同判据:必须留在宿主,不随 options 迁入 portal 面板\nfunction isTriggerSlotNode(node: Node): node is Element {\n return node instanceof Element && node.slot === 'trigger'\n}\n\n// 已移除的 empty slot 仍可能留在既有消费者 light DOM;不得把它作为普通内容迁入 portal。\nfunction isEmptySlotNode(node: Node): node is Element {\n return node instanceof Element && node.slot === 'empty'\n}\n\n/**\n * `web-ui-autocomplete`:可输入并过滤候选的单值选择器。\n *\n * 与 `web-ui-select` 共用 `<web-ui-option>` 子项注册协议与浮层能力,但触发区是\n * 可编辑输入框:`value` 即当前输入文本(表单值),键入时按 `filter` 模式过滤候选,\n * 选择 option 时文本回填为该项 label,`selected-value` 暴露该项的 value。\n * `allow-custom-value` 开启后,Enter 可把不匹配候选的原文作为 custom value 显式提交。\n *\n * 触发器跟随 select 的 wrapper div 模式:`slot[name=\"trigger\"]` 可替换默认触发器\n * (shadow 内的 `web-ui-input`);多行触发器(如 `web-ui-textarea`)保留 Enter 换行语义,\n * 关闭面板用 Escape 或 blur。\n * tab 位只属于触发器自身:默认触发器是 shadow 内 input,自定义触发器由消费者提供\n * 可聚焦元素(README 契约);包装 div 恒为 tabindex=\"-1\",不占顺序焦点,避免双 tab 位。\n */\n@customElement('web-ui-autocomplete')\nexport class WebUiAutocomplete extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(overlayMotion), unsafeCSS(style)]\n\n @property({ type: String, reflect: true }) placeholder = ''\n @property({ type: Boolean, reflect: true }) borderless = false\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) readonly = false\n @property({ type: Boolean, reflect: true }) required = false\n @property({ type: Boolean, reflect: true }) portal = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' })\n noScrollLock = false\n @property({ type: Boolean, reflect: true, attribute: 'allow-custom-value' })\n allowCustomValue = false\n @property({ attribute: false }) overlayContainer?: OverlayContainer\n @property({ type: String, reflect: true }) name = ''\n @property({ type: String, attribute: 'aria-label' }) override ariaLabel: string | null = null\n @property({ type: String, attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined\n\n private _filter: FilterMode = 'contains'\n\n @property({ type: String, reflect: true })\n get filter(): FilterMode {\n return this._filter\n }\n set filter(v: string) {\n const old = this._filter\n this._filter = normalizeLiteral(v, FILTER_MODES, 'contains')\n this.requestUpdate('filter', old)\n }\n\n // value 不反射 attribute,attribute 只参与首次声明式初始化。\n @state() private _value = ''\n\n get value(): string {\n return this._value\n }\n set value(v: string) {\n const old = this._value\n this._value = v\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n // selected-value:文本精确匹配 option label 时派生出的 option value(只读反射)。\n @state() private _selectedValue = ''\n\n get selectedValue(): string {\n return this._selectedValue\n }\n\n private _setSelectedValue(value: string) {\n const old = this._selectedValue\n if (old === value && this.getAttribute('selected-value') === value) return\n\n this._selectedValue = value\n this.setAttribute('selected-value', value)\n if (old !== value) this.requestUpdate('selectedValue', old)\n }\n\n private readonly _formAssociation = defineFormAssociation<string>({\n host: this,\n initialize: () => {\n // 同步 HTML 属性中预设的 value,补全访问器模式不处理 attribute 反射。\n const attrValue = this.getAttribute('value')\n if (attrValue !== null) this._value = attrValue\n },\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => this._value,\n getFormState: () => this._value,\n restoreState: state => {\n if (typeof state === 'string') this.value = state\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n @state() private _isOpen = false\n @state() private _activeIndex = -1\n @state() private _focused = false\n @state() private _hasTriggerSlot = false\n\n private static _nextInstanceId = 0\n private readonly _idPrefix = `web-ui-autocomplete-${++WebUiAutocomplete._nextInstanceId}`\n private readonly _a11yOptionIds = new WeakMap<WebUiOption, string>()\n private _nextA11yOptionId = 0\n private _options: WebUiOption[] = []\n private _portal?: OverlayPortal\n private _portalContent?: HTMLElement\n private readonly _scrollLock = defineScrollLockLease().make()\n\n // option 注册表 / portal 同步 / 微任务调度收敛到 shared 模块(select 同款)\n private readonly _optionPortal = defineOptionPortal().make({\n element: this,\n idPrefix: `${this._idPrefix}-option`,\n getPortal: () => this._portal,\n getPortalContent: () => this._portalContent,\n isOpen: () => this.portal && this._isOpen,\n // 默认 slot 内容随面板迁移;trigger 与已移除的 empty slot 留在宿主,不进面板。\n getMigratableNodes: () =>\n Array.from(this.childNodes).filter(node => !isTriggerSlotNode(node) && !isEmptySlotNode(node)),\n hasUpdated: () => this.hasUpdated,\n requestUpdate: () => this.requestUpdate(),\n bindOption: option => this._bindOption(option),\n unbindOption: option => this._unbindOption(option)\n })\n /*\n * 触发器委托层:默认触发器(shadow 内 web-ui-input)与 slot[name=\"trigger\"] 自定义\n * 触发器对组件暴露同一形状,监听统一挂在 trigger 包装 div 上,两条路径不特判。\n */\n private readonly _trigger = defineComboboxTrigger().make({\n getWrapper: () => this.shadowRoot?.querySelector<HTMLElement>('.autocomplete-trigger') ?? null,\n getCustomTrigger: () => {\n const assigned = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"trigger\"]')?.assignedElements()[0]\n return assigned instanceof HTMLElement ? assigned : null\n },\n getFallbackTrigger: () => this.shadowRoot?.querySelector<HTMLElement>('.autocomplete-input') ?? null,\n onInput: value => this._onTriggerInput(value),\n onClick: () => this._onTriggerClick(),\n onFocusIn: event => this._onTriggerFocusIn(event),\n onFocusOut: event => this._onTriggerFocusOut(event)\n })\n /*\n * 开启态浮层承载「我开着吗」(issue #120 Block 1):宿主级 keydown 在 portal 模式下\n * 收不到面板内的 Escape,改由唯一仲裁者在 document 捕获阶段判定最内层。\n * 宿主接口除 requestClose 外只再提供一个 isConnected(仅供惰性回收读取):开启状态是\n * 声明而非询问。\n */\n private readonly _overlay = defineOpenOverlay().make({\n requestClose: () => this._close(),\n isConnected: () => this.isConnected\n })\n private readonly _panel = defineAnchoredPanel().make({\n // 锚点取当前生效的触发器 host:默认触发器或自定义 trigger slot 内容\n getAnchor: () =>\n this._trigger.getTrigger() ?? this.shadowRoot?.querySelector<HTMLElement>('.autocomplete-trigger') ?? null,\n getLocalPanel: () => this.shadowRoot?.querySelector<HTMLElement>('.autocomplete-overlay') ?? null,\n getPositioning: () => ({\n placement: 'bottom-start',\n offset: 4,\n minAnchorWidth: true,\n strategy: this.portal ? 'fixed' : 'absolute'\n }),\n isPortal: () => this.portal,\n createPortal: () => this._createPortal(),\n openOverlay: this._overlay\n })\n\n get isOpen(): boolean {\n return this._isOpen\n }\n\n get open(): boolean {\n return this._isOpen\n }\n\n /*\n * 公共焦点委托:聚焦/失焦当前生效的触发器(默认 web-ui-input 或自定义触发器)。\n * 组件自带 focus 重定向时落到内部原生控件;自定义触发器没有该能力时,\n * 原生 focus() 聚焦 host 自身。无触发器(尚未首渲或已断开)时静默返回。\n */\n override focus(options?: FocusOptions): void {\n this._trigger.focusTrigger(options)\n }\n\n override blur(): void {\n this._trigger.blurTrigger()\n }\n\n private _onClickOutside = (e: MouseEvent) => {\n if (!this._isOpen) return\n\n // 在监听器内部判定:composedPath() 在派发结束后会被清空。\n const isInside = e.composedPath().includes(this) || !!this._panel.getHandle()?.containsEvent(e)\n if (!isInside) this._close()\n }\n\n private _onFocusOut = () => {\n handleComboboxFocusOut(\n this,\n () => this._panel.getHandle(),\n () => this._isOpen,\n () => this._close()\n )\n }\n\n private _handlePanelPointerDown = (e: PointerEvent) => {\n // 非 option 区域不可聚焦;阻止 pointerdown 的默认聚焦行为,保持 combobox 焦点,\n // 使点击 empty state、滚动区和面板 padding 不会被 focusout 误判为外部交互。\n e.preventDefault()\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._optionPortal.bindHost()\n // 宿主可能被上层浮层 portal 迁移(断开重连):shadow 内的包装 div 仍在,重新绑定委托监听\n this._trigger.bind()\n this.addEventListener('keydown', this._onKeydown)\n this.addEventListener('focusout', this._onFocusOut)\n document.addEventListener('click', this._onClickOutside)\n }\n\n private _onTriggerSlotChange = () => {\n this._syncTriggerSlot()\n }\n\n /*\n * 自定义触发器判定必须同步:slotchange 在首次渲染之后才派发,只依赖它会让默认\n * web-ui-input 先渲染一帧再被替换,自定义触发器出现时会闪一下默认输入框。\n * 判据与 portal 迁移排除(isTriggerSlotNode)同源:宿主直接子节点带 slot=\"trigger\"。\n */\n private _syncTriggerSlot() {\n const hasTriggerSlot = Array.from(this.children).some(isTriggerSlotNode)\n if (this._hasTriggerSlot === hasTriggerSlot) return\n this._hasTriggerSlot = hasTriggerSlot\n // 触发器在默认与自定义之间切换:anchor 与 portal 会话按新触发器重建。\n // 首次渲染前尚无 portal 会话,无需重建。\n if (this.hasUpdated) this._reconfigureOverlay()\n }\n\n override attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {\n super.attributeChangedCallback(name, oldValue, newValue)\n if (name === 'selected-value' && this.isConnected && newValue !== this._selectedValue) {\n queueMicrotask(() => {\n if (this.isConnected) this._syncSelectedValue()\n })\n }\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._optionPortal.dispose()\n this._trigger.dispose()\n this.removeEventListener('keydown', this._onKeydown)\n this.removeEventListener('focusout', this._onFocusOut)\n document.removeEventListener('click', this._onClickOutside)\n this._options.forEach(this._unbindOption)\n this._close()\n this._portal = undefined\n this._portalContent = undefined\n // 撤销登记由 _panel.dispose() 完成(句柄归它持有),不再需要单独 dispose 仲裁者。\n this._panel.dispose()\n this._scrollLock.release()\n }\n\n override firstUpdated() {\n this._trigger.bind()\n requestAnimationFrame(() => {\n if (this.isConnected) this._optionPortal.scheduleRefresh()\n })\n }\n\n override willUpdate() {\n this._syncTriggerSlot()\n this._refreshOptions()\n this._optionPortal.ensureOptionIds(this._options)\n this._syncSelectedValue()\n this._applyFilter()\n this._syncOptionA11y()\n this._syncActiveOption()\n }\n\n override updated(changed: PropertyValues) {\n // 文本回写经委托层落到当前触发器(默认 web-ui-input 或自定义触发器)\n this._trigger.setValue(this._value)\n if (changed.has('portal') || changed.has('overlayContainer'))\n requestAnimationFrame(() => this._reconfigureOverlay())\n if (changed.has('noScrollLock')) this._syncScrollLock()\n this.toggleAttribute('focused', this._focused)\n this._syncOpenAttribute()\n this._syncOverlayInert()\n this._syncValidity()\n this._syncOpenWithMatches()\n }\n\n override formDisabledCallback(disabled: boolean) {\n this._formAssociation.setDisabled(disabled)\n if (disabled && this._isOpen) this._close()\n }\n\n private _syncValidity() {\n const internals = this._formAssociation.getInternals()\n if (!internals || typeof internals.setValidity !== 'function') return\n // readonly 与 disabled 一致:值不可由用户修改,视为通过校验(原生 barred-from-validation 语义)\n if (this._isDisabled || this.readonly || !this.required || this._value) internals.setValidity({})\n else internals.setValidity({ valueMissing: true }, '请输入内容')\n }\n\n private _syncOpenAttribute() {\n this.toggleAttribute('open', this._isOpen)\n }\n\n /*\n * 禁用/只读态不响应 Escape。仲裁者仍在(吞掉按键并压掉原生 cancel),只是不走关闭\n * 入口 —— 与旧的「跳过候选」语义不同:旧语义放任事件落到下层浮层,把外层一起关掉。\n * 每次渲染后同步即可覆盖 disabled / readonly 属性与表单禁用三条来源。\n */\n private _syncOverlayInert() {\n this._panel.getHandle()?.setInert(this._isDisabled || this.readonly)\n }\n\n private _syncSelectedValue() {\n const query = this._value.trim().toLowerCase()\n const next = query\n ? (this._options.find(o => !o.disabled && o.label.trim().toLowerCase() === query)?.value ?? '')\n : ''\n this._setSelectedValue(next)\n }\n\n private _syncOptionA11y() {\n this._options.forEach(o => {\n o.setAttribute('role', 'option')\n o.setAttribute('aria-selected', o.value === this._selectedValue ? 'true' : 'false')\n o.setAttribute('aria-hidden', 'true')\n })\n }\n\n private _getA11yOptionId(option: WebUiOption): string {\n let id = this._a11yOptionIds.get(option)\n if (!id) {\n id = `${this._idPrefix}-a11y-option-${++this._nextA11yOptionId}`\n this._a11yOptionIds.set(option, id)\n }\n return id\n }\n\n // 按当前文本与 filter 模式过滤候选,非匹配项以 data-filtered 隐藏并跳过导航\n private _applyFilter() {\n const query = this._value.trim().toLowerCase()\n const mode = this._filter\n this._options.forEach(o => {\n const label = o.label.trim().toLowerCase()\n const match = mode === 'none' || !query || (mode === 'prefix' ? label.startsWith(query) : label.includes(query))\n o.toggleAttribute('data-filtered', !match)\n })\n // 活动项被过滤或禁用时清除键盘激活,避免激活不可见项\n const active = this._options[this._activeIndex]\n if (this._activeIndex >= 0 && (!active || active.hasAttribute('data-filtered') || active.disabled)) {\n this._activeIndex = -1\n this._syncActiveOption()\n }\n }\n\n private _hasMatchingOptions() {\n return this._options.some(option => !option.hasAttribute('data-filtered'))\n }\n\n private _syncOpenWithMatches() {\n if (this._isOpen && !this._hasMatchingOptions()) this._close()\n }\n\n private readonly _optionListeners = createOptionListenerBinding({\n // 惰性解引用:handler 字段声明在后者仍可在调用期取到\n onClick: event => this._handleOptionClick(event),\n onPointerOver: event => this._handleOptionPointerOver(event),\n onPointerDown: event => this._handleOptionPointerDown(event),\n onUpdate: () => this._onOptionUpdate()\n })\n private _bindOption = (option: WebUiOption) => this._optionListeners.bind(option)\n private _unbindOption = (option: WebUiOption) => this._optionListeners.unbind(option)\n\n private _refreshOptions() {\n const activeOption = this._options[this._activeIndex]\n // diff 绑定/解绑与激活索引按 option 身份保持在 shared 内完成\n const next = this._optionPortal.refresh(this._options, activeOption)\n this._options = next.options\n this._activeIndex = next.activeIndex\n }\n\n private _handleOptionClick = (e: Event) => {\n if (!(e.currentTarget instanceof HTMLElement)) return\n const option = e.currentTarget as WebUiOption\n if (option.disabled) return\n this._selectOption(option)\n }\n\n private _onOptionUpdate = () => {\n this._optionPortal.scheduleRefresh()\n }\n\n private _handleOptionPointerOver = (e: PointerEvent) => {\n if (e.pointerType === 'touch') return\n if (!this._isOpen || !(e.currentTarget instanceof HTMLElement)) return\n\n // 指针接管后只保留 :hover,避免键盘激活项与悬停项同时高亮。\n this._activeIndex = -1\n this._syncActiveOption()\n }\n\n private _handleOptionPointerDown = (e: PointerEvent) => {\n // 保持输入框焦点,避免 focusout 在 click 前关闭浮层。\n e.preventDefault()\n }\n\n // slotchange 不冒泡,只能由模板内 slot 自行监听;稳定引用避免 Lit 重挂载\n private _onSlotChange = () => {\n this._optionPortal.scheduleRefresh()\n }\n\n private _onKeydown = (e: KeyboardEvent) => {\n if (this._isDisabled || this.readonly) return\n this._applyFilter()\n this._syncOpenWithMatches()\n\n // Escape 不在本组件处理:由共享仲裁者在 document 捕获阶段归属(issue #120 Block 1)。\n switch (e.key) {\n /*\n * 多行触发器(textarea)的方向键归光标移动:面板打开时不接管、不\n * preventDefault,与 Enter 的 multiline 例外同判据。面板关闭时方向键\n * 仍是打开入口(键盘可达性不依赖指针)。\n */\n case 'ArrowDown':\n case 'ArrowUp': {\n if (this._isOpen && this._trigger.isMultilineEdit(e)) break\n e.preventDefault()\n if (!this._isOpen) this._open(true)\n else this._navigateActive(e.key === 'ArrowDown' ? 1 : -1)\n break\n }\n case 'Enter': {\n /*\n * 面板打开时接管 Enter:选择候选或提交 custom value。\n * 零匹配时面板会被过滤状态收敛关闭,但 allow-custom-value 仍需在关闭态提交。\n * 多行触发器(textarea)保留换行语义,不接管为选中高亮项。\n */\n const canSubmitCustomValue = this._canSubmitCustomValue()\n if (\n !this._trigger.isMultilineEdit(e) &&\n (this._isOpen || (!this._hasMatchingOptions() && canSubmitCustomValue))\n ) {\n e.preventDefault()\n if (this._isOpen) {\n const option = this._options[this._activeIndex]\n if (option && !option.disabled && !option.hasAttribute('data-filtered')) {\n this._selectOption(option)\n return\n }\n\n const exactOption = this._findExactOption(this._value)\n if (exactOption) {\n this._selectOption(exactOption)\n return\n }\n }\n\n if (canSubmitCustomValue) this._selectCustomValue()\n }\n break\n }\n }\n }\n\n private _navigateActive(delta: number) {\n const enabled = this._options.filter(o => !o.disabled && !o.hasAttribute('data-filtered'))\n if (!enabled.length) return\n\n const currentIdx = this._activeIndex >= 0 ? enabled.indexOf(this._options[this._activeIndex]) : delta > 0 ? -1 : 0\n this._activeIndex = this._options.indexOf(enabled[nextWrappingIndex(enabled.length, currentIdx, delta)])\n this._syncActiveOption()\n }\n\n private _setInitialActiveOption() {\n const firstEnabled = this._options.findIndex(o => !o.disabled && !o.hasAttribute('data-filtered'))\n this._activeIndex = firstEnabled\n this._syncActiveOption()\n }\n\n private _syncActiveOption() {\n this._options.forEach((option, index) => option.toggleAttribute('active', index === this._activeIndex))\n }\n\n private _selectOption(option: WebUiOption) {\n if (this.readonly) return\n // value 即输入文本:选择后回填为 option label,selected-value 取 option value\n this.value = option.label\n this._setSelectedValue(option.value)\n this._close()\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private _findExactOption(value: string): WebUiOption | undefined {\n const query = value.trim().toLowerCase()\n if (!query) return undefined\n\n return this._options.find(option => !option.disabled && option.label.trim().toLowerCase() === query)\n }\n\n private _hasExactOption(value: string): boolean {\n const query = value.trim().toLowerCase()\n return this._options.some(option => option.label.trim().toLowerCase() === query)\n }\n\n private _canSubmitCustomValue() {\n return this.allowCustomValue && !!this._value && !this._hasExactOption(this._value)\n }\n\n private _selectCustomValue() {\n // Custom value 的 identity 由消费端管理;组件不隐式创建 option,selected-value 保持派生空值。\n this._close()\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private _onTriggerInput = (value: string) => {\n if (this._isDisabled || this.readonly) return\n if (value === this._value) return\n this.value = value\n this._activeIndex = -1\n this._syncActiveOption()\n this._open()\n }\n\n private _onTriggerClick = () => {\n if (this._isDisabled || this.readonly) return\n this._open()\n }\n\n /*\n * focus/blur 不冒泡,委托层监听等价且冒泡的 focusin/focusout。\n * 自定义触发器位于 light DOM,其焦点变化不会跨 shadow 边界到达宿主,这里补发\n * 公共 focus/blur 兑现 README 事件契约;默认触发器在 shadow 内,原生事件已能\n * 到达宿主,重复派发会让消费端收到两次。\n */\n private _onTriggerFocusIn = (event: FocusEvent) => {\n if (this._isDisabled) return\n this._focused = true\n // composed 与原生 focus 一致(宿主内 retarget):跨 shadow 边界的消费者能收到,\n // 但不 bubbles —— focus/blur 的公共契约本来就不冒泡。\n // relatedTarget 透传:消费端靠它判断焦点来回,补发事件不该丢这个信息\n if (this._isSlottedFocus(event))\n this.dispatchEvent(new FocusEvent('focus', { composed: true, relatedTarget: event.relatedTarget }))\n }\n\n private _onTriggerFocusOut = (event: FocusEvent) => {\n this._focused = false\n if (this._isSlottedFocus(event))\n this.dispatchEvent(new FocusEvent('blur', { composed: true, relatedTarget: event.relatedTarget }))\n }\n\n private _isSlottedFocus(event: FocusEvent): boolean {\n const target = event.target\n return target instanceof Node && !!this.shadowRoot && !this.shadowRoot.contains(target)\n }\n\n // 开合生命周期不变量收敛在 shared combobox-shell;autocomplete 注入\n // readonly guard 与 close 前的 portal 内容收敛钩子\n private readonly _openController = createComboboxOpenController({\n canOpen: () => !this._isDisabled && !this.readonly,\n getIsOpen: () => this._isOpen,\n setIsOpen: open => {\n this._isOpen = open\n },\n dispatchOpenChange: () => this._dispatchOpenChange(),\n syncScrollLock: open => this._syncScrollLock(open),\n isPortal: () => this.portal,\n openOverlay: isKeyboardNavigation => this._openOverlay(isKeyboardNavigation),\n closeOverlay: () => void this._closeOverlay(),\n onOpen: isKeyboardNavigation => {\n if (isKeyboardNavigation) this._setInitialActiveOption()\n else this._syncActiveOption()\n },\n onBeforeClose: () => {\n // Portal 内容可能在当前微任务尚未完成同步;先把 light DOM 中新增或重排的节点\n // 收回当前 content,再关闭并 restore,避免 restoreContent() 按旧 tracking 顺序把\n // 新 option 插到旧 option 前面。\n this._optionPortal.syncPortalContent()\n },\n onAfterClose: () => {\n this._activeIndex = -1\n this._options.forEach(o => o.removeAttribute('active'))\n }\n })\n\n private _open(isKeyboardNavigation = false) {\n this._applyFilter()\n if (!this._hasMatchingOptions()) {\n if (this._isOpen) this._close()\n return\n }\n this._openController.open(isKeyboardNavigation)\n }\n\n private _close() {\n this._openController.close()\n }\n\n private _dispatchOpenChange() {\n dispatchOpenChangeEvent(this, this._isOpen)\n }\n\n private _openOverlay(isKeyboardNavigation = false) {\n this._panel.open(isKeyboardNavigation)\n // 新会话的句柄刚建立,惰性状态需按当前 disabled/readonly 重新同步。\n this._syncOverlayInert()\n }\n\n private async _closeOverlay() {\n const closed = await this._panel.close(() => this._isOpen)\n if (closed) {\n this._portal = undefined\n this._portalContent = undefined\n }\n }\n\n private _reconfigureOverlay() {\n this._portal = undefined\n this._portalContent = undefined\n this._panel.reconfigure(this._isOpen)\n /*\n * reconfigure 重新 claim = 新会话,inert 回到 false。实测这条重推是**冗余**的(摘掉本行\n * 全部用例仍绿):这条路径后面跟着一轮渲染,而 `updated()` 每次渲染都会调\n * `_syncOverlayInert()`。留在这里是不想依赖「这条路径必然跟着一次渲染」这个隐含前提。\n */\n this._syncOverlayInert()\n }\n\n private _syncScrollLock(isOpen = this._isOpen) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n private _createPortal(): OverlayPortal {\n const portal = defineOverlayPortal().make({\n container: this.overlayContainer,\n target: this,\n style: `${glass}\\n${overlayMotion}\\n${style}`,\n className: 'wui-glass autocomplete-overlay portal wui-floating-panel',\n onContentChange: () => {\n this._optionPortal.scheduleRefresh()\n }\n })\n this._portal = portal\n applyOverlayVariables(portal.panel, this, [\n '--wui-overlay-min-width',\n '--wui-autocomplete-max-width',\n '--wui-autocomplete-max-height'\n ])\n portal.panel.setAttribute('aria-hidden', 'true')\n portal.panel.addEventListener('pointerdown', this._handlePanelPointerDown)\n const scroll = document.createElement('div')\n scroll.className = 'autocomplete-scroll'\n const content = document.createElement('div')\n content.className = 'autocomplete-content'\n this._portalContent = content\n scroll.append(content)\n portal.panel.append(scroll)\n for (const node of Array.from(this.childNodes)) {\n // 框架注释锚点(v-if/v-for 占位)必须留在宿主:锚点进面板后 Vue 下次翻转\n // 会以面板内节点为插入基准;portal 的 marker 注释同理不参与迁移。\n if (node instanceof Comment) continue\n // 自定义 trigger 与 select(:461)同判据:留在宿主,不随 options 迁入浮层\n if (isTriggerSlotNode(node)) continue\n // 已移除的 empty slot 不能作为普通内容出现在新 portal 面板中\n if (isEmptySlotNode(node)) continue\n portal.appendContent([node], content)\n }\n return portal\n }\n\n private _getLabelledbyText(): string {\n if (!this.ariaLabelledby) return ''\n\n const root = this.getRootNode()\n if (!(root instanceof Document || root instanceof ShadowRoot)) return ''\n\n return this.ariaLabelledby\n .trim()\n .split(/\\s+/)\n .map(id => {\n const escapedId = typeof CSS !== 'undefined' && CSS.escape ? CSS.escape(id) : id\n return root.querySelector(`#${escapedId}`)?.textContent?.trim() ?? ''\n })\n .filter(Boolean)\n .join(' ')\n }\n\n override render() {\n const listboxId = `${this._idPrefix}-listbox`\n const labelId = `${this._idPrefix}-label`\n const labelledbyText = this._getLabelledbyText()\n const visibleOptions = this._options.filter(option => !option.hasAttribute('data-filtered'))\n const activeOption = this._isOpen && this._activeIndex >= 0 ? this._options[this._activeIndex] : undefined\n const activeDescendant = activeOption ? this._getA11yOptionId(activeOption) : nothing\n\n return html`\n <div class=\"wui-autocomplete-inner\">\n ${labelledbyText ? html`<span class=\"autocomplete-a11y-only\" id=${labelId}>${labelledbyText}</span>` : nothing}\n <div\n class=\"autocomplete-trigger\"\n ?data-custom-trigger=${this._hasTriggerSlot}\n role=\"combobox\"\n aria-expanded=${this._isOpen}\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n aria-controls=${listboxId}\n aria-disabled=${String(this._isDisabled)}\n aria-readonly=${this.readonly ? 'true' : nothing}\n aria-label=${ifDefined(this.ariaLabel)}\n aria-labelledby=${labelledbyText ? labelId : nothing}\n aria-activedescendant=${activeDescendant}\n tabindex=\"-1\"\n >\n <slot name=\"trigger\" @slotchange=${this._onTriggerSlotChange}></slot>\n ${\n !this._hasTriggerSlot\n ? html`<web-ui-input\n class=\"autocomplete-input\"\n .value=${this._value}\n placeholder=${this.placeholder}\n name=${this.name}\n ?borderless=${this.borderless}\n ?disabled=${this._isDisabled}\n ?readonly=${this.readonly}\n ?required=${this.required}\n aria-label=${ifDefined(this.ariaLabel || labelledbyText || undefined)}\n ></web-ui-input>`\n : nothing\n }\n </div>\n <div\n class=\"autocomplete-a11y-listbox\"\n id=${listboxId}\n role=\"listbox\"\n ?hidden=${!this._isOpen}\n aria-hidden=${String(!this._isOpen)}\n >\n ${visibleOptions.map(\n option =>\n html`<div\n id=${this._getA11yOptionId(option)}\n role=\"option\"\n aria-selected=${option.value === this._selectedValue ? 'true' : 'false'}\n aria-hidden=${String(!this._isOpen)}\n aria-disabled=${String(option.disabled)}\n >\n ${option.label}\n </div>`\n )}\n </div>\n <div\n class=\"autocomplete-overlay wui-floating-panel wui-glass\"\n hidden\n aria-hidden=\"true\"\n @pointerdown=${this._handlePanelPointerDown}\n >\n <div class=\"autocomplete-scroll\">\n <div class=\"autocomplete-content\">\n <slot @slotchange=${this._onSlotChange}></slot>\n </div>\n </div>\n </div>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n focus: FocusEvent\n blur: FocusEvent\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-autocomplete': WebUiAutocomplete\n }\n}\n"],"mappings":"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"}
|