@icen.ai/ui 0.9.0 → 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.
- package/AGENTS.md +138 -80
- package/README.md +66 -91
- package/dist/base.css +208 -179
- package/dist/behaviors/accordion.mjs +8 -8
- package/dist/behaviors/ai-branch.d.mts +1 -1
- package/dist/behaviors/ai-branch.mjs +1010 -94
- package/dist/behaviors/ai-chat.d.mts +7 -7
- package/dist/behaviors/ai-chat.mjs +1071 -161
- package/dist/behaviors/ai-composer.d.mts +44 -12
- package/dist/behaviors/ai-composer.mjs +1997 -524
- package/dist/behaviors/ai-core.d.mts +1 -1
- package/dist/behaviors/ai-core.mjs +1016 -107
- package/dist/behaviors/ai-diff.mjs +1019 -127
- package/dist/behaviors/ai-feedback.d.mts +1 -1
- package/dist/behaviors/ai-feedback.mjs +1035 -126
- package/dist/behaviors/ai-panel.d.mts +8 -8
- package/dist/behaviors/ai-panel.mjs +1284 -266
- package/dist/behaviors/ai-provider.mjs +1003 -101
- package/dist/behaviors/ai-threads.d.mts +1 -1
- package/dist/behaviors/ai-threads.mjs +1068 -154
- package/dist/behaviors/ai-tool.d.mts +2 -2
- package/dist/behaviors/ai-tool.mjs +2037 -396
- package/dist/behaviors/ai-tools.d.mts +89 -16
- package/dist/behaviors/ai-tools.mjs +4448 -372
- package/dist/behaviors/autocomplete.d.mts +53 -0
- package/dist/behaviors/autocomplete.mjs +1600 -0
- package/dist/behaviors/back-top.d.mts +1 -1
- package/dist/behaviors/back-top.mjs +951 -14
- package/dist/behaviors/carousel.d.mts +1 -1
- package/dist/behaviors/carousel.mjs +906 -11
- package/dist/behaviors/cascader.d.mts +48 -0
- package/dist/behaviors/cascader.mjs +1853 -0
- package/dist/behaviors/charts.d.mts +72 -8
- package/dist/behaviors/charts.mjs +1901 -256
- package/dist/behaviors/code-block.d.mts +36 -0
- package/dist/behaviors/code-block.mjs +1611 -0
- package/dist/behaviors/command-palette.d.mts +9 -6
- package/dist/behaviors/command-palette.mjs +161 -59
- package/dist/behaviors/context-menu.d.mts +9 -8
- package/dist/behaviors/context-menu.mjs +1112 -44
- package/dist/behaviors/controls.d.mts +13 -13
- package/dist/behaviors/controls.mjs +989 -130
- package/dist/behaviors/copy.d.mts +21 -2
- package/dist/behaviors/copy.mjs +1206 -42
- package/dist/behaviors/datatable.d.mts +53 -3
- package/dist/behaviors/datatable.mjs +1963 -207
- package/dist/behaviors/date-picker.d.mts +0 -1
- package/dist/behaviors/date-picker.mjs +1340 -293
- package/dist/behaviors/design.d.mts +54 -0
- package/dist/behaviors/design.mjs +109 -0
- package/dist/behaviors/dropdown.d.mts +5 -6
- package/dist/behaviors/dropdown.mjs +262 -110
- package/dist/behaviors/events.d.mts +120 -3
- package/dist/behaviors/events.mjs +47 -2
- package/dist/behaviors/feedback.d.mts +30 -0
- package/dist/behaviors/feedback.mjs +296 -0
- package/dist/behaviors/focus.d.mts +8 -0
- package/dist/behaviors/focus.mjs +24 -0
- package/dist/behaviors/form-validate.d.mts +102 -0
- package/dist/behaviors/form-validate.mjs +6091 -0
- package/dist/behaviors/i18n.d.mts +789 -0
- package/dist/behaviors/i18n.mjs +1711 -0
- package/dist/behaviors/icons.d.mts +93 -0
- package/dist/behaviors/icons.mjs +113 -0
- package/dist/behaviors/input.d.mts +9 -1
- package/dist/behaviors/input.mjs +1056 -60
- package/dist/behaviors/kb-access.d.mts +1 -1
- package/dist/behaviors/kb-access.mjs +2129 -239
- package/dist/behaviors/kb-acl.d.mts +6 -6
- package/dist/behaviors/kb-acl.mjs +1119 -179
- package/dist/behaviors/kb-answer.mjs +2070 -418
- package/dist/behaviors/kb-audit.d.mts +1 -1
- package/dist/behaviors/kb-audit.mjs +1076 -154
- package/dist/behaviors/kb-canvas.mjs +1100 -177
- package/dist/behaviors/kb-chain.mjs +1022 -100
- package/dist/behaviors/kb-checkpoint.d.mts +1 -1
- package/dist/behaviors/kb-checkpoint.mjs +1319 -169
- package/dist/behaviors/kb-chunks.mjs +1152 -213
- package/dist/behaviors/kb-citation.d.mts +1 -1
- package/dist/behaviors/kb-citation.mjs +1201 -179
- package/dist/behaviors/kb-clarify.mjs +1058 -130
- package/dist/behaviors/kb-conflict.mjs +1041 -119
- package/dist/behaviors/kb-connector.mjs +1104 -176
- package/dist/behaviors/kb-core.d.mts +6 -1
- package/dist/behaviors/kb-core.mjs +1077 -162
- package/dist/behaviors/kb-eval.mjs +1971 -144
- package/dist/behaviors/kb-explain.d.mts +1 -1
- package/dist/behaviors/kb-explain.mjs +1024 -102
- package/dist/behaviors/kb-filter.mjs +2008 -178
- package/dist/behaviors/kb-gap.d.mts +1 -1
- package/dist/behaviors/kb-gap.mjs +1043 -121
- package/dist/behaviors/kb-hittest.d.mts +1 -1
- package/dist/behaviors/kb-hittest.mjs +1093 -150
- package/dist/behaviors/kb-metadata.mjs +1052 -130
- package/dist/behaviors/kb-passage.d.mts +2 -2
- package/dist/behaviors/kb-passage.mjs +1101 -161
- package/dist/behaviors/kb-pipeline.d.mts +2 -2
- package/dist/behaviors/kb-pipeline.mjs +1082 -162
- package/dist/behaviors/kb-qa.mjs +1088 -160
- package/dist/behaviors/kb-rerank.mjs +1047 -114
- package/dist/behaviors/kb-retrieval.mjs +2063 -224
- package/dist/behaviors/kb-review.mjs +1990 -159
- package/dist/behaviors/kb-sandbox.d.mts +1 -1
- package/dist/behaviors/kb-sandbox.mjs +1026 -104
- package/dist/behaviors/kb-segment.mjs +1111 -179
- package/dist/behaviors/kb-select.d.mts +24 -0
- package/dist/behaviors/kb-select.mjs +1989 -0
- package/dist/behaviors/kb-sources.d.mts +1 -1
- package/dist/behaviors/kb-sources.mjs +1084 -170
- package/dist/behaviors/kb-sql.d.mts +1 -1
- package/dist/behaviors/kb-sql.mjs +1143 -226
- package/dist/behaviors/kb-trace.d.mts +1 -1
- package/dist/behaviors/kb-trace.mjs +1042 -128
- package/dist/behaviors/kb-visibility.mjs +1086 -158
- package/dist/behaviors/kb-who-can.mjs +1974 -137
- package/dist/behaviors/mentions.d.mts +93 -0
- package/dist/behaviors/mentions.mjs +2152 -0
- package/dist/behaviors/modal.d.mts +4 -3
- package/dist/behaviors/modal.mjs +1055 -33
- package/dist/behaviors/nav.d.mts +1 -1
- package/dist/behaviors/nav.mjs +6 -6
- package/dist/behaviors/notification.d.mts +16 -8
- package/dist/behaviors/notification.mjs +1615 -265
- package/dist/behaviors/panel.d.mts +4 -0
- package/dist/behaviors/panel.mjs +994 -0
- package/dist/behaviors/popover.d.mts +28 -5
- package/dist/behaviors/popover.mjs +225 -24
- package/dist/behaviors/select.d.mts +5 -2
- package/dist/behaviors/select.mjs +1410 -117
- package/dist/behaviors/sidebar.d.mts +1 -1
- package/dist/behaviors/sidebar.mjs +13 -13
- package/dist/behaviors/slider.d.mts +1 -1
- package/dist/behaviors/slider.mjs +9 -9
- package/dist/behaviors/split-pane.d.mts +1 -1
- package/dist/behaviors/split-pane.mjs +7 -7
- package/dist/behaviors/suggestions.d.mts +27 -0
- package/dist/behaviors/suggestions.mjs +1089 -0
- package/dist/behaviors/svg-parse.d.mts +14 -0
- package/dist/behaviors/svg-parse.mjs +37 -0
- package/dist/behaviors/tabs.d.mts +2 -2
- package/dist/behaviors/tabs.mjs +25 -25
- package/dist/behaviors/tag-input.d.mts +23 -5
- package/dist/behaviors/tag-input.mjs +1359 -104
- package/dist/behaviors/theme-switcher.d.mts +24 -0
- package/dist/behaviors/theme-switcher.mjs +1981 -0
- package/dist/behaviors/theme.d.mts +15 -8
- package/dist/behaviors/theme.mjs +390 -28
- package/dist/behaviors/toast.d.mts +24 -25
- package/dist/behaviors/toast.mjs +1266 -88
- package/dist/behaviors/tooltip.d.mts +4 -0
- package/dist/behaviors/tooltip.mjs +378 -0
- package/dist/behaviors/tree-select.d.mts +69 -0
- package/dist/behaviors/tree-select.mjs +1907 -0
- package/dist/behaviors/tree.d.mts +1 -1
- package/dist/behaviors/tree.mjs +15 -15
- package/dist/behaviors/upload.d.mts +3 -2
- package/dist/behaviors/upload.mjs +1076 -66
- package/dist/cli.mjs +5 -6
- package/dist/components/ai-chat.css +666 -573
- package/dist/components/ai-diff.css +153 -132
- package/dist/components/ai-panel.css +351 -343
- package/dist/components/ai-tool.css +393 -238
- package/dist/components/autocomplete.css +61 -0
- package/dist/components/autocomplete.mjs +3 -0
- package/dist/components/back-top.css +34 -31
- package/dist/components/badge.css +44 -41
- package/dist/components/breadcrumb.css +67 -46
- package/dist/components/btn.css +80 -74
- package/dist/components/card.css +117 -106
- package/dist/components/cascader.css +311 -0
- package/dist/components/cascader.mjs +3 -0
- package/dist/components/chart-area.css +8 -2
- package/dist/components/chart-bar.css +95 -33
- package/dist/components/chart-gauge.css +14 -11
- package/dist/components/chart-graph.css +20 -17
- package/dist/components/chart-heatmap.css +96 -90
- package/dist/components/chart-line.css +8 -3
- package/dist/components/chart-pie.css +9 -6
- package/dist/components/chart-radar.css +8 -5
- package/dist/components/chart-scatter.css +9 -6
- package/dist/components/chart-sparkline.css +5 -2
- package/dist/components/chart-stack.css +14 -11
- package/dist/components/charts.css +164 -73
- package/dist/components/code-block.css +418 -0
- package/dist/components/code-block.mjs +4 -0
- package/dist/components/command-palette.css +138 -117
- package/dist/components/content.css +257 -251
- package/dist/components/copy.css +72 -44
- package/dist/components/datatable.css +423 -181
- package/dist/components/date-picker.css +135 -104
- package/dist/components/empty.css +68 -65
- package/dist/components/feedback.css +201 -183
- package/dist/components/form-validate.css +120 -0
- package/dist/components/form-validate.mjs +3 -0
- package/dist/components/form.css +150 -127
- package/dist/components/icon.css +102 -0
- package/dist/components/icon.mjs +3 -0
- package/dist/components/input.css +159 -104
- package/dist/components/kb-agent.css +188 -173
- package/dist/components/kb-data.css +257 -253
- package/dist/components/kb-ground.css +197 -185
- package/dist/components/kb-ingest.css +334 -350
- package/dist/components/kb-ops.css +265 -259
- package/dist/components/kb-perm.css +260 -271
- package/dist/components/kb-search.css +317 -360
- package/dist/components/kb.css +120 -109
- package/dist/components/layout.css +85 -82
- package/dist/components/material-chinese.css +73 -0
- package/dist/components/material-soviet.css +76 -0
- package/dist/components/material.css +275 -0
- package/dist/components/media.css +220 -166
- package/dist/components/mentions.css +189 -0
- package/dist/components/mentions.mjs +3 -0
- package/dist/components/menu.css +113 -105
- package/dist/components/modal.css +107 -112
- package/dist/components/nav.css +118 -115
- package/dist/components/notification.css +238 -174
- package/dist/components/pagination.css +56 -53
- package/dist/components/panel.css +66 -63
- package/dist/components/panel.mjs +1 -0
- package/dist/components/pill.css +81 -71
- package/dist/components/popover.css +67 -53
- package/dist/components/scroll-area.css +113 -110
- package/dist/components/segmented.css +146 -143
- package/dist/components/select.css +206 -186
- package/dist/components/sidebar.css +137 -127
- package/dist/components/slider.css +117 -114
- package/dist/components/split-pane.css +112 -109
- package/dist/components/stat.css +112 -106
- package/dist/components/steps.css +109 -103
- package/dist/components/switch.css +138 -134
- package/dist/components/table.css +131 -107
- package/dist/components/tabs.css +114 -108
- package/dist/components/tag-input.css +48 -133
- package/dist/components/tag.css +81 -71
- package/dist/components/theme-switcher.css +105 -0
- package/dist/components/theme-switcher.mjs +3 -0
- package/dist/components/toast.css +100 -86
- package/dist/components/toolbar.css +45 -42
- package/dist/components/tooltip.css +102 -43
- package/dist/components/tooltip.mjs +1 -0
- package/dist/components/tree-select.css +416 -0
- package/dist/components/tree-select.mjs +3 -0
- package/dist/components/upload.css +135 -92
- package/dist/fonts/LICENSES/ark-pixel/OFL.txt +96 -0
- package/dist/fonts/LICENSES/cubic-11/OFL.txt +108 -0
- package/dist/fonts/LICENSES/galmuri/LICENSE.txt +93 -0
- package/dist/fonts/OFL.txt +96 -0
- package/dist/fonts/README.md +14 -0
- package/dist/fonts/fusion-pixel-12px-zh-hans.woff2 +0 -0
- package/dist/index.d.mts +28 -14
- package/dist/index.mjs +20705 -9601
- package/dist/react.d.mts +91 -0
- package/dist/react.mjs +8208 -0
- package/dist/registry.json +130 -13
- package/dist/tokens/chinese.css +44 -0
- package/dist/tokens/colors.css +365 -1222
- package/dist/tokens/design.css +109 -0
- package/dist/tokens/dtcg.json +11198 -0
- package/dist/tokens/figma.variables.json +7017 -0
- package/dist/tokens/icon-masks.css +78 -0
- package/dist/tokens/pixel.css +50 -0
- package/dist/tokens/soviet.css +44 -0
- package/dist/tokens/style-profiles.css +36 -547
- package/dist/tokens/themes.json +308 -0
- package/dist/tokens/tokens-studio.json +6728 -0
- package/dist/tokens/typography.css +21 -18
- package/dist/tokens.css +1555 -1801
- package/dist/ui.css +11975 -9831
- package/dist/vue.d.mts +344 -0
- package/dist/vue.mjs +7781 -0
- package/docs/spec/ai-input-experience.md +75 -0
- package/docs/spec/ai-native.md +238 -147
- package/docs/spec/design-philosophy.md +189 -0
- package/docs/spec/design-primitives.md +196 -0
- package/docs/spec/kb-family.md +147 -115
- package/package.json +43 -8
- package/dist/retro-effects.css +0 -255
- package/dist/tokens/retro-effects.css +0 -255
- package/docs/research/2026-10-03-ai-kb-components.md +0 -392
- package/docs/research/2026-10-03-kb-permissions.md +0 -992
|
@@ -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
|
-
/**
|
|
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
|
-
/**
|
|
80
|
-
|
|
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) >=
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
168
|
-
|
|
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
|
|
219
|
-
|
|
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.
|
|
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 };
|