@icen.ai/ui 0.8.1 → 0.10.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 (335) hide show
  1. package/AGENTS.md +176 -75
  2. package/README.md +66 -88
  3. package/dist/base.css +208 -179
  4. package/dist/behaviors/accordion.mjs +8 -8
  5. package/dist/behaviors/ai-branch.d.mts +19 -0
  6. package/dist/behaviors/ai-branch.mjs +1133 -0
  7. package/dist/behaviors/ai-chat.d.mts +12 -10
  8. package/dist/behaviors/ai-chat.mjs +1125 -155
  9. package/dist/behaviors/ai-composer.d.mts +44 -12
  10. package/dist/behaviors/ai-composer.mjs +2084 -523
  11. package/dist/behaviors/ai-core.d.mts +1 -1
  12. package/dist/behaviors/ai-core.mjs +1016 -107
  13. package/dist/behaviors/ai-diff.mjs +1019 -127
  14. package/dist/behaviors/ai-feedback.d.mts +26 -0
  15. package/dist/behaviors/ai-feedback.mjs +1212 -0
  16. package/dist/behaviors/ai-panel.d.mts +21 -7
  17. package/dist/behaviors/ai-panel.mjs +1308 -264
  18. package/dist/behaviors/ai-provider.mjs +1003 -101
  19. package/dist/behaviors/ai-threads.d.mts +38 -0
  20. package/dist/behaviors/ai-threads.mjs +1233 -0
  21. package/dist/behaviors/ai-tool.d.mts +2 -2
  22. package/dist/behaviors/ai-tool.mjs +2042 -396
  23. package/dist/behaviors/ai-tools.d.mts +89 -16
  24. package/dist/behaviors/ai-tools.mjs +4453 -372
  25. package/dist/behaviors/autocomplete.d.mts +53 -0
  26. package/dist/behaviors/autocomplete.mjs +1600 -0
  27. package/dist/behaviors/back-top.d.mts +1 -1
  28. package/dist/behaviors/back-top.mjs +951 -14
  29. package/dist/behaviors/carousel.d.mts +1 -1
  30. package/dist/behaviors/carousel.mjs +906 -11
  31. package/dist/behaviors/cascader.d.mts +48 -0
  32. package/dist/behaviors/cascader.mjs +1853 -0
  33. package/dist/behaviors/charts.d.mts +174 -8
  34. package/dist/behaviors/charts.mjs +2632 -192
  35. package/dist/behaviors/code-block.d.mts +36 -0
  36. package/dist/behaviors/code-block.mjs +1611 -0
  37. package/dist/behaviors/command-palette.d.mts +9 -6
  38. package/dist/behaviors/command-palette.mjs +161 -59
  39. package/dist/behaviors/context-menu.d.mts +9 -8
  40. package/dist/behaviors/context-menu.mjs +1112 -44
  41. package/dist/behaviors/controls.d.mts +13 -13
  42. package/dist/behaviors/controls.mjs +989 -130
  43. package/dist/behaviors/copy.d.mts +21 -2
  44. package/dist/behaviors/copy.mjs +1206 -42
  45. package/dist/behaviors/datatable.d.mts +53 -3
  46. package/dist/behaviors/datatable.mjs +1963 -207
  47. package/dist/behaviors/date-picker.d.mts +0 -1
  48. package/dist/behaviors/date-picker.mjs +1340 -293
  49. package/dist/behaviors/design.d.mts +54 -0
  50. package/dist/behaviors/design.mjs +109 -0
  51. package/dist/behaviors/dropdown.d.mts +5 -6
  52. package/dist/behaviors/dropdown.mjs +262 -110
  53. package/dist/behaviors/events.d.mts +313 -3
  54. package/dist/behaviors/events.mjs +47 -2
  55. package/dist/behaviors/feedback.d.mts +30 -0
  56. package/dist/behaviors/feedback.mjs +296 -0
  57. package/dist/behaviors/focus.d.mts +8 -0
  58. package/dist/behaviors/focus.mjs +24 -0
  59. package/dist/behaviors/form-validate.d.mts +102 -0
  60. package/dist/behaviors/form-validate.mjs +6091 -0
  61. package/dist/behaviors/i18n.d.mts +789 -0
  62. package/dist/behaviors/i18n.mjs +1711 -0
  63. package/dist/behaviors/icons.d.mts +93 -0
  64. package/dist/behaviors/icons.mjs +113 -0
  65. package/dist/behaviors/input.d.mts +9 -1
  66. package/dist/behaviors/input.mjs +1056 -60
  67. package/dist/behaviors/kb-access.d.mts +34 -0
  68. package/dist/behaviors/kb-access.mjs +2459 -0
  69. package/dist/behaviors/kb-acl.d.mts +50 -0
  70. package/dist/behaviors/kb-acl.mjs +1345 -0
  71. package/dist/behaviors/kb-answer.d.mts +38 -0
  72. package/dist/behaviors/kb-answer.mjs +3707 -0
  73. package/dist/behaviors/kb-audit.d.mts +23 -0
  74. package/dist/behaviors/kb-audit.mjs +1297 -0
  75. package/dist/behaviors/kb-canvas.d.mts +44 -0
  76. package/dist/behaviors/kb-canvas.mjs +1512 -0
  77. package/dist/behaviors/kb-chain.d.mts +15 -0
  78. package/dist/behaviors/kb-chain.mjs +1192 -0
  79. package/dist/behaviors/kb-checkpoint.d.mts +16 -0
  80. package/dist/behaviors/kb-checkpoint.mjs +1492 -0
  81. package/dist/behaviors/kb-chunks.d.mts +31 -0
  82. package/dist/behaviors/kb-chunks.mjs +1414 -0
  83. package/dist/behaviors/kb-citation.d.mts +38 -0
  84. package/dist/behaviors/kb-citation.mjs +1699 -0
  85. package/dist/behaviors/kb-clarify.d.mts +20 -0
  86. package/dist/behaviors/kb-clarify.mjs +1176 -0
  87. package/dist/behaviors/kb-conflict.d.mts +22 -0
  88. package/dist/behaviors/kb-conflict.mjs +1206 -0
  89. package/dist/behaviors/kb-connector.d.mts +17 -0
  90. package/dist/behaviors/kb-connector.mjs +1303 -0
  91. package/dist/behaviors/kb-core.d.mts +534 -0
  92. package/dist/behaviors/kb-core.mjs +1805 -0
  93. package/dist/behaviors/kb-eval.d.mts +22 -0
  94. package/dist/behaviors/kb-eval.mjs +2235 -0
  95. package/dist/behaviors/kb-explain.d.mts +11 -0
  96. package/dist/behaviors/kb-explain.mjs +1165 -0
  97. package/dist/behaviors/kb-filter.d.mts +31 -0
  98. package/dist/behaviors/kb-filter.mjs +2417 -0
  99. package/dist/behaviors/kb-gap.d.mts +27 -0
  100. package/dist/behaviors/kb-gap.mjs +1195 -0
  101. package/dist/behaviors/kb-hittest.d.mts +34 -0
  102. package/dist/behaviors/kb-hittest.mjs +1352 -0
  103. package/dist/behaviors/kb-metadata.d.mts +29 -0
  104. package/dist/behaviors/kb-metadata.mjs +1248 -0
  105. package/dist/behaviors/kb-passage.d.mts +41 -0
  106. package/dist/behaviors/kb-passage.mjs +1469 -0
  107. package/dist/behaviors/kb-pipeline.d.mts +18 -0
  108. package/dist/behaviors/kb-pipeline.mjs +1301 -0
  109. package/dist/behaviors/kb-qa.d.mts +26 -0
  110. package/dist/behaviors/kb-qa.mjs +1260 -0
  111. package/dist/behaviors/kb-rerank.d.mts +20 -0
  112. package/dist/behaviors/kb-rerank.mjs +1248 -0
  113. package/dist/behaviors/kb-retrieval.d.mts +49 -0
  114. package/dist/behaviors/kb-retrieval.mjs +2585 -0
  115. package/dist/behaviors/kb-review.d.mts +44 -0
  116. package/dist/behaviors/kb-review.mjs +2282 -0
  117. package/dist/behaviors/kb-sandbox.d.mts +33 -0
  118. package/dist/behaviors/kb-sandbox.mjs +1247 -0
  119. package/dist/behaviors/kb-segment.d.mts +32 -0
  120. package/dist/behaviors/kb-segment.mjs +1431 -0
  121. package/dist/behaviors/kb-select.d.mts +24 -0
  122. package/dist/behaviors/kb-select.mjs +1989 -0
  123. package/dist/behaviors/kb-sources.d.mts +27 -0
  124. package/dist/behaviors/kb-sources.mjs +1287 -0
  125. package/dist/behaviors/kb-sql.d.mts +43 -0
  126. package/dist/behaviors/kb-sql.mjs +1865 -0
  127. package/dist/behaviors/kb-trace.d.mts +16 -0
  128. package/dist/behaviors/kb-trace.mjs +1328 -0
  129. package/dist/behaviors/kb-visibility.d.mts +44 -0
  130. package/dist/behaviors/kb-visibility.mjs +1331 -0
  131. package/dist/behaviors/kb-who-can.d.mts +32 -0
  132. package/dist/behaviors/kb-who-can.mjs +2216 -0
  133. package/dist/behaviors/mentions.d.mts +93 -0
  134. package/dist/behaviors/mentions.mjs +2152 -0
  135. package/dist/behaviors/modal.d.mts +4 -3
  136. package/dist/behaviors/modal.mjs +1055 -33
  137. package/dist/behaviors/nav.d.mts +1 -1
  138. package/dist/behaviors/nav.mjs +6 -6
  139. package/dist/behaviors/notification.d.mts +16 -8
  140. package/dist/behaviors/notification.mjs +1615 -265
  141. package/dist/behaviors/panel.d.mts +4 -0
  142. package/dist/behaviors/panel.mjs +994 -0
  143. package/dist/behaviors/popover.d.mts +28 -5
  144. package/dist/behaviors/popover.mjs +225 -24
  145. package/dist/behaviors/select.d.mts +5 -2
  146. package/dist/behaviors/select.mjs +1410 -117
  147. package/dist/behaviors/sidebar.d.mts +1 -1
  148. package/dist/behaviors/sidebar.mjs +13 -13
  149. package/dist/behaviors/slider.d.mts +1 -1
  150. package/dist/behaviors/slider.mjs +9 -9
  151. package/dist/behaviors/split-pane.d.mts +1 -1
  152. package/dist/behaviors/split-pane.mjs +7 -7
  153. package/dist/behaviors/suggestions.d.mts +27 -0
  154. package/dist/behaviors/suggestions.mjs +1089 -0
  155. package/dist/behaviors/svg-parse.d.mts +14 -0
  156. package/dist/behaviors/svg-parse.mjs +37 -0
  157. package/dist/behaviors/tabs.d.mts +2 -2
  158. package/dist/behaviors/tabs.mjs +25 -25
  159. package/dist/behaviors/tag-input.d.mts +23 -5
  160. package/dist/behaviors/tag-input.mjs +1359 -104
  161. package/dist/behaviors/theme-switcher.d.mts +24 -0
  162. package/dist/behaviors/theme-switcher.mjs +1981 -0
  163. package/dist/behaviors/theme.d.mts +15 -8
  164. package/dist/behaviors/theme.mjs +390 -28
  165. package/dist/behaviors/toast.d.mts +24 -25
  166. package/dist/behaviors/toast.mjs +1266 -88
  167. package/dist/behaviors/tooltip.d.mts +4 -0
  168. package/dist/behaviors/tooltip.mjs +378 -0
  169. package/dist/behaviors/tree-select.d.mts +69 -0
  170. package/dist/behaviors/tree-select.mjs +1907 -0
  171. package/dist/behaviors/tree.d.mts +1 -1
  172. package/dist/behaviors/tree.mjs +15 -15
  173. package/dist/behaviors/upload.d.mts +3 -2
  174. package/dist/behaviors/upload.mjs +1076 -66
  175. package/dist/cli.mjs +5 -6
  176. package/dist/components/ai-branch.mjs +3 -0
  177. package/dist/components/ai-chat.css +846 -458
  178. package/dist/components/ai-diff.css +153 -132
  179. package/dist/components/ai-feedback.mjs +3 -0
  180. package/dist/components/ai-panel.css +495 -270
  181. package/dist/components/ai-threads.mjs +3 -0
  182. package/dist/components/ai-tool.css +393 -238
  183. package/dist/components/autocomplete.css +61 -0
  184. package/dist/components/autocomplete.mjs +3 -0
  185. package/dist/components/back-top.css +34 -31
  186. package/dist/components/badge.css +44 -41
  187. package/dist/components/breadcrumb.css +67 -46
  188. package/dist/components/btn.css +80 -74
  189. package/dist/components/card.css +117 -106
  190. package/dist/components/cascader.css +311 -0
  191. package/dist/components/cascader.mjs +3 -0
  192. package/dist/components/chart-area.css +8 -2
  193. package/dist/components/chart-bar.css +95 -33
  194. package/dist/components/chart-gauge.css +14 -11
  195. package/dist/components/chart-graph.css +60 -0
  196. package/dist/components/chart-graph.mjs +4 -0
  197. package/dist/components/chart-heatmap.css +96 -90
  198. package/dist/components/chart-line.css +8 -3
  199. package/dist/components/chart-map.mjs +3 -0
  200. package/dist/components/chart-pie.css +9 -6
  201. package/dist/components/chart-radar.css +8 -5
  202. package/dist/components/chart-scatter.css +9 -6
  203. package/dist/components/chart-scatter.mjs +1 -1
  204. package/dist/components/chart-sparkline.css +5 -2
  205. package/dist/components/chart-stack.css +14 -11
  206. package/dist/components/charts.css +190 -67
  207. package/dist/components/charts.mjs +1 -0
  208. package/dist/components/code-block.css +418 -0
  209. package/dist/components/code-block.mjs +4 -0
  210. package/dist/components/command-palette.css +138 -117
  211. package/dist/components/content.css +257 -251
  212. package/dist/components/copy.css +72 -44
  213. package/dist/components/datatable.css +423 -181
  214. package/dist/components/date-picker.css +135 -104
  215. package/dist/components/empty.css +68 -65
  216. package/dist/components/feedback.css +201 -183
  217. package/dist/components/form-validate.css +120 -0
  218. package/dist/components/form-validate.mjs +3 -0
  219. package/dist/components/form.css +150 -127
  220. package/dist/components/icon.css +102 -0
  221. package/dist/components/icon.mjs +3 -0
  222. package/dist/components/input.css +159 -104
  223. package/dist/components/kb-access.mjs +4 -0
  224. package/dist/components/kb-acl.mjs +4 -0
  225. package/dist/components/kb-agent.css +496 -0
  226. package/dist/components/kb-answer.mjs +4 -0
  227. package/dist/components/kb-audit.mjs +4 -0
  228. package/dist/components/kb-canvas.mjs +4 -0
  229. package/dist/components/kb-chain.mjs +4 -0
  230. package/dist/components/kb-checkpoint.mjs +4 -0
  231. package/dist/components/kb-chunks.mjs +4 -0
  232. package/dist/components/kb-citation.mjs +4 -0
  233. package/dist/components/kb-clarify.mjs +4 -0
  234. package/dist/components/kb-conflict.mjs +4 -0
  235. package/dist/components/kb-connector.mjs +4 -0
  236. package/dist/components/kb-data.css +653 -0
  237. package/dist/components/kb-eval.mjs +4 -0
  238. package/dist/components/kb-explain.mjs +4 -0
  239. package/dist/components/kb-filter.mjs +4 -0
  240. package/dist/components/kb-gap.mjs +4 -0
  241. package/dist/components/kb-ground.css +578 -0
  242. package/dist/components/kb-hittest.mjs +4 -0
  243. package/dist/components/kb-ingest.css +882 -0
  244. package/dist/components/kb-metadata.mjs +4 -0
  245. package/dist/components/kb-ops.css +554 -0
  246. package/dist/components/kb-passage.mjs +4 -0
  247. package/dist/components/kb-perm.css +433 -0
  248. package/dist/components/kb-pipeline.mjs +4 -0
  249. package/dist/components/kb-qa.mjs +4 -0
  250. package/dist/components/kb-rerank.mjs +4 -0
  251. package/dist/components/kb-retrieval.mjs +4 -0
  252. package/dist/components/kb-review.mjs +4 -0
  253. package/dist/components/kb-sandbox.mjs +4 -0
  254. package/dist/components/kb-search.css +897 -0
  255. package/dist/components/kb-segment.mjs +4 -0
  256. package/dist/components/kb-sources.mjs +4 -0
  257. package/dist/components/kb-sql.mjs +4 -0
  258. package/dist/components/kb-trace.mjs +4 -0
  259. package/dist/components/kb-visibility.mjs +4 -0
  260. package/dist/components/kb-who-can.mjs +4 -0
  261. package/dist/components/kb.css +263 -0
  262. package/dist/components/kb.mjs +9 -0
  263. package/dist/components/layout.css +85 -82
  264. package/dist/components/material-chinese.css +73 -0
  265. package/dist/components/material-soviet.css +76 -0
  266. package/dist/components/material.css +275 -0
  267. package/dist/components/media.css +220 -166
  268. package/dist/components/mentions.css +189 -0
  269. package/dist/components/mentions.mjs +3 -0
  270. package/dist/components/menu.css +113 -105
  271. package/dist/components/modal.css +107 -112
  272. package/dist/components/nav.css +118 -115
  273. package/dist/components/notification.css +238 -174
  274. package/dist/components/pagination.css +56 -53
  275. package/dist/components/panel.css +66 -63
  276. package/dist/components/panel.mjs +1 -0
  277. package/dist/components/pill.css +81 -71
  278. package/dist/components/popover.css +67 -53
  279. package/dist/components/scroll-area.css +113 -110
  280. package/dist/components/segmented.css +146 -143
  281. package/dist/components/select.css +206 -186
  282. package/dist/components/sidebar.css +137 -127
  283. package/dist/components/slider.css +117 -114
  284. package/dist/components/split-pane.css +112 -109
  285. package/dist/components/stat.css +112 -106
  286. package/dist/components/steps.css +109 -103
  287. package/dist/components/switch.css +138 -134
  288. package/dist/components/table.css +131 -107
  289. package/dist/components/tabs.css +114 -108
  290. package/dist/components/tag-input.css +48 -133
  291. package/dist/components/tag.css +81 -71
  292. package/dist/components/theme-switcher.css +105 -0
  293. package/dist/components/theme-switcher.mjs +3 -0
  294. package/dist/components/toast.css +100 -86
  295. package/dist/components/toolbar.css +45 -42
  296. package/dist/components/tooltip.css +102 -43
  297. package/dist/components/tooltip.mjs +1 -0
  298. package/dist/components/tree-select.css +416 -0
  299. package/dist/components/tree-select.mjs +3 -0
  300. package/dist/components/upload.css +135 -92
  301. package/dist/fonts/LICENSES/ark-pixel/OFL.txt +96 -0
  302. package/dist/fonts/LICENSES/cubic-11/OFL.txt +108 -0
  303. package/dist/fonts/LICENSES/galmuri/LICENSE.txt +93 -0
  304. package/dist/fonts/OFL.txt +96 -0
  305. package/dist/fonts/README.md +14 -0
  306. package/dist/fonts/fusion-pixel-12px-zh-hans.woff2 +0 -0
  307. package/dist/index.d.mts +64 -15
  308. package/dist/index.mjs +28049 -8595
  309. package/dist/react.d.mts +91 -0
  310. package/dist/react.mjs +8208 -0
  311. package/dist/registry.json +454 -15
  312. package/dist/tokens/chinese.css +44 -0
  313. package/dist/tokens/colors.css +365 -1222
  314. package/dist/tokens/design.css +109 -0
  315. package/dist/tokens/dtcg.json +11198 -0
  316. package/dist/tokens/figma.variables.json +7017 -0
  317. package/dist/tokens/icon-masks.css +78 -0
  318. package/dist/tokens/pixel.css +50 -0
  319. package/dist/tokens/soviet.css +44 -0
  320. package/dist/tokens/style-profiles.css +36 -547
  321. package/dist/tokens/themes.json +308 -0
  322. package/dist/tokens/tokens-studio.json +6728 -0
  323. package/dist/tokens/typography.css +21 -18
  324. package/dist/tokens.css +1555 -1801
  325. package/dist/ui.css +16147 -8597
  326. package/dist/vue.d.mts +344 -0
  327. package/dist/vue.mjs +7781 -0
  328. package/docs/spec/ai-input-experience.md +75 -0
  329. package/docs/spec/ai-native.md +238 -147
  330. package/docs/spec/design-philosophy.md +189 -0
  331. package/docs/spec/design-primitives.md +196 -0
  332. package/docs/spec/kb-family.md +475 -0
  333. package/package.json +42 -7
  334. package/dist/retro-effects.css +0 -255
  335. package/dist/tokens/retro-effects.css +0 -255
@@ -40,6 +40,12 @@ interface PanelSizing {
40
40
  /** 高度上限 */
41
41
  maxHeight?: number;
42
42
  }
43
+ /** Reveal an item inside one explicit scrollport. Never scrolls document ancestors;
44
+ * use this instead of scrollIntoView for portalled menus and nested listboxes. */
45
+ declare function revealPopoverItem(item: HTMLElement, scrollport: HTMLElement, opts?: {
46
+ focus?: boolean;
47
+ axis?: 'block' | 'inline' | 'both';
48
+ }): void;
43
49
  /** 从元素读取 data-panel-* 尺寸属性(非法/缺失字段跳过,不抛异常)。 */
44
50
  declare function readPanelSizing(el: Element | null | undefined): PanelSizing;
45
51
  /** 合并尺寸:程序面 overrides 优先于属性面(undefined 字段不覆盖)。 */
@@ -52,7 +58,7 @@ declare function sizingToLayout<T extends {
52
58
  }>(sizing: PanelSizing, opts: T): T;
53
59
  /** 把尺寸契约内联到自建面板(dropdown / context-menu / date-picker 等 fixed 面板)。
54
60
  * 传 null 复位全部内联尺寸——单例面板跨实例复用时防「上次的设置串味」。
55
- * 视口守卫:width / maxWidth 一律夹到 视口宽 − 24px(窄屏上过大的 data-panel-width 不溢出,
61
+ * 视口守卫:width / maxWidth 一律夹到 视口宽 − 24px(窄屏上过大的 data-icen-panel-width 不溢出,
56
62
  * 与 computePopoverLayout 的 availableWidth 同一纪律,不再两套标准)。 */
57
63
  declare function applyPanelSizing(panel: HTMLElement, sizing: PanelSizing | null): void;
58
64
  interface PopoverLayout {
@@ -68,15 +74,32 @@ interface OpenPopoverOptions extends PopoverLayoutOptions {
68
74
  sizing?: PanelSizing;
69
75
  /** 关闭(含外点/Esc 触发的关闭)后的回调 */
70
76
  onClose?: () => void;
77
+ /** Optional initial focus inside an interactive panel. */
78
+ initialFocus?: HTMLElement;
79
+ /** Restore the anchor only if the panel still owns focus; default true. */
80
+ restoreFocus?: boolean;
71
81
  }
72
82
  /** 视口智能布局:返回 {side, left, top, width, maxHeight}。
73
83
  * 纵向侧(top/bottom):选侧按上下空间,宽按 min/max 夹取,高按内容预期。
74
84
  * 横向侧(left/right):锚点左右翻转,宽取 min(无内容测量,横向侧取下限),
75
85
  * 高占满可用空间,纵向按 align(start/center/end;stretch 视作 start)贴锚。 */
76
86
  declare function computePopoverLayout(anchor: PopoverRect, viewport: PopoverViewport, opts?: PopoverLayoutOptions): PopoverLayout;
77
- /** 打开浮层:panel 移入 body 并按 anchor 布局;重复调用同一 panel 为先关后开。 */
87
+ /** Shared ownership for action menus: opening one retires the previous renderer
88
+ * without stealing focus. The release handle cannot release a newer owner. */
89
+ declare function claimMenuFocus(close: () => void): () => void;
90
+ /** 打开浮层:panel 移入 body 并按 anchor 布局;重复调用同一 panel 为先关后开;
91
+ * 退场中重开从当前动画位置续接(复用既有门户记录,不重复插占位)。 */
78
92
  declare function openPopover(panel: HTMLElement, options: OpenPopoverOptions): void;
79
- /** 关闭浮层:移除监听并把 panel 还原到打开前的位置与 hidden 状态。 */
80
- declare function closePopover(panel: HTMLElement): void;
93
+ /** 关闭浮层:视觉先退场(可被打断重开),finalize 再还原门户;焦点与 onClose 立即结算。
94
+ * immediate: true 供卸载/销毁路径——跳过退场直接 finalize(拆页时不把面板留在 body)。 */
95
+ declare function closePopover(panel: HTMLElement, opts?: {
96
+ immediate?: boolean;
97
+ }): void;
98
+ /**
99
+ * 声明式浮层委托:<button data-icen-popover-target="panelId"> 点击开关 .icen-popover 面板,
100
+ * 同步触发器 aria-haspopup/aria-expanded/aria-controls;面板内 .icen-popover-close 点击关闭。
101
+ * 同一 root 重复调用返回同一销毁函数;销毁摘除委托并关闭本作用域打开的浮层。SSR 下返回 no-op。
102
+ */
103
+ declare function initPopover(root?: ParentNode): () => void;
81
104
 
82
- export { type OpenPopoverOptions, type PanelSizing, type PopoverAlign, type PopoverLayout, type PopoverLayoutOptions, type PopoverRect, type PopoverSide, type PopoverViewport, applyPanelSizing, closePopover, computePopoverLayout, openPopover, readPanelSizing, resolvePanelSizing, sizingToLayout };
105
+ export { type OpenPopoverOptions, type PanelSizing, type PopoverAlign, type PopoverLayout, type PopoverLayoutOptions, type PopoverRect, type PopoverSide, type PopoverViewport, applyPanelSizing, claimMenuFocus, closePopover, computePopoverLayout, initPopover, openPopover, readPanelSizing, resolvePanelSizing, revealPopoverItem, sizingToLayout };
@@ -1,10 +1,32 @@
1
1
  // src/behaviors/popover.ts
2
+ function revealPopoverItem(item, scrollport, opts = {}) {
3
+ if (typeof document === "undefined") return;
4
+ if (opts.focus) item.focus({ preventScroll: true });
5
+ const rect = item.getBoundingClientRect(), bounds = scrollport.getBoundingClientRect();
6
+ const style = getComputedStyle(scrollport);
7
+ const inset = (value, extent) => {
8
+ const amount = Number.parseFloat(value) || 0;
9
+ return value.endsWith("%") ? amount * extent / 100 : amount;
10
+ };
11
+ if (opts.axis !== "inline") {
12
+ const top = bounds.top + scrollport.clientTop + inset(style.scrollPaddingTop, scrollport.clientHeight);
13
+ const bottom = bounds.top + scrollport.clientTop + scrollport.clientHeight - inset(style.scrollPaddingBottom, scrollport.clientHeight);
14
+ if (rect.top < top) scrollport.scrollTop -= top - rect.top;
15
+ else if (rect.bottom > bottom) scrollport.scrollTop += rect.bottom - bottom;
16
+ }
17
+ if (opts.axis === "inline" || opts.axis === "both") {
18
+ const left = bounds.left + scrollport.clientLeft + inset(style.scrollPaddingLeft, scrollport.clientWidth);
19
+ const right = bounds.left + scrollport.clientLeft + scrollport.clientWidth - inset(style.scrollPaddingRight, scrollport.clientWidth);
20
+ if (rect.left < left) scrollport.scrollLeft -= left - rect.left;
21
+ else if (rect.right > right) scrollport.scrollLeft += rect.right - right;
22
+ }
23
+ }
2
24
  var PANEL_SIZING_ATTRS = {
3
- width: "data-panel-width",
4
- minWidth: "data-panel-min",
5
- maxWidth: "data-panel-max",
6
- minHeight: "data-panel-min-height",
7
- maxHeight: "data-panel-max-height"
25
+ width: "data-icen-panel-width",
26
+ minWidth: "data-icen-panel-min",
27
+ maxWidth: "data-icen-panel-max",
28
+ minHeight: "data-icen-panel-min-height",
29
+ maxHeight: "data-icen-panel-max-height"
8
30
  };
9
31
  function readPanelSizing(el) {
10
32
  const out = {};
@@ -85,10 +107,10 @@ function computePopoverLayout(anchor, viewport, opts = {}) {
85
107
  const preferredSide = opts.side ?? "top";
86
108
  const align = opts.align ?? "start";
87
109
  if (preferredSide === "left" || preferredSide === "right") {
110
+ const minWidth2 = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
88
111
  const availableLeft = Math.max(0, anchor.left - margin - offset);
89
112
  const availableRight = Math.max(0, viewport.width - anchor.right - margin - offset);
90
- const side2 = (preferredSide === "left" ? availableLeft : availableRight) >= DEFAULT_MIN_WIDTH ? preferredSide : preferredSide === "left" ? "right" : "left";
91
- const minWidth2 = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
113
+ const side2 = (preferredSide === "left" ? availableLeft : availableRight) >= minWidth2 ? preferredSide : preferredSide === "left" ? "right" : "left";
92
114
  const maxWidth2 = Math.min(
93
115
  Math.max(opts.maxWidth ?? DEFAULT_MAX_WIDTH, minWidth2),
94
116
  side2 === "left" ? availableLeft : availableRight
@@ -117,7 +139,7 @@ function computePopoverLayout(anchor, viewport, opts = {}) {
117
139
  const minHeight = Math.min(Math.max(80, opts.minHeight ?? DEFAULT_MIN_HEIGHT), availableHeight);
118
140
  const requestedMaxHeight = Math.max(minHeight, opts.maxHeight ?? DEFAULT_MAX_HEIGHT);
119
141
  const fallbackHeight = Math.min(260, requestedMaxHeight);
120
- const contentHeight = Math.max(opts.contentHeightHint ?? 0, fallbackHeight);
142
+ const contentHeight = opts.contentHeightHint != null && opts.contentHeightHint > 0 ? opts.contentHeightHint : fallbackHeight;
121
143
  const desiredHeight = clamp(contentHeight, minHeight, Math.min(requestedMaxHeight, availableHeight));
122
144
  const side = chooseSide(preferredSide, availableTop, availableBottom, desiredHeight);
123
145
  const sideSpace = side === "top" ? availableTop : availableBottom;
@@ -134,6 +156,16 @@ function computePopoverLayout(anchor, viewport, opts = {}) {
134
156
  return { side, left, top, width, maxHeight };
135
157
  }
136
158
  var openPanels = /* @__PURE__ */ new Map();
159
+ var closingPanels = /* @__PURE__ */ new Map();
160
+ var EXIT_FALLBACK_MS = 240;
161
+ var menuCloser = null;
162
+ function claimMenuFocus(close) {
163
+ menuCloser?.();
164
+ menuCloser = close;
165
+ return () => {
166
+ if (menuCloser === close) menuCloser = null;
167
+ };
168
+ }
137
169
  function rectFromElement(el) {
138
170
  const r = el.getBoundingClientRect();
139
171
  return { left: r.left, top: r.top, right: r.right, bottom: r.bottom, width: r.width, height: r.height };
@@ -151,21 +183,54 @@ function relayout(state) {
151
183
  closePopover(state.panel);
152
184
  return;
153
185
  }
154
- const layout = computePopoverLayout(
186
+ let layout = computePopoverLayout(
155
187
  anchor,
156
188
  { width: window.innerWidth, height: window.innerHeight },
157
189
  state.opts
158
190
  );
159
191
  const { panel } = state;
192
+ const scrollPositions = [panel, ...panel.querySelectorAll("*")].filter((node) => node.scrollTop !== 0 || node.scrollLeft !== 0).map((node) => ({ node, top: node.scrollTop, left: node.scrollLeft }));
193
+ panel.style.width = `${layout.width}px`;
194
+ if (state.opts.contentHeightHint == null && (layout.side === "top" || layout.side === "bottom")) {
195
+ panel.style.maxHeight = `${Math.min(state.opts.maxHeight ?? DEFAULT_MAX_HEIGHT, Math.max(80, window.innerHeight - (state.opts.margin ?? 12) * 2))}px`;
196
+ const measured = panel.offsetHeight;
197
+ if (measured > 0) layout = computePopoverLayout(anchor, { width: window.innerWidth, height: window.innerHeight }, { ...state.opts, contentHeightHint: measured });
198
+ }
160
199
  panel.style.left = `${layout.left}px`;
161
- panel.style.top = `${layout.top}px`;
162
200
  panel.style.width = `${layout.width}px`;
163
201
  panel.style.maxHeight = `${layout.maxHeight}px`;
164
- panel.dataset.side = layout.side;
202
+ let top = layout.top;
203
+ const actualHeight = panel.offsetHeight;
204
+ if (actualHeight > 0 && (layout.side === "top" || layout.side === "bottom")) {
205
+ const desired = layout.side === "top" ? anchor.top - (state.opts.offset ?? 8) - actualHeight : anchor.bottom + (state.opts.offset ?? 8);
206
+ const margin = state.opts.margin ?? 12;
207
+ top = clamp(desired, margin, window.innerHeight - margin - actualHeight);
208
+ }
209
+ panel.style.top = `${top}px`;
210
+ for (const pos of scrollPositions) {
211
+ pos.node.scrollTop = pos.top;
212
+ pos.node.scrollLeft = pos.left;
213
+ }
214
+ panel.dataset.icenSide = layout.side;
165
215
  panel.style.visibility = state.prevVisibility;
166
216
  }
167
- function onWindowChange() {
168
- openPanels.forEach(relayout);
217
+ function observeOpenPanel(state) {
218
+ if (typeof ResizeObserver === "undefined") return;
219
+ state.resizeObserver = new ResizeObserver(() => {
220
+ if (state.layoutFrame != null) return;
221
+ state.layoutFrame = window.requestAnimationFrame(() => {
222
+ state.layoutFrame = void 0;
223
+ if (openPanels.get(state.panel) === state) relayout(state);
224
+ });
225
+ });
226
+ state.resizeObserver.observe(state.panel);
227
+ if (state.anchor instanceof Element) state.resizeObserver.observe(state.anchor);
228
+ }
229
+ function onWindowChange(event) {
230
+ openPanels.forEach((state) => {
231
+ if (event?.type === "scroll" && event.target instanceof Node && state.panel.contains(event.target)) return;
232
+ relayout(state);
233
+ });
169
234
  }
170
235
  function onOutsidePointerDown(e) {
171
236
  const target = e.target;
@@ -177,18 +242,40 @@ function onOutsidePointerDown(e) {
177
242
  });
178
243
  }
179
244
  function onKeyDown(e) {
180
- if (e.key !== "Escape") return;
245
+ if (e.key !== "Escape" || e.defaultPrevented || e.isComposing || e.keyCode === 229 || menuCloser) return;
181
246
  const states = Array.from(openPanels.values());
182
247
  const top = states[states.length - 1];
183
248
  if (top) {
184
249
  e.preventDefault();
250
+ e.stopPropagation();
185
251
  closePopover(top.panel);
186
252
  }
187
253
  }
188
254
  function openPopover(panel, options) {
189
255
  if (typeof document === "undefined") return;
256
+ const pending = closingPanels.get(panel);
257
+ if (pending) {
258
+ window.clearTimeout(pending.timer);
259
+ panel.removeEventListener("transitionend", pending.onEnd);
260
+ closingPanels.delete(panel);
261
+ delete panel.dataset.icenState;
262
+ openPanels.set(panel, pending.state);
263
+ panel.hidden = false;
264
+ panel.style.visibility = "hidden";
265
+ relayout(pending.state);
266
+ observeOpenPanel(pending.state);
267
+ if (openPanels.size === 1) {
268
+ window.addEventListener("resize", onWindowChange);
269
+ window.addEventListener("scroll", onWindowChange, true);
270
+ document.addEventListener("pointerdown", onOutsidePointerDown, true);
271
+ window.addEventListener("keydown", onKeyDown, true);
272
+ }
273
+ const ifs = options.initialFocus;
274
+ if (ifs && panel.contains(ifs)) ifs.focus({ preventScroll: true });
275
+ return;
276
+ }
190
277
  if (openPanels.has(panel)) closePopover(panel);
191
- const { anchor, onClose, sizing, ...rest } = options;
278
+ const { anchor, onClose, sizing, initialFocus, restoreFocus, ...rest } = options;
192
279
  const layoutOpts = sizingToLayout(resolvePanelSizing(panel, sizing), rest);
193
280
  const placeholder = document.createComment("popover-anchor");
194
281
  panel.parentNode?.insertBefore(placeholder, panel);
@@ -201,25 +288,25 @@ function openPopover(panel, options) {
201
288
  onClose,
202
289
  placeholder,
203
290
  wasHidden,
204
- prevVisibility
291
+ prevVisibility,
292
+ returnFocus: restoreFocus === false ? null : anchor instanceof HTMLElement ? anchor : document.activeElement instanceof HTMLElement ? document.activeElement : null
205
293
  };
206
294
  openPanels.set(panel, state);
207
295
  document.body.appendChild(panel);
208
296
  panel.hidden = false;
209
297
  panel.style.visibility = "hidden";
210
298
  relayout(state);
299
+ observeOpenPanel(state);
300
+ if (initialFocus && panel.contains(initialFocus)) initialFocus.focus({ preventScroll: true });
211
301
  if (openPanels.size === 1) {
212
302
  window.addEventListener("resize", onWindowChange);
213
303
  window.addEventListener("scroll", onWindowChange, true);
214
304
  document.addEventListener("pointerdown", onOutsidePointerDown, true);
215
- window.addEventListener("keydown", onKeyDown);
305
+ window.addEventListener("keydown", onKeyDown, true);
216
306
  }
217
307
  }
218
- function closePopover(panel) {
219
- if (typeof document === "undefined") return;
220
- const state = openPanels.get(panel);
221
- if (!state) return;
222
- openPanels.delete(panel);
308
+ function finalizeClose(panel, state) {
309
+ delete panel.dataset.icenState;
223
310
  panel.style.visibility = state.prevVisibility;
224
311
  if (typeof state.wasHidden === "string") panel.setAttribute("hidden", state.wasHidden);
225
312
  else panel.hidden = state.wasHidden;
@@ -227,25 +314,139 @@ function closePopover(panel) {
227
314
  panel.style.top = "";
228
315
  panel.style.width = "";
229
316
  panel.style.maxHeight = "";
230
- delete panel.dataset.side;
317
+ delete panel.dataset.icenSide;
231
318
  if (state.placeholder.parentNode) {
232
319
  state.placeholder.parentNode.insertBefore(panel, state.placeholder);
233
320
  state.placeholder.remove();
234
321
  }
322
+ }
323
+ function closePopover(panel, opts) {
324
+ if (typeof document === "undefined") return;
325
+ const state = openPanels.get(panel);
326
+ if (!state) return;
327
+ openPanels.delete(panel);
328
+ state.resizeObserver?.disconnect();
329
+ if (state.layoutFrame != null) {
330
+ window.cancelAnimationFrame(state.layoutFrame);
331
+ state.layoutFrame = void 0;
332
+ }
333
+ const ownsFocus = panel.contains(document.activeElement);
334
+ if (ownsFocus && state.returnFocus?.isConnected) state.returnFocus.focus({ preventScroll: true });
235
335
  if (openPanels.size === 0) {
236
336
  window.removeEventListener("resize", onWindowChange);
237
337
  window.removeEventListener("scroll", onWindowChange, true);
238
338
  document.removeEventListener("pointerdown", onOutsidePointerDown, true);
239
- window.removeEventListener("keydown", onKeyDown);
339
+ window.removeEventListener("keydown", onKeyDown, true);
240
340
  }
241
341
  state.onClose?.();
342
+ const calm = opts?.immediate || typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
343
+ if (calm || !panel.isConnected) {
344
+ finalizeClose(panel, state);
345
+ return;
346
+ }
347
+ panel.dataset.icenState = "closing";
348
+ const finalize = () => {
349
+ const pending = closingPanels.get(panel);
350
+ if (!pending || pending.state !== state) return;
351
+ window.clearTimeout(pending.timer);
352
+ panel.removeEventListener("transitionend", pending.onEnd);
353
+ closingPanels.delete(panel);
354
+ finalizeClose(panel, state);
355
+ };
356
+ const onEnd = (event) => {
357
+ if (event.target === panel && event.propertyName === "opacity") finalize();
358
+ };
359
+ panel.addEventListener("transitionend", onEnd);
360
+ closingPanels.set(panel, {
361
+ state,
362
+ timer: window.setTimeout(finalize, EXIT_FALLBACK_MS),
363
+ onEnd
364
+ });
365
+ }
366
+ var popoverScopes = /* @__PURE__ */ new WeakMap();
367
+ var popoverDefaults = /* @__PURE__ */ new WeakMap();
368
+ function rememberPopoverDefaults(panel) {
369
+ let defaults = popoverDefaults.get(panel);
370
+ if (!defaults) {
371
+ const side = panel.dataset.icenSide;
372
+ const align = panel.getAttribute("data-icen-align");
373
+ defaults = {
374
+ side: side === "top" || side === "bottom" || side === "left" || side === "right" ? side : void 0,
375
+ align: align === "start" || align === "center" || align === "end" ? align : void 0
376
+ };
377
+ popoverDefaults.set(panel, defaults);
378
+ }
379
+ return defaults;
380
+ }
381
+ function initPopover(root) {
382
+ if (typeof document === "undefined") return () => {
383
+ };
384
+ const scope = root ?? document;
385
+ const existing = popoverScopes.get(scope);
386
+ if (existing) return existing;
387
+ const opened = /* @__PURE__ */ new Map();
388
+ const dismissHandlers = /* @__PURE__ */ new Map();
389
+ const watchDismiss = (panel) => {
390
+ if (dismissHandlers.has(panel)) return;
391
+ const handler = (event) => {
392
+ const target = event.target;
393
+ if (!(target instanceof Element)) return;
394
+ if (target.closest(".icen-popover-close, [data-icen-popover-dismiss]")) closePopover(panel);
395
+ };
396
+ panel.addEventListener("click", handler);
397
+ dismissHandlers.set(panel, handler);
398
+ };
399
+ const onClick = (event) => {
400
+ const target = event.target;
401
+ if (!(target instanceof Element)) return;
402
+ const trigger = target.closest("[data-icen-popover-target]");
403
+ if (!trigger || !scope.contains(trigger)) return;
404
+ const id = trigger.getAttribute("data-icen-popover-target") ?? "";
405
+ const panel = document.getElementById(id);
406
+ if (!panel || !panel.classList.contains("icen-popover")) return;
407
+ if (openPanels.has(panel)) {
408
+ closePopover(panel);
409
+ return;
410
+ }
411
+ const defaults = rememberPopoverDefaults(panel);
412
+ const sideAttr = trigger.getAttribute("data-icen-side");
413
+ const alignAttr = trigger.getAttribute("data-icen-align");
414
+ if (!trigger.hasAttribute("aria-haspopup")) trigger.setAttribute("aria-haspopup", "dialog");
415
+ trigger.setAttribute("aria-expanded", "true");
416
+ if (panel.id) trigger.setAttribute("aria-controls", panel.id);
417
+ opened.set(panel, trigger);
418
+ watchDismiss(panel);
419
+ openPopover(panel, {
420
+ anchor: trigger,
421
+ side: sideAttr ?? defaults.side ?? "bottom",
422
+ align: alignAttr ?? defaults.align ?? "start",
423
+ initialFocus: panel.querySelector("[data-icen-autofocus], [autofocus]") ?? void 0,
424
+ onClose: () => {
425
+ opened.delete(panel);
426
+ trigger.setAttribute("aria-expanded", "false");
427
+ }
428
+ });
429
+ };
430
+ scope.addEventListener("click", onClick);
431
+ const dispose = () => {
432
+ scope.removeEventListener("click", onClick);
433
+ for (const panel of [...opened.keys()]) closePopover(panel, { immediate: true });
434
+ for (const [panel, handler] of dismissHandlers) panel.removeEventListener("click", handler);
435
+ dismissHandlers.clear();
436
+ popoverScopes.delete(scope);
437
+ };
438
+ popoverScopes.set(scope, dispose);
439
+ return dispose;
242
440
  }
243
441
  export {
244
442
  applyPanelSizing,
443
+ claimMenuFocus,
245
444
  closePopover,
246
445
  computePopoverLayout,
446
+ initPopover,
247
447
  openPopover,
248
448
  readPanelSizing,
249
449
  resolvePanelSizing,
450
+ revealPopoverItem,
250
451
  sizingToLayout
251
452
  };
@@ -1,6 +1,9 @@
1
- /** 为 root 下每个 [data-select] 容器初始化(root 自身是 [data-select] 也算)。
1
+ /** 为 root 下每个 [data-icen-select] 容器初始化(root 自身是 [data-icen-select] 也算)。
2
2
  * 返回销毁函数:摘除本次初始化容器的全部监听并复位幂等标记(可重新 init);
3
3
  * 最后一个容器销毁时同时摘除 document 级单例委托。 */
4
4
  declare function initSelect(root?: ParentNode): () => void;
5
+ /** Resync authored selection classes, labels and form values without emitting a
6
+ * user-change event. Child/text/native-disabled changes are observed automatically. */
7
+ declare function refreshSelect(root?: ParentNode): void;
5
8
 
6
- export { initSelect };
9
+ export { initSelect, refreshSelect };