@greypan/web-ui 11.0.0 → 11.2.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 (173) hide show
  1. package/README.CN.md +105 -41
  2. package/README.md +69 -0
  3. package/dist/assets/overlay-motion.js +1 -1
  4. package/dist/assets/overlay-motion.js.map +1 -1
  5. package/dist/assets/selection-control.js +1 -1
  6. package/dist/assets/selection-control.js.map +1 -1
  7. package/dist/classes/glass.js +6 -0
  8. package/dist/classes/glass.js.map +1 -0
  9. package/dist/classes/glow.js +6 -0
  10. package/dist/classes/glow.js.map +1 -0
  11. package/dist/classes/style.css +1 -0
  12. package/dist/components/autocomplete/index.d.ts +7 -0
  13. package/dist/components/autocomplete/index.js +33 -13
  14. package/dist/components/autocomplete/index.js.map +1 -1
  15. package/dist/components/autocomplete/style.js +1 -1
  16. package/dist/components/autocomplete/style.js.map +1 -1
  17. package/dist/components/avatar/index.js +2 -2
  18. package/dist/components/avatar/index.js.map +1 -1
  19. package/dist/components/avatar/style.js +1 -1
  20. package/dist/components/avatar/style.js.map +1 -1
  21. package/dist/components/back-top/index.d.ts +4 -0
  22. package/dist/components/back-top/index.js +60 -22
  23. package/dist/components/back-top/index.js.map +1 -1
  24. package/dist/components/back-top/style.js +1 -1
  25. package/dist/components/back-top/style.js.map +1 -1
  26. package/dist/components/badge/index.d.ts +3 -0
  27. package/dist/components/badge/index.js +29 -14
  28. package/dist/components/badge/index.js.map +1 -1
  29. package/dist/components/badge/style.js +1 -1
  30. package/dist/components/badge/style.js.map +1 -1
  31. package/dist/components/button/index.d.ts +3 -0
  32. package/dist/components/button/index.js +27 -18
  33. package/dist/components/button/index.js.map +1 -1
  34. package/dist/components/button/style.js +1 -1
  35. package/dist/components/button/style.js.map +1 -1
  36. package/dist/components/button-group/index.d.ts +3 -0
  37. package/dist/components/button-group/index.js +15 -2
  38. package/dist/components/button-group/index.js.map +1 -1
  39. package/dist/components/button-group/style.js +1 -1
  40. package/dist/components/button-group/style.js.map +1 -1
  41. package/dist/components/checkbox/index.js +6 -6
  42. package/dist/components/checkbox/index.js.map +1 -1
  43. package/dist/components/checkbox/style.js +1 -1
  44. package/dist/components/checkbox/style.js.map +1 -1
  45. package/dist/components/collapse/index.d.ts +15 -7
  46. package/dist/components/collapse/index.js +44 -26
  47. package/dist/components/collapse/index.js.map +1 -1
  48. package/dist/components/collapse/style.js +1 -1
  49. package/dist/components/collapse/style.js.map +1 -1
  50. package/dist/components/dialog/index.d.ts +1 -0
  51. package/dist/components/dialog/index.js +5 -2
  52. package/dist/components/dialog/index.js.map +1 -1
  53. package/dist/components/dialog/style.js +1 -1
  54. package/dist/components/dialog/style.js.map +1 -1
  55. package/dist/components/drawer/index.js +1 -1
  56. package/dist/components/drawer/index.js.map +1 -1
  57. package/dist/components/dropdown/index.d.ts +16 -0
  58. package/dist/components/dropdown/index.js +147 -18
  59. package/dist/components/dropdown/index.js.map +1 -1
  60. package/dist/components/dropdown-item/style.js +1 -1
  61. package/dist/components/dropdown-item/style.js.map +1 -1
  62. package/dist/components/editable-text/index.js +7 -3
  63. package/dist/components/editable-text/index.js.map +1 -1
  64. package/dist/components/editable-text/style.js +1 -1
  65. package/dist/components/editable-text/style.js.map +1 -1
  66. package/dist/components/empty/index.d.ts +10 -22
  67. package/dist/components/empty/index.js +19 -35
  68. package/dist/components/empty/index.js.map +1 -1
  69. package/dist/components/empty/style.js +1 -1
  70. package/dist/components/empty/style.js.map +1 -1
  71. package/dist/components/image-preview/index.js +2 -2
  72. package/dist/components/image-preview/index.js.map +1 -1
  73. package/dist/components/input/index.d.ts +1 -0
  74. package/dist/components/input/index.js +5 -2
  75. package/dist/components/input/index.js.map +1 -1
  76. package/dist/components/input/style.js +1 -1
  77. package/dist/components/input/style.js.map +1 -1
  78. package/dist/components/input-number/index.d.ts +31 -1
  79. package/dist/components/input-number/index.js +281 -22
  80. package/dist/components/input-number/index.js.map +1 -1
  81. package/dist/components/input-number/style.js +1 -1
  82. package/dist/components/input-number/style.js.map +1 -1
  83. package/dist/components/layout/index.js +44 -39
  84. package/dist/components/layout/index.js.map +1 -1
  85. package/dist/components/layout/style.js +1 -1
  86. package/dist/components/layout/style.js.map +1 -1
  87. package/dist/components/middle-ellipsis/index.d.ts +8 -1
  88. package/dist/components/middle-ellipsis/index.js +68 -10
  89. package/dist/components/middle-ellipsis/index.js.map +1 -1
  90. package/dist/components/option/style.js +1 -1
  91. package/dist/components/option/style.js.map +1 -1
  92. package/dist/components/popover/index.js +1 -1
  93. package/dist/components/popover/index.js.map +1 -1
  94. package/dist/components/popover/style.js +1 -1
  95. package/dist/components/popover/style.js.map +1 -1
  96. package/dist/components/radio/index.d.ts +5 -0
  97. package/dist/components/radio/index.js +33 -23
  98. package/dist/components/radio/index.js.map +1 -1
  99. package/dist/components/radio/interaction.d.ts +4 -0
  100. package/dist/components/radio/interaction.js +17 -0
  101. package/dist/components/radio/interaction.js.map +1 -0
  102. package/dist/components/radio/style.js +1 -1
  103. package/dist/components/radio/style.js.map +1 -1
  104. package/dist/components/radio-group/index.d.ts +22 -0
  105. package/dist/components/radio-group/index.js +219 -28
  106. package/dist/components/radio-group/index.js.map +1 -1
  107. package/dist/components/segmented/index.d.ts +2 -0
  108. package/dist/components/segmented/index.js +74 -18
  109. package/dist/components/segmented/index.js.map +1 -1
  110. package/dist/components/segmented/style.js +1 -1
  111. package/dist/components/segmented/style.js.map +1 -1
  112. package/dist/components/segmented-trigger/index.js +3 -2
  113. package/dist/components/segmented-trigger/index.js.map +1 -1
  114. package/dist/components/segmented-trigger/style.js +1 -1
  115. package/dist/components/segmented-trigger/style.js.map +1 -1
  116. package/dist/components/select/index.d.ts +8 -0
  117. package/dist/components/select/index.js +76 -25
  118. package/dist/components/select/index.js.map +1 -1
  119. package/dist/components/select/style.js +1 -1
  120. package/dist/components/select/style.js.map +1 -1
  121. package/dist/components/slider/index.d.ts +15 -0
  122. package/dist/components/slider/index.js +175 -39
  123. package/dist/components/slider/index.js.map +1 -1
  124. package/dist/components/slider/style.js +1 -1
  125. package/dist/components/slider/style.js.map +1 -1
  126. package/dist/components/spinner/index.d.ts +15 -0
  127. package/dist/components/spinner/index.js +79 -22
  128. package/dist/components/spinner/index.js.map +1 -1
  129. package/dist/components/spinner/style.js +1 -1
  130. package/dist/components/spinner/style.js.map +1 -1
  131. package/dist/components/svg-draw-lines/index.js +48 -12
  132. package/dist/components/svg-draw-lines/index.js.map +1 -1
  133. package/dist/components/switch/index.d.ts +11 -3
  134. package/dist/components/switch/index.js +57 -53
  135. package/dist/components/switch/index.js.map +1 -1
  136. package/dist/components/switch/style.js +1 -1
  137. package/dist/components/switch/style.js.map +1 -1
  138. package/dist/components/textarea/index.js +2 -2
  139. package/dist/components/textarea/index.js.map +1 -1
  140. package/dist/components/textarea/style.js +1 -1
  141. package/dist/components/textarea/style.js.map +1 -1
  142. package/dist/components/theme/index.d.ts +1 -0
  143. package/dist/components/theme/index.js +26 -15
  144. package/dist/components/theme/index.js.map +1 -1
  145. package/dist/components/theme/style.js +1 -1
  146. package/dist/components/theme/style.js.map +1 -1
  147. package/dist/components/toast/style.js +1 -1
  148. package/dist/components/toast/style.js.map +1 -1
  149. package/dist/components/toast/toast.js +1 -1
  150. package/dist/components/toast/toast.js.map +1 -1
  151. package/dist/components/tooltip/index.d.ts +1 -0
  152. package/dist/components/tooltip/index.js +10 -5
  153. package/dist/components/tooltip/index.js.map +1 -1
  154. package/dist/icons/generated/andrsrxn-inbox.d.ts +6 -0
  155. package/dist/icons/generated/andrsrxn-inbox.js +10 -0
  156. package/dist/icons/generated/andrsrxn-inbox.js.map +1 -0
  157. package/dist/icons/index.d.ts +1 -0
  158. package/dist/icons/index.js +75 -74
  159. package/dist/shared/focus/pointer-focus.js +12 -10
  160. package/dist/shared/focus/pointer-focus.js.map +1 -1
  161. package/dist/shared/gesture/drag-gesture.js +4 -4
  162. package/dist/shared/gesture/drag-gesture.js.map +1 -1
  163. package/dist/shared/group-management/index.js +43 -18
  164. package/dist/shared/group-management/index.js.map +1 -1
  165. package/dist/shared/menu-portal/menu-portal.js +1 -1
  166. package/dist/shared/menu-portal/menu-portal.js.map +1 -1
  167. package/dist/shared/overlay/overlay-root.js +1 -1
  168. package/dist/shared/overlay/overlay-root.js.map +1 -1
  169. package/dist/shared/overlay/overlay.js +7 -1
  170. package/dist/shared/overlay/overlay.js.map +1 -1
  171. package/package.json +5 -1
  172. package/dist/assets/glass.js +0 -6
  173. package/dist/assets/glass.js.map +0 -1
package/README.CN.md CHANGED
@@ -60,6 +60,70 @@ import { OverlayScrollbarsComponent } from 'overlayscrollbars-vue' // 或 overla
60
60
  声明它会让引擎放弃平台的覆盖式 scrollbar、固定占一条布局宽度,而组件内部 scrollbar 正是不能占宽度。
61
61
  导入本样式表,组件内部 scrollbar 才会跟随外观,与整页 scrollbar 同一机制。
62
62
 
63
+ ## Classes
64
+
65
+ `@greypan/web-ui/classes.css` 汇总可复用的公共 class,组件内部与消费方共用同一份源码:
66
+ `.wui-glass`(毛玻璃表面)与 `.wui-glow`(装饰性背景晕染)。
67
+
68
+ ```js
69
+ import '@greypan/web-ui/classes.css'
70
+ ```
71
+
72
+ ### .wui-glass
73
+
74
+ 给任意容器加 `.wui-glass` 即可获得毛玻璃表面;组件内部(drawer body、dialog / toast、tooltip /
75
+ popover、input 等)用的也是同一份样式。
76
+
77
+ ```html
78
+ <div class="wui-glass">...</div>
79
+ ```
80
+
81
+ - 同时提供半透明底色、`backdrop-filter`(模糊 / 饱和 / 亮度)、元素自身的 inset 高光与投影,
82
+ 以及用 `::before` 挖出的 1px 描边环:环的左上 / 右下受光、右上 / 左下背光,半径由
83
+ `--wui-glass-corner-radius` 控制。
84
+ - 容器需要有确定的盒子尺寸和可被采样的背景。`.wui-glass` 只在宿主没有其他 `position` 时补
85
+ `position: relative`,并移除自身 border(描边由环承担);圆角仍跟随宿主的 `border-radius`。
86
+ - 非 pill 表面应把 `--wui-glass-corner-radius` 覆盖成与自身圆角同源的值,否则角部光影半径与
87
+ 盒形不匹配。颜色 token 由主题给出浅 / 深两套默认值,优先覆盖 token 而不是重写 `backdrop-filter`。
88
+
89
+ | token | 默认值 | 说明 |
90
+ | ------------------------------ | ------------------------- | ---------------------------------------------------- |
91
+ | `--wui-color-surface-glass` | `rgb(250 250 250 / 0.34)` | 半透明底色;深色主题为 `rgb(44 44 46 / 0.42)` |
92
+ | `--wui-glass-blur` | `4px` | `backdrop-filter` 模糊半径;设 `0px` 关闭 |
93
+ | `--wui-glass-saturation` | `160%` | 背景饱和度 |
94
+ | `--wui-glass-brightness` | `1.06` | 背景亮度;深色主题为 `1.02` |
95
+ | `--wui-color-glass-highlight` | `rgb(255 255 255 / 0.9)` | 元素 inset 高光;深色主题为 `rgb(255 255 255 / 0.1)` |
96
+ | `--wui-glass-corner-radius` | `32px` | 描边环径向光影的半径 |
97
+ | `--wui-color-glass-ring` | `rgb(0 0 0 / 0.05)` | 1px 环底色;深色主题为 `rgb(255 255 255 / 0.05)` |
98
+ | `--wui-color-glass-ring-sheen` | `rgb(255 255 255 / 0.5)` | 受光角光泽;深色主题为 `rgb(255 255 255 / 0.2)` |
99
+ | `--wui-color-glass-ring-shade` | `rgb(0 0 0 / 0.03)` | 背光角压暗;深色主题为 `rgb(0 0 0 / 0.5)` |
100
+ | `--wui-shadow-glass` | 四层扩散阴影 | 元素投影;未设置时使用类内默认值 |
101
+
102
+ ### .wui-glow
103
+
104
+ 给任意容器加 `.wui-glow` 即可,不需要额外组件或 DOM;`web-ui-layout` 的 `header-glow` 与
105
+ collapse 的 peek 边缘用的也是同一份样式。
106
+
107
+ ```html
108
+ <div class="wui-glow">...</div>
109
+ ```
110
+
111
+ - 渐变与模糊都是容器的背景层,内容始终位于其上方。容器需要有确定的盒子尺寸;`.wui-glow` 只在
112
+ 宿主没有其他 `position` 时补 `position: relative`,并加 `isolation: isolate`。
113
+ - 默认从容器顶部向下铺 `--wui-color-page` 渐变,再叠 `blur(4px)`;模糊层向下探出 24px 后收尾。
114
+ 容器或祖先的 `overflow: hidden` 会裁掉这段尾巴。
115
+ - 宿主是不透明背景时,渐变会叠在它上方,但模糊采样不到宿主背后的内容——只会模糊自身的纯色背景。
116
+ 想要毛玻璃观感,宿主表面应保持半透明。
117
+
118
+ | token | 默认值 | 说明 |
119
+ | ------------------------------- | -------------------------------------- | ------------------------------------------- |
120
+ | `--wui-glow-color` | `var(--wui-color-page, #fff)` | 渐变起点色 |
121
+ | `--wui-glow-gradient-direction` | `to bottom` | `linear-gradient` 的方向 |
122
+ | `--wui-glow-gradient` | 由起点色与方向生成的 `linear-gradient` | 完整 background-image;设 `none` 关闭渐变 |
123
+ | `--wui-glow-blur` | `4px` | `backdrop-filter` 模糊半径;设 `0px` 关闭 |
124
+ | `--wui-glow-overhang` | `24px` | 模糊层向容器下方探出的高度 |
125
+ | `--wui-glow-fade` | `36px` | 从全强度到透明的衰减总长,应不小于 overhang |
126
+
63
127
  ## Vite
64
128
 
65
129
  `@greypan/web-ui/vite` 导出 `webUiOptimizeDeps`,即需要并进应用 `optimizeDeps.include` 的 web-ui 运行时
@@ -305,44 +369,44 @@ dropdown、tooltip)不需要它。
305
369
 
306
370
  每个组件都有自己的文档页,API 参考、行为说明和可交互示例都在 [`docs/`](./docs/) 下。下面的表只是索引。
307
371
 
308
- | 分类 | 组件 |
309
- | --------------------- | ----------------------------------------------------------- |
310
- | **表单控件** | [`<web-ui-input>`](./docs/input.md) |
311
- | | [`<web-ui-textarea>`](./docs/textarea.md) |
312
- | | [`<web-ui-editable-text>`](./docs/editable-text.md) |
313
- | | [`<web-ui-input-number>`](./docs/input-number.md) |
314
- | | [`<web-ui-select>`](./docs/select.md) |
315
- | | [`<web-ui-autocomplete>`](./docs/autocomplete.md) |
316
- | | [`<web-ui-slider>`](./docs/slider.md) |
317
- | | [`<web-ui-checkbox>`](./docs/checkbox.md) |
318
- | | [`<web-ui-radio>`](./docs/radio.md) |
319
- | | [`<web-ui-switch>`](./docs/switch.md) |
320
- | | [`<web-ui-segmented>`](./docs/segmented.md) |
321
- | | [`<web-ui-checkbox-group>`](./docs/checkbox-group.md) |
322
- | | [`<web-ui-radio-group>`](./docs/radio-group.md) |
323
- | **button** | [`<web-ui-button>`](./docs/button.md) |
324
- | | [`<web-ui-button-group>`](./docs/button-group.md) |
325
- | **overlay / 模态** | [`<web-ui-dialog>`](./docs/dialog.md) |
326
- | | [`<web-ui-drawer>`](./docs/drawer.md) |
327
- | | [`imagePreview()`](./docs/image-preview.md) |
328
- | **文档流 Disclosure** | [`<web-ui-collapse>`](./docs/collapse.md) |
329
- | **浮动** | [`<web-ui-popover>`](./docs/popover.md) |
330
- | | [`<web-ui-tooltip>`](./docs/tooltip.md) |
331
- | | [`<web-ui-context-menu>`](./docs/context-menu.md) |
332
- | **menu** | [`<web-ui-dropdown>`](./docs/dropdown.md) |
333
- | | [`<web-ui-dropdown-item>`](./docs/dropdown-item.md) |
334
- | | [`<web-ui-dropdown-divider>`](./docs/dropdown-divider.md) |
335
- | | [`<web-ui-dropdown-header>`](./docs/dropdown-header.md) |
336
- | **数据展示** | [`<web-ui-avatar>`](./docs/avatar.md) |
337
- | | [`<web-ui-badge>`](./docs/badge.md) |
338
- | | [`<web-ui-empty>`](./docs/empty.md) |
339
- | | [`<web-ui-middle-ellipsis>`](./docs/middle-ellipsis.md) |
340
- | | [`<web-ui-icon>`](./docs/icon.md) |
341
- | | [`<web-ui-spinner>`](./docs/spinner.md) |
342
- | **布局与工具** | [`<web-ui-layout>`](./docs/layout.md) |
343
- | | [`<web-ui-back-top>`](./docs/back-top.md) |
344
- | | [`<web-ui-svg-draw-lines>`](./docs/svg-draw-lines.md) |
345
- | | [`<web-ui-theme>`](./docs/theme.md) |
346
- | **通知** | [`<web-ui-toast>`](./docs/toast.md) |
347
- | **子项** | [`<web-ui-option>`](./docs/option.md) |
348
- | | [`<web-ui-segmented-trigger>`](./docs/segmented-trigger.md) |
372
+ | 分类 | 组件 |
373
+ | --------------------- | -------------------------------------------------------------- |
374
+ | **表单控件** | [`<web-ui-input>`](./docs/input.CN.md) |
375
+ | | [`<web-ui-textarea>`](./docs/textarea.CN.md) |
376
+ | | [`<web-ui-editable-text>`](./docs/editable-text.CN.md) |
377
+ | | [`<web-ui-input-number>`](./docs/input-number.CN.md) |
378
+ | | [`<web-ui-select>`](./docs/select.CN.md) |
379
+ | | [`<web-ui-autocomplete>`](./docs/autocomplete.CN.md) |
380
+ | | [`<web-ui-slider>`](./docs/slider.CN.md) |
381
+ | | [`<web-ui-checkbox>`](./docs/checkbox.CN.md) |
382
+ | | [`<web-ui-radio>`](./docs/radio.CN.md) |
383
+ | | [`<web-ui-switch>`](./docs/switch.CN.md) |
384
+ | | [`<web-ui-segmented>`](./docs/segmented.CN.md) |
385
+ | | [`<web-ui-checkbox-group>`](./docs/checkbox-group.CN.md) |
386
+ | | [`<web-ui-radio-group>`](./docs/radio-group.CN.md) |
387
+ | **button** | [`<web-ui-button>`](./docs/button.CN.md) |
388
+ | | [`<web-ui-button-group>`](./docs/button-group.CN.md) |
389
+ | **overlay / 模态** | [`<web-ui-dialog>`](./docs/dialog.CN.md) |
390
+ | | [`<web-ui-drawer>`](./docs/drawer.CN.md) |
391
+ | | [`imagePreview()`](./docs/image-preview.CN.md) |
392
+ | **文档流 Disclosure** | [`<web-ui-collapse>`](./docs/collapse.CN.md) |
393
+ | **浮动** | [`<web-ui-popover>`](./docs/popover.CN.md) |
394
+ | | [`<web-ui-tooltip>`](./docs/tooltip.CN.md) |
395
+ | | [`<web-ui-context-menu>`](./docs/context-menu.CN.md) |
396
+ | **menu** | [`<web-ui-dropdown>`](./docs/dropdown.CN.md) |
397
+ | | [`<web-ui-dropdown-item>`](./docs/dropdown-item.CN.md) |
398
+ | | [`<web-ui-dropdown-divider>`](./docs/dropdown-divider.CN.md) |
399
+ | | [`<web-ui-dropdown-header>`](./docs/dropdown-header.CN.md) |
400
+ | **数据展示** | [`<web-ui-avatar>`](./docs/avatar.CN.md) |
401
+ | | [`<web-ui-badge>`](./docs/badge.CN.md) |
402
+ | | [`<web-ui-empty>`](./docs/empty.CN.md) |
403
+ | | [`<web-ui-middle-ellipsis>`](./docs/middle-ellipsis.CN.md) |
404
+ | | [`<web-ui-icon>`](./docs/icon.CN.md) |
405
+ | | [`<web-ui-spinner>`](./docs/spinner.CN.md) |
406
+ | **布局与工具** | [`<web-ui-layout>`](./docs/layout.CN.md) |
407
+ | | [`<web-ui-back-top>`](./docs/back-top.CN.md) |
408
+ | | [`<web-ui-svg-draw-lines>`](./docs/svg-draw-lines.CN.md) |
409
+ | | [`<web-ui-theme>`](./docs/theme.CN.md) |
410
+ | **通知** | [`<web-ui-toast>`](./docs/toast.CN.md) |
411
+ | **子项** | [`<web-ui-option>`](./docs/option.CN.md) |
412
+ | | [`<web-ui-segmented-trigger>`](./docs/segmented-trigger.CN.md) |
package/README.md CHANGED
@@ -64,6 +64,75 @@ import { OverlayScrollbarsComponent } from 'overlayscrollbars-react' // or overl
64
64
  not do. Import this stylesheet for the internal scrollbars to follow the appearance, just like the
65
65
  page-level ones.
66
66
 
67
+ ## Classes
68
+
69
+ `@greypan/web-ui/classes.css` bundles the reusable public classes shared by components and consumers:
70
+ `.wui-glass` (frosted surface) and `.wui-glow` (decorative backdrop).
71
+
72
+ ```js
73
+ import '@greypan/web-ui/classes.css'
74
+ ```
75
+
76
+ ### .wui-glass
77
+
78
+ Add `.wui-glass` to any container for a frosted surface. Components use the same stylesheet internally
79
+ (drawer body, dialog / toast, tooltip / popover, input, and so on).
80
+
81
+ ```html
82
+ <div class="wui-glass">...</div>
83
+ ```
84
+
85
+ - It provides the translucent background, the `backdrop-filter` (blur / saturation / brightness), the
86
+ element's own inset highlight and shadow, and a 1px ring cut out with `::before`: the ring lights the
87
+ top-left / bottom-right corners and shades the top-right / bottom-left ones, with the radius controlled
88
+ by `--wui-glass-corner-radius`.
89
+ - The container needs a box size and a background that can be sampled. `.wui-glass` only adds
90
+ `position: relative` when the host has no other `position`, and removes the host's own border (the ring
91
+ replaces it); the corner radius still follows the host's `border-radius`.
92
+ - For non-pill surfaces, override `--wui-glass-corner-radius` with a value derived from the element's own
93
+ radius, otherwise the corner lighting will not match the box shape. Colours come from the theme with
94
+ separate light / dark defaults; prefer overriding tokens over rewriting `backdrop-filter`.
95
+
96
+ | token | default | description |
97
+ | ------------------------------ | ------------------------- | ---------------------------------------------------- |
98
+ | `--wui-color-surface-glass` | `rgb(250 250 250 / 0.34)` | Translucent surface; `rgb(44 44 46 / 0.42)` in dark |
99
+ | `--wui-glass-blur` | `4px` | `backdrop-filter` radius; set `0px` to disable |
100
+ | `--wui-glass-saturation` | `160%` | Backdrop saturation |
101
+ | `--wui-glass-brightness` | `1.06` | Backdrop brightness; `1.02` in dark |
102
+ | `--wui-color-glass-highlight` | `rgb(255 255 255 / 0.9)` | Inset highlight; `rgb(255 255 255 / 0.1)` in dark |
103
+ | `--wui-glass-corner-radius` | `32px` | Radius of the ring's corner lighting |
104
+ | `--wui-color-glass-ring` | `rgb(0 0 0 / 0.05)` | 1px ring base; `rgb(255 255 255 / 0.05)` in dark |
105
+ | `--wui-color-glass-ring-sheen` | `rgb(255 255 255 / 0.5)` | Lit-corner sheen; `rgb(255 255 255 / 0.2)` in dark |
106
+ | `--wui-color-glass-ring-shade` | `rgb(0 0 0 / 0.03)` | Unlit-corner shade; `rgb(0 0 0 / 0.5)` in dark |
107
+ | `--wui-shadow-glass` | four-layer diffuse shadow | Element shadow; the class default applies when unset |
108
+
109
+ ### .wui-glow
110
+
111
+ Add `.wui-glow` to any container; no extra component or DOM is needed. `web-ui-layout`'s
112
+ `header-glow` and collapse's peek edge use the same stylesheet.
113
+
114
+ ```html
115
+ <div class="wui-glow">...</div>
116
+ ```
117
+
118
+ - Both the gradient and the blur are background layers; content always sits above them. The container
119
+ needs a box size. `.wui-glow` only adds `position: relative` when the host has no other `position`, and
120
+ adds `isolation: isolate`.
121
+ - By default it lays a `--wui-color-page` gradient from the top edge down, then overlays `blur(4px)`; the
122
+ blurred layer extends 24px below the container before fading out. `overflow: hidden` on the container or
123
+ an ancestor clips that tail.
124
+ - On an opaque host the gradient still shows above its background, but the blur cannot sample what is behind
125
+ the host — it only blurs the host's own flat colour. Keep the host surface translucent for a frosted look.
126
+
127
+ | token | default | description |
128
+ | ------------------------------- | ----------------------------------------------- | ---------------------------------------------------- |
129
+ | `--wui-glow-color` | `var(--wui-color-page, #fff)` | Gradient start colour |
130
+ | `--wui-glow-gradient-direction` | `to bottom` | `linear-gradient` direction |
131
+ | `--wui-glow-gradient` | `linear-gradient` built from colour + direction | Full background-image; set `none` to disable |
132
+ | `--wui-glow-blur` | `4px` | `backdrop-filter` radius; set `0px` to disable |
133
+ | `--wui-glow-overhang` | `24px` | How far the blurred layer extends below the box |
134
+ | `--wui-glow-fade` | `36px` | Full-strength-to-transparent length; keep ≥ overhang |
135
+
67
136
  ## Vite
68
137
 
69
138
  `@greypan/web-ui/vite` exports `webUiOptimizeDeps`, the list of web-ui runtime dependencies an app has to merge
@@ -1,5 +1,5 @@
1
1
  //#region src/assets/overlay-motion.css?inline
2
- var e = ".wui-floating-panel{transform-origin:var(--wui-internal-overlay-transform-origin,center);opacity:1;-webkit-backdrop-filter:blur(4px) saturate(160%) brightness(var(--wui-glass-brightness,1.06));backdrop-filter:blur(4px) saturate(160%) brightness(var(--wui-glass-brightness,1.06));transition:opacity var(--wui-internal-duration-float-enter,var(--wui-duration-float-enter,.24s)) var(--wui-ease-float,cubic-bezier(.4, .38, .2, 1)), -webkit-backdrop-filter var(--wui-internal-duration-float-enter,var(--wui-duration-float-enter,.24s)) var(--wui-ease-float,cubic-bezier(.4, .38, .2, 1)), backdrop-filter var(--wui-internal-duration-float-enter,var(--wui-duration-float-enter,.24s)) var(--wui-ease-float,cubic-bezier(.4, .38, .2, 1)), transform var(--wui-internal-duration-float-enter,var(--wui-duration-float-enter,.24s)) var(--wui-ease-float,cubic-bezier(.4, .38, .2, 1));transform:scale(1)}.wui-floating-panel[data-wui-presence=entering],.wui-floating-panel[data-wui-presence=closing]{transform:scale(var(--wui-scale-enter,.9));opacity:0;-webkit-backdrop-filter:blur(0) saturate(160%) brightness(var(--wui-glass-brightness,1.06));backdrop-filter:blur(0) saturate(160%) brightness(var(--wui-glass-brightness,1.06))}.wui-floating-panel[data-wui-presence=closing]{pointer-events:none;transition-duration:var(--wui-internal-duration-float-exit,var(--wui-duration-float-exit,.16s))}@media (prefers-reduced-motion:reduce){.wui-floating-panel,.wui-floating-panel[data-wui-presence=entering],.wui-floating-panel[data-wui-presence=closing]{transition-duration:.12s;transform:none}}";
2
+ var e = ".wui-floating-panel{transform-origin:var(--wui-internal-overlay-transform-origin,center);opacity:1;-webkit-backdrop-filter:blur(var(--wui-glass-blur,4px)) saturate(var(--wui-glass-saturation,160%)) brightness(var(--wui-glass-brightness,1.06));backdrop-filter:blur(var(--wui-glass-blur,4px)) saturate(var(--wui-glass-saturation,160%)) brightness(var(--wui-glass-brightness,1.06));transition:opacity var(--wui-internal-duration-float-enter,var(--wui-duration-float-enter,.24s)) var(--wui-ease-float,cubic-bezier(.4, .38, .2, 1)), -webkit-backdrop-filter var(--wui-internal-duration-float-enter,var(--wui-duration-float-enter,.24s)) var(--wui-ease-float,cubic-bezier(.4, .38, .2, 1)), backdrop-filter var(--wui-internal-duration-float-enter,var(--wui-duration-float-enter,.24s)) var(--wui-ease-float,cubic-bezier(.4, .38, .2, 1)), transform var(--wui-internal-duration-float-enter,var(--wui-duration-float-enter,.24s)) var(--wui-ease-float,cubic-bezier(.4, .38, .2, 1));transform:scale(1)}.wui-floating-panel[data-wui-presence=entering],.wui-floating-panel[data-wui-presence=closing]{transform:scale(var(--wui-scale-enter,.9));opacity:0;-webkit-backdrop-filter:blur(0) saturate(var(--wui-glass-saturation,160%)) brightness(var(--wui-glass-brightness,1.06));backdrop-filter:blur(0) saturate(var(--wui-glass-saturation,160%)) brightness(var(--wui-glass-brightness,1.06))}.wui-floating-panel[data-wui-presence=closing]{pointer-events:none;transition-duration:var(--wui-internal-duration-float-exit,var(--wui-duration-float-exit,.16s))}@media (prefers-reduced-motion:reduce){.wui-floating-panel,.wui-floating-panel[data-wui-presence=entering],.wui-floating-panel[data-wui-presence=closing]{transition-duration:.12s;transform:none}}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"overlay-motion.js","names":[],"sources":["../../src/assets/overlay-motion.css?inline"],"sourcesContent":["/*\n * overlay panel 共享动效(单层玻璃)。\n *\n * backdrop-filter 的 blur(0px) ↔ blur(4px) 在 Chrome/Safari 均平滑插值(Manager\n * 双引擎实测,Safari 逐帧采样 blur 值连续),当年「生硬跳变」的根因是 none↔blur\n * 不可插值,而不是 backdrop-filter 不可动画。因此 panel 自身直接承担玻璃:背景、\n * 阴影与 backdrop-filter 都在 panel 元素上(wui-glass 类也回 panel),打开/关闭时\n * opacity 与 backdrop-filter 同步过渡,无需双层结构——双层带来的全部层间问题\n * (变白、关闭残影、双重模糊、阴影作用域承接)一并消失。\n */\n\n.wui-floating-panel {\n transform-origin: var(--wui-internal-overlay-transform-origin, center);\n transform: scale(1);\n\n opacity: 1;\n\n /* 玻璃由 panel 自身承担(wui-glass 提供背景/描边/阴影)。打开目标态 blur(4px),\n entering/closing 切到 blur(0px) 作为插值起点——blur(0px) 与 blur(4px) 平滑过渡\n (none↔blur 不可插值才是当年生硬跳变的根因)。 */\n backdrop-filter: blur(4px) saturate(160%) brightness(var(--wui-glass-brightness, 1.06));\n\n /* transition 列表需手写 -webkit-backdrop-filter:lightningcss 不会在 transition\n value 内追加 -webkit 项,iOS 16.4 上 blur 过渡依赖它(属性前缀会自动生成)。\n 缓动取 --wui-ease-float:--wui-ease-enter 会在开局就冲到 85% 进度,锚定 panel 的展开\n 需要可读的过程。 */\n transition:\n opacity var(--wui-internal-duration-float-enter, var(--wui-duration-float-enter, 240ms))\n var(--wui-ease-float, cubic-bezier(0.4, 0.38, 0.2, 1)),\n -webkit-backdrop-filter var(--wui-internal-duration-float-enter, var(--wui-duration-float-enter, 240ms))\n var(--wui-ease-float, cubic-bezier(0.4, 0.38, 0.2, 1)),\n backdrop-filter var(--wui-internal-duration-float-enter, var(--wui-duration-float-enter, 240ms))\n var(--wui-ease-float, cubic-bezier(0.4, 0.38, 0.2, 1)),\n transform var(--wui-internal-duration-float-enter, var(--wui-duration-float-enter, 240ms))\n var(--wui-ease-float, cubic-bezier(0.4, 0.38, 0.2, 1));\n}\n\n.wui-floating-panel[data-wui-presence='entering'],\n.wui-floating-panel[data-wui-presence='closing'] {\n transform: scale(var(--wui-scale-enter, 0.9));\n opacity: 0;\n backdrop-filter: blur(0) saturate(160%) brightness(var(--wui-glass-brightness, 1.06));\n}\n\n.wui-floating-panel[data-wui-presence='closing'] {\n pointer-events: none;\n transition-duration: var(--wui-internal-duration-float-exit, var(--wui-duration-float-exit, 160ms));\n}\n\n@media (prefers-reduced-motion: reduce) {\n .wui-floating-panel,\n .wui-floating-panel[data-wui-presence='entering'],\n .wui-floating-panel[data-wui-presence='closing'] {\n transform: none;\n transition-duration: 120ms;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"overlay-motion.js","names":[],"sources":["../../src/assets/overlay-motion.css?inline"],"sourcesContent":["/*\n * overlay panel 共享动效(单层玻璃)。\n *\n * backdrop-filter 的 blur(0px) ↔ blur(4px) 在 Chrome/Safari 均平滑插值(Manager\n * 双引擎实测,Safari 逐帧采样 blur 值连续),当年「生硬跳变」的根因是 none↔blur\n * 不可插值,而不是 backdrop-filter 不可动画。因此 panel 自身直接承担玻璃:背景、\n * 阴影与 backdrop-filter 都在 panel 元素上(wui-glass 类也回 panel),打开/关闭时\n * opacity 与 backdrop-filter 同步过渡,无需双层结构——双层带来的全部层间问题\n * (变白、关闭残影、双重模糊、阴影作用域承接)一并消失。\n */\n\n.wui-floating-panel {\n transform-origin: var(--wui-internal-overlay-transform-origin, center);\n transform: scale(1);\n\n opacity: 1;\n\n /* 玻璃由 panel 自身承担(wui-glass 提供背景/描边/阴影)。打开目标态 blur(4px),\n entering/closing 切到 blur(0px) 作为插值起点——blur(0px) 与 blur(4px) 平滑过渡\n (none↔blur 不可插值才是当年生硬跳变的根因)。 */\n backdrop-filter: blur(var(--wui-glass-blur, 4px)) saturate(var(--wui-glass-saturation, 160%))\n brightness(var(--wui-glass-brightness, 1.06));\n\n /* transition 列表需手写 -webkit-backdrop-filter:lightningcss 不会在 transition\n value 内追加 -webkit 项,iOS 16.4 上 blur 过渡依赖它(属性前缀会自动生成)。\n 缓动取 --wui-ease-float:--wui-ease-enter 会在开局就冲到 85% 进度,锚定 panel 的展开\n 需要可读的过程。 */\n transition:\n opacity var(--wui-internal-duration-float-enter, var(--wui-duration-float-enter, 240ms))\n var(--wui-ease-float, cubic-bezier(0.4, 0.38, 0.2, 1)),\n -webkit-backdrop-filter var(--wui-internal-duration-float-enter, var(--wui-duration-float-enter, 240ms))\n var(--wui-ease-float, cubic-bezier(0.4, 0.38, 0.2, 1)),\n backdrop-filter var(--wui-internal-duration-float-enter, var(--wui-duration-float-enter, 240ms))\n var(--wui-ease-float, cubic-bezier(0.4, 0.38, 0.2, 1)),\n transform var(--wui-internal-duration-float-enter, var(--wui-duration-float-enter, 240ms))\n var(--wui-ease-float, cubic-bezier(0.4, 0.38, 0.2, 1));\n}\n\n.wui-floating-panel[data-wui-presence='entering'],\n.wui-floating-panel[data-wui-presence='closing'] {\n transform: scale(var(--wui-scale-enter, 0.9));\n opacity: 0;\n backdrop-filter: blur(0) saturate(var(--wui-glass-saturation, 160%)) brightness(var(--wui-glass-brightness, 1.06));\n}\n\n.wui-floating-panel[data-wui-presence='closing'] {\n pointer-events: none;\n transition-duration: var(--wui-internal-duration-float-exit, var(--wui-duration-float-exit, 160ms));\n}\n\n@media (prefers-reduced-motion: reduce) {\n .wui-floating-panel,\n .wui-floating-panel[data-wui-presence='entering'],\n .wui-floating-panel[data-wui-presence='closing'] {\n transform: none;\n transition-duration: 120ms;\n }\n}\n"],"mappings":""}
@@ -1,5 +1,5 @@
1
1
  //#region src/assets/selection-control.css?inline
2
- var e = "*,:before,:after{box-sizing:border-box}:host{cursor:default;vertical-align:middle;align-items:center;display:inline-flex}:host([disabled]){cursor:not-allowed}:where(.wui-radio,.wui-checkbox){cursor:inherit;-webkit-user-select:none;user-select:none;font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);align-items:center;gap:10px;line-height:1;display:inline-flex}:where(.wui-radio,.wui-checkbox):focus-visible{border-radius:var(--wui-radius-control,calc(infinity * 1px));outline:var(--wui-focus-ring-width,3px) solid var(--wui-color-focus-ring,color-mix(in srgb, var(--wui-color-accent,#08f) 40%, transparent));outline-offset:2px}:where(.wui-radio.is-label-empty,.wui-checkbox.is-label-empty){gap:0}:host([data-wui-pointer-focus]) :where(.wui-radio,.wui-checkbox):focus-visible{outline-color:#0000}:where(.wui-radio.is-disabled,.wui-checkbox.is-disabled){cursor:not-allowed;color:var(--wui-color-text-secondary,#6a6a6a);opacity:.4}:where(.wui-radio-circle,.wui-checkbox-box){isolation:isolate;width:var(--wui-selection-control-size,18px);height:var(--wui-selection-control-size,18px);background-color:var(--wui-color-surface-control,#dfdfdf);transition:background-color var(--wui-duration-trigger,.16s) ease;flex-shrink:0;justify-content:center;align-items:center;display:flex;position:relative}:where(.wui-radio-circle,.wui-checkbox-box):before{content:\"\";z-index:-1;border-radius:inherit;position:absolute;inset:0}:where(.wui-radio-label,.wui-checkbox-label){pointer-events:none}@media (hover:hover) and (pointer:fine){:where(.wui-radio,.wui-checkbox):hover:not(.is-disabled,.is-checked) :where(.wui-radio-circle,.wui-checkbox-box):before{background-color:color-mix(in srgb, var(--wui-color-text,#1b1b1b) 6%, transparent)}}";
2
+ var e = "*,:before,:after{box-sizing:border-box}:host{cursor:default;vertical-align:middle;align-items:center;display:inline-flex}:host([disabled]){cursor:not-allowed}:where(.wui-radio,.wui-checkbox){cursor:inherit;-webkit-user-select:none;user-select:none;font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);align-items:center;gap:10px;line-height:1;display:inline-flex}:where(.wui-radio,.wui-checkbox):focus-visible{border-radius:var(--wui-radius-control,calc(infinity * 1px));outline:var(--wui-focus-ring-width,3px) solid var(--wui-color-focus-ring,color-mix(in srgb, var(--wui-color-accent,#0071e3) 40%, transparent));outline-offset:2px}:where(.wui-radio.is-label-empty,.wui-checkbox.is-label-empty){gap:0}:host([data-wui-pointer-focus]) :where(.wui-radio,.wui-checkbox):focus-visible{outline-color:#0000}:where(.wui-radio.is-disabled,.wui-checkbox.is-disabled){cursor:not-allowed;color:var(--wui-color-text-secondary,#6a6a6a);opacity:.4}:where(.wui-radio-circle,.wui-checkbox-box){isolation:isolate;width:var(--wui-selection-control-size,18px);height:var(--wui-selection-control-size,18px);background-color:var(--wui-color-surface-control,#dfdfdf);transition:background-color var(--wui-duration-trigger,.16s) ease;flex-shrink:0;justify-content:center;align-items:center;display:flex;position:relative}:where(.wui-radio-circle,.wui-checkbox-box):before{content:\"\";z-index:-1;border-radius:inherit;position:absolute;inset:0}:where(.wui-radio-label,.wui-checkbox-label){pointer-events:none}@media (hover:hover) and (pointer:fine){:where(.wui-radio,.wui-checkbox):hover:not(.is-disabled,.is-checked) :where(.wui-radio-circle,.wui-checkbox-box):before{background-color:color-mix(in srgb, var(--wui-color-text,#1b1b1b) 6%, transparent)}}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"selection-control.js","names":[],"sources":["../../src/assets/selection-control.css?inline"],"sourcesContent":["/*\n * radio / checkbox 共享外壳。\n *\n * indicator 只有 18px。宿主一旦走 inline-block,页面行高会在宿主内生成匿名行盒,把盒子撑到\n * 行高(Tailwind preflight 下是 24px),且多出的空隙落在哪一侧取决于内部基线落点,radio 与\n * checkbox 因此一个贴顶一个贴底。改成 inline-flex 后宿主高度由内容决定,行高不再参与,\n * 与相邻文字的对齐交给 vertical-align 显式声明。\n */\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n cursor: default;\n display: inline-flex;\n align-items: center;\n vertical-align: middle;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n}\n\n/* :where() 保持零特异性,组件级配置(圆角、选中色、内部图形)始终覆盖共享层。 */\n:where(.wui-radio, .wui-checkbox) {\n cursor: inherit;\n user-select: none;\n\n display: inline-flex;\n gap: 10px;\n align-items: center;\n\n font-size: var(--wui-font-size, 14px);\n line-height: 1;\n color: var(--wui-color-text, #1b1b1b);\n\n &:focus-visible {\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n outline: var(--wui-focus-ring-width, 3px) solid\n var(--wui-color-focus-ring, color-mix(in srgb, var(--wui-color-accent, #08f) 40%, transparent));\n outline-offset: 2px;\n }\n}\n\n/*\n * label 没有可渲染内容时把间距一并收掉。gap 只认「这里有个 flex item」,不认它有没有宽度,所以空槽位\n * 或只挂了一个视觉隐藏的无障碍名时,indicator 右边照样多出这一段;判据见 LabelEmptinessController。\n */\n:where(.wui-radio.is-label-empty, .wui-checkbox.is-label-empty) {\n gap: 0;\n}\n\n:host([data-wui-pointer-focus]) :where(.wui-radio, .wui-checkbox):focus-visible {\n outline-color: transparent;\n}\n\n:where(.wui-radio.is-disabled, .wui-checkbox.is-disabled) {\n cursor: not-allowed;\n color: var(--wui-color-text-secondary, #6a6a6a);\n opacity: 0.4;\n}\n\n/*\n * indicator 底色取控件档。surface-raised 在深色下(#2c2c2e)离页面底色(#242628)只有 8 个\n * 亮度点,未选中时几乎看不出有一个圆/方框;surface-control 才是主题里「控件自己的底」\n * 那一档(中性 button 同档),深色比页面高 22 个亮度点,浅色下也从近白的 #f2f2f7 变成可读的 #dfdfdf。\n */\n:where(.wui-radio-circle, .wui-checkbox-box) {\n isolation: isolate;\n position: relative;\n\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n\n width: var(--wui-selection-control-size, 18px);\n height: var(--wui-selection-control-size, 18px);\n\n background-color: var(--wui-color-surface-control, #dfdfdf);\n\n transition: background-color var(--wui-duration-trigger, 160ms) ease;\n}\n\n/*\n * 状态层走覆盖层,不占用 indicator 自己的 background-color:同一个属性没法一半过渡一半即时,\n * 而这里要的是 hover 当帧切换、选中态的 accent 按 --wui-duration-trigger 淡入淡出。\n * z-index: -1 配父级 isolation 把覆盖层夹在控件底与圆点/勾之间,半径跟着 indicator 走。\n */\n:where(.wui-radio-circle, .wui-checkbox-box)::before {\n content: '';\n\n position: absolute;\n z-index: -1;\n inset: 0;\n\n border-radius: inherit;\n}\n\n:where(.wui-radio-label, .wui-checkbox-label) {\n pointer-events: none;\n}\n\n/*\n * 未激活态的 hover 状态层。用半透明色而不是混进控件底:覆盖层本就压在不透明的控件底上,\n * 合成结果与 color-mix(text 6%, surface-control) 一致,深色档同样成立。\n * label 文字带 pointer-events: none,命中落回 label 自身,所以整条触发区(含右侧文字)\n * 的 hover 都会作用到 indicator。hover 只在真正的指针设备上生效,避免触屏留态。\n */\n@media (hover: hover) and (pointer: fine) {\n :where(.wui-radio, .wui-checkbox):hover:not(.is-disabled, .is-checked)\n :where(.wui-radio-circle, .wui-checkbox-box)::before {\n background-color: color-mix(in srgb, var(--wui-color-text, #1b1b1b) 6%, transparent);\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"selection-control.js","names":[],"sources":["../../src/assets/selection-control.css?inline"],"sourcesContent":["/*\n * radio / checkbox 共享外壳。\n *\n * indicator 只有 18px。宿主一旦走 inline-block,页面行高会在宿主内生成匿名行盒,把盒子撑到\n * 行高(Tailwind preflight 下是 24px),且多出的空隙落在哪一侧取决于内部基线落点,radio 与\n * checkbox 因此一个贴顶一个贴底。改成 inline-flex 后宿主高度由内容决定,行高不再参与,\n * 与相邻文字的对齐交给 vertical-align 显式声明。\n */\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n cursor: default;\n display: inline-flex;\n align-items: center;\n vertical-align: middle;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n}\n\n/* :where() 保持零特异性,组件级配置(圆角、选中色、内部图形)始终覆盖共享层。 */\n:where(.wui-radio, .wui-checkbox) {\n cursor: inherit;\n user-select: none;\n\n display: inline-flex;\n gap: 10px;\n align-items: center;\n\n font-size: var(--wui-font-size, 14px);\n line-height: 1;\n color: var(--wui-color-text, #1b1b1b);\n\n &:focus-visible {\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n outline: var(--wui-focus-ring-width, 3px) solid\n var(--wui-color-focus-ring, color-mix(in srgb, var(--wui-color-accent, #0071e3) 40%, transparent));\n outline-offset: 2px;\n }\n}\n\n/*\n * label 没有可渲染内容时把间距一并收掉。gap 只认「这里有个 flex item」,不认它有没有宽度,所以空槽位\n * 或只挂了一个视觉隐藏的无障碍名时,indicator 右边照样多出这一段;判据见 LabelEmptinessController。\n */\n:where(.wui-radio.is-label-empty, .wui-checkbox.is-label-empty) {\n gap: 0;\n}\n\n:host([data-wui-pointer-focus]) :where(.wui-radio, .wui-checkbox):focus-visible {\n outline-color: transparent;\n}\n\n:where(.wui-radio.is-disabled, .wui-checkbox.is-disabled) {\n cursor: not-allowed;\n color: var(--wui-color-text-secondary, #6a6a6a);\n opacity: 0.4;\n}\n\n/*\n * indicator 底色取控件档。surface-raised 在深色下(#2c2c2e)离页面底色(#242628)只有 8 个\n * 亮度点,未选中时几乎看不出有一个圆/方框;surface-control 才是主题里「控件自己的底」\n * 那一档(中性 button 同档),深色比页面高 22 个亮度点,浅色下也从近白的 #f2f2f7 变成可读的 #dfdfdf。\n */\n:where(.wui-radio-circle, .wui-checkbox-box) {\n isolation: isolate;\n position: relative;\n\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n\n width: var(--wui-selection-control-size, 18px);\n height: var(--wui-selection-control-size, 18px);\n\n background-color: var(--wui-color-surface-control, #dfdfdf);\n\n transition: background-color var(--wui-duration-trigger, 160ms) ease;\n}\n\n/*\n * 状态层走覆盖层,不占用 indicator 自己的 background-color:同一个属性没法一半过渡一半即时,\n * 而这里要的是 hover 当帧切换、选中态的 accent 按 --wui-duration-trigger 淡入淡出。\n * z-index: -1 配父级 isolation 把覆盖层夹在控件底与圆点/勾之间,半径跟着 indicator 走。\n */\n:where(.wui-radio-circle, .wui-checkbox-box)::before {\n content: '';\n\n position: absolute;\n z-index: -1;\n inset: 0;\n\n border-radius: inherit;\n}\n\n:where(.wui-radio-label, .wui-checkbox-label) {\n pointer-events: none;\n}\n\n/*\n * 未激活态的 hover 状态层。用半透明色而不是混进控件底:覆盖层本就压在不透明的控件底上,\n * 合成结果与 color-mix(text 6%, surface-control) 一致,深色档同样成立。\n * label 文字带 pointer-events: none,命中落回交互元素自身,所以整条触发区(含右侧文字)\n * 的 hover 都会作用到 indicator。hover 只在真正的指针设备上生效,避免触屏留态。\n */\n@media (hover: hover) and (pointer: fine) {\n :where(.wui-radio, .wui-checkbox):hover:not(.is-disabled, .is-checked)\n :where(.wui-radio-circle, .wui-checkbox-box)::before {\n background-color: color-mix(in srgb, var(--wui-color-text, #1b1b1b) 6%, transparent);\n }\n}\n"],"mappings":""}
@@ -0,0 +1,6 @@
1
+ //#region src/classes/glass.css?inline
2
+ var e = ":where(.wui-glass){position:relative}.wui-glass{--wui-internal-glass-shadow:var(--wui-shadow-glass,0 0 1px #00000005, 0 0 8px #00000008, 0 2px 16px #00000006, 0 8px 32px #00000004);background-color:var(--wui-color-surface-glass,#fafafa57);-webkit-backdrop-filter:blur(var(--wui-glass-blur,4px)) saturate(var(--wui-glass-saturation,160%)) brightness(var(--wui-glass-brightness,1.06));backdrop-filter:blur(var(--wui-glass-blur,4px)) saturate(var(--wui-glass-saturation,160%)) brightness(var(--wui-glass-brightness,1.06));box-shadow:inset 0 0 0 .5px color-mix(in srgb, var(--wui-color-glass-highlight,#ffffffe6) 38%, transparent), inset 0 1px .5px color-mix(in srgb, var(--wui-color-glass-highlight,#ffffffe6) 52%, transparent), inset 0 -.5px 1px #00000008, var(--wui-internal-glass-shadow,var(--wui-shadow-glass,0 0 1px #00000005, 0 0 8px #00000008, 0 2px 16px #00000006, 0 8px 32px #00000004));background-clip:padding-box;border:none}.wui-glass:before{pointer-events:none;content:\"\";border-radius:inherit;background-color:var(--wui-color-glass-ring,#0000000d);background-image:radial-gradient(var(--wui-glass-corner-radius,32px) at 100% 0, var(--wui-color-glass-ring-shade,#00000008), transparent), radial-gradient(var(--wui-glass-corner-radius,32px) at 0 100%, var(--wui-color-glass-ring-shade,#00000008), transparent), radial-gradient(calc(var(--wui-glass-corner-radius,32px) * 2) at 0 0, var(--wui-color-glass-ring-sheen,#ffffff80), transparent), radial-gradient(calc(var(--wui-glass-corner-radius,32px) * 2) at 100% 100%, var(--wui-color-glass-ring-sheen,#ffffff80), transparent);-webkit-mask-composite:xor;-webkit-mask-source-type:auto,auto;padding:1px;position:absolute;inset:0;-webkit-mask-image:linear-gradient(#000 0 0),linear-gradient(#000 0 0);mask-image:linear-gradient(#000 0 0),linear-gradient(#000 0 0);-webkit-mask-position:0 0,0 0;mask-position:0 0,0 0;-webkit-mask-size:auto,auto;mask-size:auto,auto;-webkit-mask-repeat:repeat,repeat;mask-repeat:repeat,repeat;-webkit-mask-clip:content-box,border-box;mask-clip:content-box,border-box;-webkit-mask-origin:content-box,border-box;mask-origin:content-box,border-box;-webkit-mask-composite:xor;mask-composite:exclude;-webkit-mask-source-type:auto,auto;mask-mode:match-source,match-source}";
3
+ //#endregion
4
+ export { e as default };
5
+
6
+ //# sourceMappingURL=glass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glass.js","names":[],"sources":["../../src/classes/glass.css?inline"],"sourcesContent":["/* 只在宿主没有其他 position 时补定位,避免盖掉消费方的绝对/固定定位。 */\n:where(.wui-glass) {\n position: relative;\n}\n\n.wui-glass {\n /*\n * 内部阴影变量在 glass 元素上自声明默认值,切断从祖先 glass 容器(drawer/dialog\n * body 等声明了 overlay 阴影的元素)沿 flattened tree 继承进 slotted 内容的\n * 通道;各组件在自身元素上的同变量配置(特异性/源顺序恒胜出)不受影响。\n */\n --wui-internal-glass-shadow: var(\n --wui-shadow-glass,\n 0 0 1px rgb(0 0 0 / 0.02),\n 0 0 8px rgb(0 0 0 / 0.03),\n 0 2px 16px rgb(0 0 0 / 0.025),\n 0 8px 32px rgb(0 0 0 / 0.015)\n );\n\n border: none;\n\n background-color: var(--wui-color-surface-glass, rgb(250 250 250 / 0.34));\n background-clip: padding-box;\n\n /* Safari 16.4 只支持带 -webkit- 前缀的 backdrop-filter(无前缀 18 才支持);\n 前缀由 lightningcss 按 ?inline 管线默认 targets(含 ios16.4)自动生成,无需手写。 */\n backdrop-filter: blur(var(--wui-glass-blur, 4px)) saturate(var(--wui-glass-saturation, 160%))\n brightness(var(--wui-glass-brightness, 1.06));\n box-shadow:\n inset 0 0 0 0.5px color-mix(in srgb, var(--wui-color-glass-highlight, rgb(255 255 255 / 0.9)) 38%, transparent),\n inset 0 1px 0.5px color-mix(in srgb, var(--wui-color-glass-highlight, rgb(255 255 255 / 0.9)) 52%, transparent),\n inset 0 -0.5px 1px rgb(0 0 0 / 0.03),\n var(\n --wui-internal-glass-shadow,\n var(\n --wui-shadow-glass,\n 0 0 1px rgb(0 0 0 / 0.02),\n 0 0 8px rgb(0 0 0 / 0.03),\n 0 2px 16px rgb(0 0 0 / 0.025),\n 0 8px 32px rgb(0 0 0 / 0.015)\n )\n );\n}\n\n/*\n * 玻璃描边环:底色铺满整圈,其上叠两枚 sheen→transparent radial 作左上/右下受光\n * 光泽,再叠两枚 shade→transparent radial(置于最上层)压暗右上/左下背光角,形成\n * 对角光影的玻璃立体感。\n *\n * 底色(--wui-color-glass-ring)不是可选项:四枚 radial 全部锚定角点、半径固定\n * (R = --wui-glass-corner-radius,sheen 用 2R、shade 用 R),一条边只有长过约 3R\n * 才有一段落在所有 radial 之外。底色是唯一覆盖那一段的一层,也是圆角弧段(如 pill\n * 的半圆端)不发暗的原因。三枚 token 同属 ring 前缀,因为它们共同描述这一枚环,\n * 而非三个独立效果。\n *\n * 底色字面量取明色值 rgb(0 0 0 / 0.05) 而非 transparent:本文件其余玻璃 token 的\n * fallback 一律等于明色真值(surface-glass / brightness / highlight / sheen / shade),\n * token 缺省时(未挂 <web-ui-theme>,或 theme 未设 appearance —— 颜色 token 只定义在\n * [appearance] 三个选择器下)整套玻璃面就是明色观感,这一层没有理由例外。\n *\n * 渐变半径为 --wui-glass-corner-radius 的固定物理尺寸正圆——视觉覆盖范围不随元素\n * 宽高缩放(百分比方案在大 panel 上暗边过长、小控件上过弱),各组件用与自身\n * border-radius 同源的值覆盖该变量即可联动。\n *\n * 同源的 --wui-color-glass-highlight 不在此层:它是元素自身 box-shadow 的 inset\n * 高光,与本环无关。-webkit-mask-* 旧语法(含 mask-composite:xor)由 lightningcss\n * 按 targets 自动生成,无前缀 mask-composite 留给 Chrome 120+。\n */\n.wui-glass::before {\n pointer-events: none;\n content: '';\n\n position: absolute;\n inset: 0;\n\n padding: 1px;\n border-radius: inherit;\n\n background-color: var(--wui-color-glass-ring, rgb(0 0 0 / 0.05));\n background-image:\n radial-gradient(\n var(--wui-glass-corner-radius, 32px) at 100% 0,\n var(--wui-color-glass-ring-shade, rgb(0 0 0 / 0.03)),\n transparent\n ),\n radial-gradient(\n var(--wui-glass-corner-radius, 32px) at 0 100%,\n var(--wui-color-glass-ring-shade, rgb(0 0 0 / 0.03)),\n transparent\n ),\n radial-gradient(\n calc(var(--wui-glass-corner-radius, 32px) * 2) at 0 0,\n var(--wui-color-glass-ring-sheen, rgb(255 255 255 / 0.5)),\n transparent\n ),\n radial-gradient(\n calc(var(--wui-glass-corner-radius, 32px) * 2) at 100% 100%,\n var(--wui-color-glass-ring-sheen, rgb(255 255 255 / 0.5)),\n transparent\n );\n\n mask:\n linear-gradient(#000 0 0) content-box,\n linear-gradient(#000 0 0);\n mask-composite: exclude;\n}\n"],"mappings":""}
@@ -0,0 +1,6 @@
1
+ //#region src/classes/glow.css?inline
2
+ var e = ":where(.wui-glow){position:relative}.wui-glow{isolation:isolate}.wui-glow:before,.wui-glow:after{pointer-events:none;content:\"\";z-index:-1;border-radius:inherit;position:absolute;inset:0}.wui-glow:before{background:var(--wui-glow-gradient,linear-gradient(var(--wui-glow-gradient-direction,to bottom), var(--wui-glow-color,var(--wui-color-page,#fff)), transparent))}.wui-glow:after{inset:0 0 calc(-1 * var(--wui-glow-overhang,24px));-webkit-backdrop-filter:blur(var(--wui-glow-blur,4px));backdrop-filter:blur(var(--wui-glow-blur,4px));-webkit-mask-image:linear-gradient(to bottom, black 0, black calc(100% - var(--wui-glow-fade,36px)), transparent 100%);-webkit-mask-image:linear-gradient(to bottom, black 0, black calc(100% - var(--wui-glow-fade,36px)), transparent 100%);mask-image:linear-gradient(to bottom, black 0, black calc(100% - var(--wui-glow-fade,36px)), transparent 100%);-webkit-mask-size:100% 100%;mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}";
3
+ //#endregion
4
+ export { e as default };
5
+
6
+ //# sourceMappingURL=glow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glow.js","names":[],"sources":["../../src/classes/glow.css?inline"],"sourcesContent":["/*\n * 通用晕染层\n * ==========\n *\n * 给任意有盒子尺寸的容器加一层装饰性背景:::before 画渐变,::after 叠真实模糊\n * (backdrop-filter)。两层都放在 z-index: -1,因此内容与插槽始终位于其上方。\n *\n * 用法是给容器加 `.wui-glow`,并从 `@greypan/web-ui/classes.css` 引入本文件(该文件\n * 同时导出 `.wui-glass`)。组件内部需要同一效果时(如 `header-glow`、collapse peek)\n * 用 `?inline` 引入同一份源码,避免复制规则。\n *\n * 定位与层叠\n * ----------\n * `:where()` 让 position 只做兜底:宿主自己声明了 sticky / absolute / fixed 时一律\n * 让位。`isolation: isolate` 是必要的,否则负层级伪元素会落到更外层 stacking context\n * 的背景之后。容器若有 `overflow: hidden`,向外的模糊尾巴会被裁掉;要保留尾巴就\n * 让 overflow 保持 `visible`。\n *\n * 参数\n * ----\n * 渐变是否出现、颜色与方向都由 token 决定:\n *\n * - `--wui-glow-gradient`:完整 background-image 值;设 `none` 即只保留模糊。\n * - `--wui-glow-gradient-direction`:`linear-gradient` 的方向,默认 `to bottom`。\n * - `--wui-glow-color`:渐变起点色,默认跟随页面底色 `--wui-color-page`。\n * - `--wui-glow-blur`:`backdrop-filter` 的模糊半径,默认 `4px`;设 `0px` 关闭模糊。\n * - `--wui-glow-overhang`:模糊层向盒下探的高度,默认 `24px`。\n * - `--wui-glow-fade`:从全强度到 0 的衰减总长度,默认 `36px`,应不小于 overhang。\n *\n * 盒底缘的 alpha = overhang / fade。旧版对 52px header 是向下外扩 30px、衰减约 45px,\n * 底缘 alpha ≈ 0.67;24 / 36 在同样高度上保持这个强度,盒内曲线与原版基本重合,盒外\n * 只剩 24px 的模糊尾巴。旧版的百分比 mask 会让更高的 header 底缘更淡;新版改用绝对\n * 长度,底缘强度不随容器高度变化。fade 小于 overhang 时,全强度段会延伸到盒外。\n *\n * 端点用裸 `transparent`(即 rgb(0 0 0 / 0))而不是与起点色做 color-mix:后者按\n * 预乘 alpha 混色会得到完全不透明的原色,终点没有羽化。裸 transparent 在渐变插值\n * 中同样按预乘处理,各引擎一致淡到 0,且不引入新色相。\n */\n:where(.wui-glow) {\n position: relative;\n}\n\n.wui-glow {\n isolation: isolate;\n}\n\n.wui-glow::before,\n.wui-glow::after {\n pointer-events: none;\n content: '';\n\n position: absolute;\n z-index: -1;\n inset: 0;\n\n border-radius: inherit;\n}\n\n.wui-glow::before {\n background: var(\n --wui-glow-gradient,\n linear-gradient(\n var(--wui-glow-gradient-direction, to bottom),\n var(--wui-glow-color, var(--wui-color-page, #fff)),\n transparent\n )\n );\n}\n\n.wui-glow::after {\n inset: 0 0 calc(-1 * var(--wui-glow-overhang, 24px));\n\n backdrop-filter: blur(var(--wui-glow-blur, 4px));\n\n mask-image: linear-gradient(to bottom, black 0, black calc(100% - var(--wui-glow-fade, 36px)), transparent 100%);\n mask-repeat: no-repeat;\n mask-size: 100% 100%;\n}\n"],"mappings":""}
@@ -0,0 +1 @@
1
+ :where(.wui-glass){position:relative}.wui-glass{--wui-internal-glass-shadow:var(--wui-shadow-glass,0 0 1px #00000005, 0 0 8px #00000008, 0 2px 16px #00000006, 0 8px 32px #00000004);background-color:var(--wui-color-surface-glass,#fafafa57);-webkit-backdrop-filter:blur(var(--wui-glass-blur,4px)) saturate(var(--wui-glass-saturation,160%)) brightness(var(--wui-glass-brightness,1.06));backdrop-filter:blur(var(--wui-glass-blur,4px)) saturate(var(--wui-glass-saturation,160%)) brightness(var(--wui-glass-brightness,1.06));box-shadow:inset 0 0 0 .5px color-mix(in srgb, var(--wui-color-glass-highlight,#ffffffe6) 38%, transparent), inset 0 1px .5px color-mix(in srgb, var(--wui-color-glass-highlight,#ffffffe6) 52%, transparent), inset 0 -.5px 1px #00000008, var(--wui-internal-glass-shadow,var(--wui-shadow-glass,0 0 1px #00000005, 0 0 8px #00000008, 0 2px 16px #00000006, 0 8px 32px #00000004));background-clip:padding-box;border:none}.wui-glass:before{pointer-events:none;content:"";border-radius:inherit;background-color:var(--wui-color-glass-ring,#0000000d);background-image:radial-gradient(var(--wui-glass-corner-radius,32px) at 100% 0, var(--wui-color-glass-ring-shade,#00000008), transparent), radial-gradient(var(--wui-glass-corner-radius,32px) at 0 100%, var(--wui-color-glass-ring-shade,#00000008), transparent), radial-gradient(calc(var(--wui-glass-corner-radius,32px) * 2) at 0 0, var(--wui-color-glass-ring-sheen,#ffffff80), transparent), radial-gradient(calc(var(--wui-glass-corner-radius,32px) * 2) at 100% 100%, var(--wui-color-glass-ring-sheen,#ffffff80), transparent);-webkit-mask-composite:xor;-webkit-mask-source-type:auto,auto;padding:1px;position:absolute;inset:0;-webkit-mask-image:linear-gradient(#000 0 0),linear-gradient(#000 0 0);mask-image:linear-gradient(#000 0 0),linear-gradient(#000 0 0);-webkit-mask-position:0 0,0 0;mask-position:0 0,0 0;-webkit-mask-size:auto,auto;mask-size:auto,auto;-webkit-mask-repeat:repeat,repeat;mask-repeat:repeat,repeat;-webkit-mask-clip:content-box,border-box;mask-clip:content-box,border-box;-webkit-mask-origin:content-box,border-box;mask-origin:content-box,border-box;-webkit-mask-composite:xor;mask-composite:exclude;-webkit-mask-source-type:auto,auto;mask-mode:match-source,match-source}:where(.wui-glow){position:relative}.wui-glow{isolation:isolate}.wui-glow:before,.wui-glow:after{pointer-events:none;content:"";z-index:-1;border-radius:inherit;position:absolute;inset:0}.wui-glow:before{background:var(--wui-glow-gradient,linear-gradient(var(--wui-glow-gradient-direction,to bottom), var(--wui-glow-color,var(--wui-color-page,#fff)), transparent))}.wui-glow:after{inset:0 0 calc(-1 * var(--wui-glow-overhang,24px));-webkit-backdrop-filter:blur(var(--wui-glow-blur,4px));backdrop-filter:blur(var(--wui-glow-blur,4px));-webkit-mask-image:linear-gradient(to bottom, black 0, black calc(100% - var(--wui-glow-fade,36px)), transparent 100%);-webkit-mask-image:linear-gradient(to bottom, black 0, black calc(100% - var(--wui-glow-fade,36px)), transparent 100%);mask-image:linear-gradient(to bottom, black 0, black calc(100% - var(--wui-glow-fade,36px)), transparent 100%);-webkit-mask-size:100% 100%;mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}
@@ -417,6 +417,8 @@ export declare class WebUiAutocomplete extends WebUiAutocomplete_base {
417
417
  private _options;
418
418
  private _portal?;
419
419
  private _portalContent?;
420
+ private _keyboardNavigationActive;
421
+ private _scrollInteractionElement?;
420
422
  private readonly _scrollLock;
421
423
  private readonly _optionPortal;
422
424
  private readonly _trigger;
@@ -454,12 +456,17 @@ export declare class WebUiAutocomplete extends WebUiAutocomplete_base {
454
456
  private _handleOptionClick;
455
457
  private _onOptionUpdate;
456
458
  private _handleOptionPointerOver;
459
+ private _syncScrollInteractionListeners;
460
+ private _clearKeyboardNavigation;
461
+ private _handleListPointerMove;
462
+ private _handleListWheel;
457
463
  private _handleOptionPointerDown;
458
464
  private _onSlotChange;
459
465
  private _onKeydown;
460
466
  private _navigateActive;
461
467
  private _setInitialActiveOption;
462
468
  private _syncActiveOption;
469
+ private _scrollActiveOptionIntoView;
463
470
  private _selectOption;
464
471
  private _findExactOption;
465
472
  private _hasExactOption;
@@ -1,5 +1,5 @@
1
1
  import e from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
2
- import t from "../../assets/glass.js";
2
+ import t from "../../classes/glass.js";
3
3
  import { FormAssociated as n, FormAssociationController as r, defineFormAssociation as i } from "../../shared/form-association/index.js";
4
4
  import { normalizeLiteral as a } from "../../shared/normalize/index.js";
5
5
  import "../input/index.js";
@@ -49,7 +49,7 @@ var N = class extends n(x) {
49
49
  typeof e == "string" && (this.value = e);
50
50
  },
51
51
  syncValidity: () => this._syncValidity()
52
- }).make(), this._formAssociationController = new r(this, this._formAssociation), this._isOpen = !1, this._activeIndex = -1, this._focused = !1, this._hasTriggerSlot = !1, this._idPrefix = `web-ui-autocomplete-${++k._nextInstanceId}`, this._a11yOptionIds = /* @__PURE__ */ new WeakMap(), this._nextA11yOptionId = 0, this._options = [], this._scrollLock = y().make(), this._optionPortal = m().make({
52
+ }).make(), this._formAssociationController = new r(this, this._formAssociation), this._isOpen = !1, this._activeIndex = -1, this._focused = !1, this._hasTriggerSlot = !1, this._idPrefix = `web-ui-autocomplete-${++k._nextInstanceId}`, this._a11yOptionIds = /* @__PURE__ */ new WeakMap(), this._nextA11yOptionId = 0, this._options = [], this._keyboardNavigationActive = !1, this._scrollLock = y().make(), this._optionPortal = m().make({
53
53
  element: this,
54
54
  idPrefix: `${this._idPrefix}-option`,
55
55
  getPortal: () => this._portal,
@@ -106,7 +106,11 @@ var N = class extends n(x) {
106
106
  }, this._onOptionUpdate = () => {
107
107
  this._optionPortal.scheduleRefresh();
108
108
  }, this._handleOptionPointerOver = (e) => {
109
- e.pointerType !== "touch" && this._isOpen && e.currentTarget instanceof HTMLElement && (this._activeIndex = -1, this._syncActiveOption());
109
+ e.pointerType !== "touch" && this._isOpen && e.currentTarget instanceof HTMLElement && (this._keyboardNavigationActive || (this._activeIndex = -1, this._syncActiveOption()));
110
+ }, this._handleListPointerMove = () => {
111
+ this._isOpen && this._clearKeyboardNavigation();
112
+ }, this._handleListWheel = () => {
113
+ this._isOpen && this._clearKeyboardNavigation();
110
114
  }, this._handleOptionPointerDown = (e) => {
111
115
  e.preventDefault();
112
116
  }, this._onSlotChange = () => {
@@ -139,7 +143,7 @@ var N = class extends n(x) {
139
143
  }
140
144
  }
141
145
  }, this._onTriggerInput = (e) => {
142
- this._isDisabled || this.readonly || e !== this._value && (this.value = e, this._activeIndex = -1, this._syncActiveOption(), this._open());
146
+ this._isDisabled || this.readonly || e !== this._value && (this.value = e, this._keyboardNavigationActive = !1, this._activeIndex = -1, this._syncActiveOption(), this._open());
143
147
  }, this._onTriggerClick = () => {
144
148
  this._isDisabled || this.readonly || this._open();
145
149
  }, this._onTriggerFocusIn = (e) => {
@@ -164,13 +168,13 @@ var N = class extends n(x) {
164
168
  openOverlay: (e) => this._openOverlay(e),
165
169
  closeOverlay: () => void this._closeOverlay(),
166
170
  onOpen: (e) => {
167
- e ? this._setInitialActiveOption() : this._syncActiveOption();
171
+ this._keyboardNavigationActive = e, e ? this._setInitialActiveOption() : this._syncActiveOption();
168
172
  },
169
173
  onBeforeClose: () => {
170
174
  this._optionPortal.syncPortalContent();
171
175
  },
172
176
  onAfterClose: () => {
173
- this._activeIndex = -1, this._options.forEach((e) => e.removeAttribute("active"));
177
+ this._keyboardNavigationActive = !1, this._activeIndex = -1, this._options.forEach((e) => e.removeAttribute("active")), this._syncScrollInteractionListeners();
174
178
  }
175
179
  });
176
180
  }
@@ -234,7 +238,7 @@ var N = class extends n(x) {
234
238
  });
235
239
  }
236
240
  disconnectedCallback() {
237
- super.disconnectedCallback(), this._optionPortal.dispose(), this._trigger.dispose(), this.removeEventListener("keydown", this._onKeydown), this.removeEventListener("focusout", this._onFocusOut), document.removeEventListener("click", this._onClickOutside), this._options.forEach(this._unbindOption), this._close(), this._portal = void 0, this._portalContent = void 0, this._panel.dispose(), this._scrollLock.release();
241
+ super.disconnectedCallback(), this._optionPortal.dispose(), this._trigger.dispose(), this.removeEventListener("keydown", this._onKeydown), this.removeEventListener("focusout", this._onFocusOut), document.removeEventListener("click", this._onClickOutside), this._options.forEach(this._unbindOption), this._close(), this._syncScrollInteractionListeners(), this._portal = void 0, this._portalContent = void 0, this._panel.dispose(), this._scrollLock.release();
238
242
  }
239
243
  firstUpdated() {
240
244
  this._trigger.bind(), requestAnimationFrame(() => {
@@ -292,18 +296,34 @@ var N = class extends n(x) {
292
296
  let e = this._options[this._activeIndex], t = this._optionPortal.refresh(this._options, e);
293
297
  this._options = t.options, this._activeIndex = t.activeIndex;
294
298
  }
299
+ _syncScrollInteractionListeners() {
300
+ let e = this._isOpen ? this._panel.getPanel()?.querySelector(".autocomplete-scroll") : void 0;
301
+ e !== this._scrollInteractionElement && (this._scrollInteractionElement?.removeEventListener("pointermove", this._handleListPointerMove), this._scrollInteractionElement?.removeEventListener("wheel", this._handleListWheel), this._scrollInteractionElement = e ?? void 0, this._scrollInteractionElement?.addEventListener("pointermove", this._handleListPointerMove), this._scrollInteractionElement?.addEventListener("wheel", this._handleListWheel));
302
+ }
303
+ _clearKeyboardNavigation() {
304
+ this._keyboardNavigationActive && (this._keyboardNavigationActive = !1, this._activeIndex = -1, this._syncActiveOption());
305
+ }
295
306
  _navigateActive(e) {
296
307
  let t = this._options.filter((e) => !e.disabled && !e.hasAttribute("data-filtered"));
297
308
  if (!t.length) return;
309
+ this._keyboardNavigationActive = !0;
298
310
  let n = this._activeIndex >= 0 ? t.indexOf(this._options[this._activeIndex]) : e > 0 ? -1 : 0;
299
311
  this._activeIndex = this._options.indexOf(t[p(t.length, n, e)]), this._syncActiveOption();
300
312
  }
301
313
  _setInitialActiveOption() {
302
314
  let e = this._options.findIndex((e) => !e.disabled && !e.hasAttribute("data-filtered"));
303
- this._activeIndex = e, this._syncActiveOption();
315
+ this._keyboardNavigationActive = !0, this._activeIndex = e, this._syncActiveOption();
304
316
  }
305
317
  _syncActiveOption() {
306
- this._options.forEach((e, t) => e.toggleAttribute("active", t === this._activeIndex));
318
+ this._options.forEach((e, t) => e.toggleAttribute("active", t === this._activeIndex)), this._isOpen && this._scrollActiveOptionIntoView();
319
+ }
320
+ _scrollActiveOptionIntoView() {
321
+ let e = this._options[this._activeIndex], t = this._panel.getPanel()?.querySelector(".autocomplete-scroll");
322
+ if (!e || !t?.clientHeight) return;
323
+ let n = e.getBoundingClientRect(), r = t.getBoundingClientRect(), i = t.offsetHeight ? r.height / t.offsetHeight : 1;
324
+ if (!i) return;
325
+ let a = r.top + t.clientTop * i, o = a + t.clientHeight * i;
326
+ n.top < a ? t.scrollTop -= (a - n.top) / i : n.bottom > o && (t.scrollTop += (n.bottom - o) / i);
307
327
  }
308
328
  _selectOption(e) {
309
329
  this.readonly || (this.value = e.label, this._setSelectedValue(e.value), this._close(), this.dispatchEvent(new Event("input", {
@@ -352,13 +372,13 @@ var N = class extends n(x) {
352
372
  c(this, this._isOpen);
353
373
  }
354
374
  _openOverlay(e = !1) {
355
- this._panel.open(e), this._syncOverlayInert();
375
+ this._panel.open(e), this._syncScrollInteractionListeners(), this._syncActiveOption(), this._syncOverlayInert();
356
376
  }
357
377
  async _closeOverlay() {
358
- await this._panel.close(() => this._isOpen) && (this._portal = void 0, this._portalContent = void 0);
378
+ await this._panel.close(() => this._isOpen) && (this._syncScrollInteractionListeners(), this._portal = void 0, this._portalContent = void 0);
359
379
  }
360
380
  _reconfigureOverlay() {
361
- this._portal = void 0, this._portalContent = void 0, this._panel.reconfigure(this._isOpen), this._syncOverlayInert();
381
+ this._portal = void 0, this._portalContent = void 0, this._panel.reconfigure(this._isOpen), this._syncScrollInteractionListeners(), this._syncOverlayInert();
362
382
  }
363
383
  _syncScrollLock(e = this._isOpen) {
364
384
  this._scrollLock.sync(e && !this.noScrollLock);
@@ -367,7 +387,7 @@ var N = class extends n(x) {
367
387
  let e = v().make({
368
388
  container: this.overlayContainer,
369
389
  target: this,
370
- style: `${t}\n${o}\n${b}`,
390
+ style: `${t}\n${o}\n${s}\n${b}`,
371
391
  className: "wui-glass autocomplete-overlay portal wui-floating-panel",
372
392
  onContentChange: () => {
373
393
  this._optionPortal.scheduleRefresh();