@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
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
// src/behaviors/popover.ts
|
|
2
|
+
var DEFAULT_MIN_WIDTH = 220;
|
|
3
|
+
var DEFAULT_MAX_WIDTH = 420;
|
|
4
|
+
var DEFAULT_MIN_HEIGHT = 120;
|
|
5
|
+
var DEFAULT_MAX_HEIGHT = 420;
|
|
6
|
+
function clamp(value, min, max) {
|
|
7
|
+
if (max < min) return min;
|
|
8
|
+
return Math.min(max, Math.max(min, value));
|
|
9
|
+
}
|
|
10
|
+
function chooseSide(preferredSide, availableTop, availableBottom, desiredHeight) {
|
|
11
|
+
const preferredSpace = preferredSide === "top" ? availableTop : availableBottom;
|
|
12
|
+
const oppositeSpace = preferredSide === "top" ? availableBottom : availableTop;
|
|
13
|
+
if (preferredSpace >= desiredHeight) return preferredSide;
|
|
14
|
+
if (oppositeSpace >= desiredHeight) return preferredSide === "top" ? "bottom" : "top";
|
|
15
|
+
if (preferredSpace >= oppositeSpace) return preferredSide;
|
|
16
|
+
return preferredSide === "top" ? "bottom" : "top";
|
|
17
|
+
}
|
|
18
|
+
function horizontalAnchor(anchor, width, align) {
|
|
19
|
+
if (align === "center") return anchor.left + anchor.width / 2 - width / 2;
|
|
20
|
+
if (align === "end") return anchor.right - width;
|
|
21
|
+
return anchor.left;
|
|
22
|
+
}
|
|
23
|
+
function computePopoverLayout(anchor, viewport, opts = {}) {
|
|
24
|
+
const margin = opts.margin ?? 12;
|
|
25
|
+
const offset = opts.offset ?? 8;
|
|
26
|
+
const preferredSide = opts.side ?? "top";
|
|
27
|
+
const align = opts.align ?? "start";
|
|
28
|
+
if (preferredSide === "left" || preferredSide === "right") {
|
|
29
|
+
const minWidth2 = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
30
|
+
const availableLeft = Math.max(0, anchor.left - margin - offset);
|
|
31
|
+
const availableRight = Math.max(0, viewport.width - anchor.right - margin - offset);
|
|
32
|
+
const side2 = (preferredSide === "left" ? availableLeft : availableRight) >= minWidth2 ? preferredSide : preferredSide === "left" ? "right" : "left";
|
|
33
|
+
const maxWidth2 = Math.min(
|
|
34
|
+
Math.max(opts.maxWidth ?? DEFAULT_MAX_WIDTH, minWidth2),
|
|
35
|
+
side2 === "left" ? availableLeft : availableRight
|
|
36
|
+
);
|
|
37
|
+
const width2 = minWidth2;
|
|
38
|
+
const left2 = clamp(
|
|
39
|
+
side2 === "left" ? anchor.left - offset - width2 : anchor.right + offset,
|
|
40
|
+
margin,
|
|
41
|
+
viewport.width - margin - width2
|
|
42
|
+
);
|
|
43
|
+
const availableHeight2 = Math.max(80, viewport.height - margin * 2);
|
|
44
|
+
const maxHeight2 = Math.min(opts.maxHeight ?? DEFAULT_MAX_HEIGHT, availableHeight2);
|
|
45
|
+
const desired = clamp(opts.contentHeightHint ?? Math.min(anchor.height, maxHeight2), 40, maxHeight2);
|
|
46
|
+
const anchorTop = align === "center" ? anchor.top + anchor.height / 2 - desired / 2 : align === "end" ? anchor.bottom - desired : anchor.top;
|
|
47
|
+
const top2 = clamp(anchorTop, margin, viewport.height - margin - desired);
|
|
48
|
+
return { side: side2, left: left2, top: top2, width: width2, maxHeight: maxHeight2 };
|
|
49
|
+
}
|
|
50
|
+
const availableWidth = Math.max(80, viewport.width - margin * 2);
|
|
51
|
+
const requestedMinWidth = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
52
|
+
const requestedMaxWidth = Math.max(requestedMinWidth, opts.maxWidth ?? DEFAULT_MAX_WIDTH);
|
|
53
|
+
const maxWidth = Math.min(requestedMaxWidth, availableWidth);
|
|
54
|
+
const minWidth = Math.min(requestedMinWidth, maxWidth);
|
|
55
|
+
const availableTop = Math.max(0, anchor.top - margin - offset);
|
|
56
|
+
const availableBottom = Math.max(0, viewport.height - anchor.bottom - margin - offset);
|
|
57
|
+
const availableHeight = Math.max(80, viewport.height - margin * 2);
|
|
58
|
+
const minHeight = Math.min(Math.max(80, opts.minHeight ?? DEFAULT_MIN_HEIGHT), availableHeight);
|
|
59
|
+
const requestedMaxHeight = Math.max(minHeight, opts.maxHeight ?? DEFAULT_MAX_HEIGHT);
|
|
60
|
+
const fallbackHeight = Math.min(260, requestedMaxHeight);
|
|
61
|
+
const contentHeight = opts.contentHeightHint != null && opts.contentHeightHint > 0 ? opts.contentHeightHint : fallbackHeight;
|
|
62
|
+
const desiredHeight = clamp(contentHeight, minHeight, Math.min(requestedMaxHeight, availableHeight));
|
|
63
|
+
const side = chooseSide(preferredSide, availableTop, availableBottom, desiredHeight);
|
|
64
|
+
const sideSpace = side === "top" ? availableTop : availableBottom;
|
|
65
|
+
const maxHeight = Math.min(
|
|
66
|
+
requestedMaxHeight,
|
|
67
|
+
Math.max(80, sideSpace || Math.max(availableTop, availableBottom)),
|
|
68
|
+
availableHeight
|
|
69
|
+
);
|
|
70
|
+
const positionedHeight = clamp(desiredHeight, 40, maxHeight);
|
|
71
|
+
const width = align === "stretch" ? clamp(anchor.width, minWidth, maxWidth) : maxWidth;
|
|
72
|
+
const left = clamp(horizontalAnchor(anchor, width, align), margin, viewport.width - margin - width);
|
|
73
|
+
const desiredTop = side === "top" ? anchor.top - offset - positionedHeight : anchor.bottom + offset;
|
|
74
|
+
const top = clamp(desiredTop, margin, viewport.height - margin - positionedHeight);
|
|
75
|
+
return { side, left, top, width, maxHeight };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/behaviors/tooltip.ts
|
|
79
|
+
var HOVER_DELAY_MS = 400;
|
|
80
|
+
var LEAVE_TIMEOUT_MS = 240;
|
|
81
|
+
var POINTER_LEAVE_DELAY_MS = 80;
|
|
82
|
+
var OFFSET_PX = 6;
|
|
83
|
+
var VIEWPORT_MARGIN_PX = 8;
|
|
84
|
+
var WATCHED_ATTRS = ["data-icen-tooltip", "data-icen-tooltip-side", "data-icen-tooltip-align", "data-icen-tooltip-multiline"];
|
|
85
|
+
var scopes = /* @__PURE__ */ new WeakMap();
|
|
86
|
+
var handled = /* @__PURE__ */ new WeakSet();
|
|
87
|
+
var bubble = null;
|
|
88
|
+
var current = null;
|
|
89
|
+
var dismissed = /* @__PURE__ */ new Set();
|
|
90
|
+
var triggerOwners = /* @__PURE__ */ new WeakMap();
|
|
91
|
+
var closeTimer = null;
|
|
92
|
+
var pending = null;
|
|
93
|
+
var leaving = null;
|
|
94
|
+
var observer = null;
|
|
95
|
+
var sequence = 0;
|
|
96
|
+
function textOf(trigger) {
|
|
97
|
+
return trigger.getAttribute("data-icen-tooltip")?.trim() ?? "";
|
|
98
|
+
}
|
|
99
|
+
function sideOf(trigger) {
|
|
100
|
+
const side = trigger.getAttribute("data-icen-tooltip-side");
|
|
101
|
+
return side === "bottom" || side === "left" || side === "right" ? side : "top";
|
|
102
|
+
}
|
|
103
|
+
function alignOf(trigger) {
|
|
104
|
+
const align = trigger.getAttribute("data-icen-tooltip-align");
|
|
105
|
+
return align === "start" || align === "end" ? align : "center";
|
|
106
|
+
}
|
|
107
|
+
function focusVisible(el) {
|
|
108
|
+
try {
|
|
109
|
+
return el.matches(":focus-visible");
|
|
110
|
+
} catch {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function triggerFrom(target) {
|
|
115
|
+
return target instanceof Element ? target.closest("[data-icen-tooltip]") : null;
|
|
116
|
+
}
|
|
117
|
+
function cancelPending() {
|
|
118
|
+
if (!pending) return;
|
|
119
|
+
window.clearTimeout(pending.timer);
|
|
120
|
+
pending.trigger.removeAttribute("data-icen-tooltip-live");
|
|
121
|
+
pending = null;
|
|
122
|
+
}
|
|
123
|
+
function cancelLeaving() {
|
|
124
|
+
if (!leaving) return;
|
|
125
|
+
window.clearTimeout(leaving.timer);
|
|
126
|
+
leaving.node.classList.add("is-open");
|
|
127
|
+
leaving = null;
|
|
128
|
+
}
|
|
129
|
+
function release(trigger) {
|
|
130
|
+
trigger.removeAttribute("data-icen-tooltip-live");
|
|
131
|
+
const tokens = (trigger.getAttribute("aria-describedby") ?? "").split(/\s+/).filter((id) => id && id !== bubble?.id);
|
|
132
|
+
if (tokens.length) trigger.setAttribute("aria-describedby", tokens.join(" "));
|
|
133
|
+
else trigger.removeAttribute("aria-describedby");
|
|
134
|
+
}
|
|
135
|
+
function cancelClose() {
|
|
136
|
+
if (closeTimer !== null) window.clearTimeout(closeTimer);
|
|
137
|
+
closeTimer = null;
|
|
138
|
+
}
|
|
139
|
+
function clearDismissed(trigger) {
|
|
140
|
+
if (!dismissed.delete(trigger)) return;
|
|
141
|
+
if (current !== trigger && pending?.trigger !== trigger) trigger.removeAttribute("data-icen-tooltip-live");
|
|
142
|
+
}
|
|
143
|
+
function scheduleClose() {
|
|
144
|
+
cancelClose();
|
|
145
|
+
closeTimer = window.setTimeout(() => {
|
|
146
|
+
closeTimer = null;
|
|
147
|
+
if (current && document.activeElement !== current) hide(current);
|
|
148
|
+
}, POINTER_LEAVE_DELAY_MS);
|
|
149
|
+
}
|
|
150
|
+
function unwatch() {
|
|
151
|
+
window.removeEventListener("scroll", onViewportScroll, true);
|
|
152
|
+
window.removeEventListener("resize", onViewportResize);
|
|
153
|
+
observer?.disconnect();
|
|
154
|
+
observer = null;
|
|
155
|
+
}
|
|
156
|
+
function removeBubble(node) {
|
|
157
|
+
node.remove();
|
|
158
|
+
if (bubble === node) bubble = null;
|
|
159
|
+
}
|
|
160
|
+
function hide(trigger, opts) {
|
|
161
|
+
cancelClose();
|
|
162
|
+
const waiting = pending?.trigger;
|
|
163
|
+
cancelPending();
|
|
164
|
+
if (waiting && opts?.suppressFallback) {
|
|
165
|
+
dismissed.add(waiting);
|
|
166
|
+
waiting.setAttribute("data-icen-tooltip-live", "");
|
|
167
|
+
}
|
|
168
|
+
const active = current;
|
|
169
|
+
if (!active || trigger && active !== trigger) return;
|
|
170
|
+
current = null;
|
|
171
|
+
release(active);
|
|
172
|
+
if (opts?.suppressFallback) {
|
|
173
|
+
dismissed.add(active);
|
|
174
|
+
active.setAttribute("data-icen-tooltip-live", "");
|
|
175
|
+
}
|
|
176
|
+
unwatch();
|
|
177
|
+
if (!bubble) return;
|
|
178
|
+
const node = bubble;
|
|
179
|
+
if (opts?.instant) {
|
|
180
|
+
cancelLeaving();
|
|
181
|
+
removeBubble(node);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
node.classList.remove("is-open");
|
|
185
|
+
leaving = {
|
|
186
|
+
node,
|
|
187
|
+
timer: window.setTimeout(() => {
|
|
188
|
+
if (leaving?.node === node) leaving = null;
|
|
189
|
+
removeBubble(node);
|
|
190
|
+
}, LEAVE_TIMEOUT_MS)
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function layout() {
|
|
194
|
+
if (!current || !bubble) return;
|
|
195
|
+
const anchor = current.getBoundingClientRect();
|
|
196
|
+
const width = bubble.offsetWidth;
|
|
197
|
+
const height = bubble.offsetHeight;
|
|
198
|
+
const out = computePopoverLayout(anchor, { width: window.innerWidth, height: window.innerHeight }, {
|
|
199
|
+
side: sideOf(current),
|
|
200
|
+
align: alignOf(current),
|
|
201
|
+
offset: OFFSET_PX,
|
|
202
|
+
margin: VIEWPORT_MARGIN_PX,
|
|
203
|
+
minWidth: width,
|
|
204
|
+
maxWidth: width,
|
|
205
|
+
minHeight: height,
|
|
206
|
+
maxHeight: height,
|
|
207
|
+
contentHeightHint: height
|
|
208
|
+
});
|
|
209
|
+
bubble.style.left = `${out.left}px`;
|
|
210
|
+
bubble.style.top = `${out.top}px`;
|
|
211
|
+
bubble.dataset.icenSide = out.side;
|
|
212
|
+
bubble.style.visibility = "";
|
|
213
|
+
const node = bubble, trigger = current;
|
|
214
|
+
requestAnimationFrame(() => {
|
|
215
|
+
if (bubble === node && current === trigger) node.classList.add("is-open");
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
function watch() {
|
|
219
|
+
unwatch();
|
|
220
|
+
window.addEventListener("scroll", onViewportScroll, { capture: true, passive: true });
|
|
221
|
+
window.addEventListener("resize", onViewportResize, { passive: true });
|
|
222
|
+
observer = new MutationObserver(() => {
|
|
223
|
+
if (!current) return;
|
|
224
|
+
const text = textOf(current);
|
|
225
|
+
if (!current.isConnected || !text) {
|
|
226
|
+
hide(null, { instant: true });
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (bubble) {
|
|
230
|
+
if (bubble.textContent !== text) bubble.textContent = text;
|
|
231
|
+
bubble.classList.toggle("is-multiline", current.getAttribute("data-icen-tooltip-multiline") === "true");
|
|
232
|
+
}
|
|
233
|
+
layout();
|
|
234
|
+
});
|
|
235
|
+
observer.observe(document.documentElement, {
|
|
236
|
+
subtree: true,
|
|
237
|
+
childList: true,
|
|
238
|
+
attributes: true,
|
|
239
|
+
attributeFilter: WATCHED_ATTRS
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
function onViewportScroll() {
|
|
243
|
+
hide(null, { instant: true, suppressFallback: true });
|
|
244
|
+
}
|
|
245
|
+
function onViewportResize() {
|
|
246
|
+
hide(null, { instant: true, suppressFallback: true });
|
|
247
|
+
}
|
|
248
|
+
function show(trigger) {
|
|
249
|
+
if (!trigger.isConnected) return;
|
|
250
|
+
const text = textOf(trigger);
|
|
251
|
+
if (!text) return;
|
|
252
|
+
cancelPending();
|
|
253
|
+
cancelClose();
|
|
254
|
+
cancelLeaving();
|
|
255
|
+
if (current && current !== trigger) release(current);
|
|
256
|
+
current = trigger;
|
|
257
|
+
trigger.setAttribute("data-icen-tooltip-live", "");
|
|
258
|
+
if (!bubble) {
|
|
259
|
+
bubble = document.createElement("div");
|
|
260
|
+
bubble.className = "icen-tooltip";
|
|
261
|
+
bubble.setAttribute("role", "tooltip");
|
|
262
|
+
bubble.id = `icen-tooltip-${++sequence}`;
|
|
263
|
+
bubble.addEventListener("pointerenter", cancelClose);
|
|
264
|
+
bubble.addEventListener("pointerleave", (event) => {
|
|
265
|
+
if (current && event.relatedTarget instanceof Node && current.contains(event.relatedTarget)) return;
|
|
266
|
+
scheduleClose();
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
bubble.textContent = text;
|
|
270
|
+
bubble.classList.toggle("is-multiline", trigger.getAttribute("data-icen-tooltip-multiline") === "true");
|
|
271
|
+
if (!bubble.isConnected) {
|
|
272
|
+
bubble.style.visibility = "hidden";
|
|
273
|
+
document.body.appendChild(bubble);
|
|
274
|
+
}
|
|
275
|
+
const tokens = new Set((trigger.getAttribute("aria-describedby") ?? "").split(/\s+/).filter(Boolean));
|
|
276
|
+
tokens.add(bubble.id);
|
|
277
|
+
trigger.setAttribute("aria-describedby", [...tokens].join(" "));
|
|
278
|
+
layout();
|
|
279
|
+
watch();
|
|
280
|
+
}
|
|
281
|
+
function markPending(trigger) {
|
|
282
|
+
for (const stale of dismissed) if (!stale.isConnected) clearDismissed(stale);
|
|
283
|
+
cancelClose();
|
|
284
|
+
cancelPending();
|
|
285
|
+
if (current === trigger) return;
|
|
286
|
+
trigger.setAttribute("data-icen-tooltip-live", "");
|
|
287
|
+
pending = {
|
|
288
|
+
trigger,
|
|
289
|
+
timer: window.setTimeout(() => {
|
|
290
|
+
pending = null;
|
|
291
|
+
show(trigger);
|
|
292
|
+
}, HOVER_DELAY_MS)
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
function owns(trigger, scope) {
|
|
296
|
+
if (!trigger) return false;
|
|
297
|
+
return scope instanceof Node ? scope.contains(trigger) || scope === trigger : false;
|
|
298
|
+
}
|
|
299
|
+
function initTooltip(root) {
|
|
300
|
+
if (typeof document === "undefined") return () => {
|
|
301
|
+
};
|
|
302
|
+
const scope = root ?? document;
|
|
303
|
+
const existing = scopes.get(scope);
|
|
304
|
+
if (existing) return existing;
|
|
305
|
+
const onPointerOver = (event) => {
|
|
306
|
+
if (handled.has(event)) return;
|
|
307
|
+
handled.add(event);
|
|
308
|
+
const pointer = event;
|
|
309
|
+
if (pointer.pointerType === "touch") return;
|
|
310
|
+
const trigger = triggerFrom(pointer.target);
|
|
311
|
+
if (!trigger) return;
|
|
312
|
+
if (pointer.relatedTarget instanceof Node && trigger.contains(pointer.relatedTarget)) return;
|
|
313
|
+
if (dismissed.has(trigger)) return;
|
|
314
|
+
if (current === trigger) {
|
|
315
|
+
cancelClose();
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (pending?.trigger === trigger) return;
|
|
319
|
+
triggerOwners.set(trigger, scope);
|
|
320
|
+
markPending(trigger);
|
|
321
|
+
};
|
|
322
|
+
const onPointerOut = (event) => {
|
|
323
|
+
const pointer = event;
|
|
324
|
+
const trigger = triggerFrom(pointer.target);
|
|
325
|
+
if (!trigger) return;
|
|
326
|
+
if (pointer.relatedTarget instanceof Node && trigger.contains(pointer.relatedTarget)) return;
|
|
327
|
+
if (document.activeElement !== trigger) clearDismissed(trigger);
|
|
328
|
+
if (pending?.trigger === trigger) cancelPending();
|
|
329
|
+
if (current === trigger && !(pointer.relatedTarget instanceof Node && bubble?.contains(pointer.relatedTarget))) scheduleClose();
|
|
330
|
+
};
|
|
331
|
+
const onFocusIn = (event) => {
|
|
332
|
+
if (handled.has(event)) return;
|
|
333
|
+
handled.add(event);
|
|
334
|
+
const trigger = triggerFrom(event.target);
|
|
335
|
+
if (!trigger || !focusVisible(trigger) || dismissed.has(trigger)) return;
|
|
336
|
+
triggerOwners.set(trigger, scope);
|
|
337
|
+
show(trigger);
|
|
338
|
+
};
|
|
339
|
+
const onFocusOut = (event) => {
|
|
340
|
+
const trigger = triggerFrom(event.target);
|
|
341
|
+
if (!trigger) return;
|
|
342
|
+
if (!trigger.matches(":hover")) clearDismissed(trigger);
|
|
343
|
+
if (pending?.trigger === trigger) cancelPending();
|
|
344
|
+
if (current === trigger && !trigger.matches(":hover")) hide(trigger);
|
|
345
|
+
};
|
|
346
|
+
const onPointerDown = () => {
|
|
347
|
+
hide(null, { suppressFallback: true });
|
|
348
|
+
};
|
|
349
|
+
const onKeyDown = (event) => {
|
|
350
|
+
if (event.key !== "Escape") return;
|
|
351
|
+
hide(null, { suppressFallback: true });
|
|
352
|
+
};
|
|
353
|
+
scope.addEventListener("pointerover", onPointerOver);
|
|
354
|
+
scope.addEventListener("pointerout", onPointerOut);
|
|
355
|
+
scope.addEventListener("focusin", onFocusIn);
|
|
356
|
+
scope.addEventListener("focusout", onFocusOut);
|
|
357
|
+
scope.addEventListener("pointerdown", onPointerDown, true);
|
|
358
|
+
scope.addEventListener("keydown", onKeyDown, true);
|
|
359
|
+
const dispose = () => {
|
|
360
|
+
scope.removeEventListener("pointerover", onPointerOver);
|
|
361
|
+
scope.removeEventListener("pointerout", onPointerOut);
|
|
362
|
+
scope.removeEventListener("focusin", onFocusIn);
|
|
363
|
+
scope.removeEventListener("focusout", onFocusOut);
|
|
364
|
+
scope.removeEventListener("pointerdown", onPointerDown, true);
|
|
365
|
+
scope.removeEventListener("keydown", onKeyDown, true);
|
|
366
|
+
if (owns(pending?.trigger ?? null, scope)) cancelPending();
|
|
367
|
+
if (owns(current, scope)) hide(null, { instant: true });
|
|
368
|
+
for (const trigger of dismissed) {
|
|
369
|
+
if (scope === document || triggerOwners.get(trigger) === scope || owns(trigger, scope)) clearDismissed(trigger);
|
|
370
|
+
}
|
|
371
|
+
scopes.delete(scope);
|
|
372
|
+
};
|
|
373
|
+
scopes.set(scope, dispose);
|
|
374
|
+
return dispose;
|
|
375
|
+
}
|
|
376
|
+
export {
|
|
377
|
+
initTooltip
|
|
378
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
interface TreeNode {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
children?: TreeNode[];
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
interface TreeSelectOptions {
|
|
8
|
+
/** Accessible field name; defaults to placeholder. */
|
|
9
|
+
label?: string;
|
|
10
|
+
size?: 'sm' | 'md' | 'lg';
|
|
11
|
+
nodes: TreeNode[];
|
|
12
|
+
/** 初值(多选数组;单选取首个有效值) */
|
|
13
|
+
value?: string[];
|
|
14
|
+
multiple?: boolean;
|
|
15
|
+
/** 面板内搜索:过滤命中节点自动展开祖先,命中词 <mark> 高亮 */
|
|
16
|
+
searchable?: boolean;
|
|
17
|
+
/** 多选的三态勾选(父 = 子集并集,混合态);隐含多值语义 */
|
|
18
|
+
checkable?: boolean;
|
|
19
|
+
/** 单选时仅叶子可定值(默认任意级可选) */
|
|
20
|
+
leafOnly?: boolean;
|
|
21
|
+
/** 默认「请选择」 */
|
|
22
|
+
placeholder?: string;
|
|
23
|
+
clearable?: boolean;
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
/** 搜索无命中文案,默认「无匹配」 */
|
|
26
|
+
emptyLabel?: string;
|
|
27
|
+
/** 搜索框占位,默认「搜索」 */
|
|
28
|
+
searchPlaceholder?: string;
|
|
29
|
+
onChange?: (value: string[], labels: string[]) => void;
|
|
30
|
+
}
|
|
31
|
+
interface TreeSelectHandle {
|
|
32
|
+
getValue(): string[];
|
|
33
|
+
getLabels(): string[];
|
|
34
|
+
setValue(v: string[]): void;
|
|
35
|
+
clear(): void;
|
|
36
|
+
open(): void;
|
|
37
|
+
close(): void;
|
|
38
|
+
expandAll(): void;
|
|
39
|
+
collapseAll(): void;
|
|
40
|
+
destroy(): void;
|
|
41
|
+
}
|
|
42
|
+
interface FlatTreeEntry {
|
|
43
|
+
node: TreeNode;
|
|
44
|
+
depth: number;
|
|
45
|
+
parents: TreeNode[];
|
|
46
|
+
}
|
|
47
|
+
/** 深度优先拍平(文档序):节点 + 深度 + 父链(键盘导航 / chips 序 / 值序共用)。 */
|
|
48
|
+
declare function flattenTreeNodes(nodes: TreeNode[]): FlatTreeEntry[];
|
|
49
|
+
interface TreeFilterResult {
|
|
50
|
+
/** label 命中 query 的节点值集合(大小写不敏感) */
|
|
51
|
+
matched: Set<string>;
|
|
52
|
+
/** 需保留可见的节点值集合 = 命中 ∪ 祖先 ∪ 命中节点的子孙(上下文可见,不高亮) */
|
|
53
|
+
keep: Set<string>;
|
|
54
|
+
/** 命中节点祖先(搜索态自动展开的目标) */
|
|
55
|
+
expand: Set<string>;
|
|
56
|
+
}
|
|
57
|
+
/** 搜索过滤:命中节点 + 其全部祖先 + 其子孙保留可见(子孙是命中上下文,不高亮);
|
|
58
|
+
* 祖先进 expand(搜索态自动展开的目标)。空 query 返回空集。 */
|
|
59
|
+
declare function filterTreeNodes(nodes: TreeNode[], query: string): TreeFilterResult;
|
|
60
|
+
type TreeCheckState = 'checked' | 'unchecked' | 'mixed';
|
|
61
|
+
/**
|
|
62
|
+
* 三态求值:checked 集合(全部选中节点值)下某节点的勾选态。
|
|
63
|
+
* 自身 + 全部子孙都在集合 → checked;全不在 → unchecked;部分在 → mixed
|
|
64
|
+
* (父不在集合但全部子孙在也呈 mixed——诚实呈现部分选择,由 toggle 逻辑保证一致)。
|
|
65
|
+
*/
|
|
66
|
+
declare function treeNodeCheckState(node: TreeNode, checked: ReadonlySet<string>): TreeCheckState;
|
|
67
|
+
declare function createTreeSelect(el: HTMLElement, opts: TreeSelectOptions): TreeSelectHandle;
|
|
68
|
+
|
|
69
|
+
export { type FlatTreeEntry, type TreeCheckState, type TreeFilterResult, type TreeNode, type TreeSelectHandle, type TreeSelectOptions, createTreeSelect, filterTreeNodes, flattenTreeNodes, treeNodeCheckState };
|