@icen.ai/ui 0.8.0 → 0.9.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 +63 -14
- package/README.md +17 -9
- package/dist/base.css +4 -3
- package/dist/behaviors/accordion.d.mts +5 -2
- package/dist/behaviors/accordion.mjs +36 -11
- package/dist/behaviors/ai-branch.d.mts +19 -0
- package/dist/behaviors/ai-branch.mjs +217 -0
- package/dist/behaviors/ai-chat.d.mts +11 -7
- package/dist/behaviors/ai-chat.mjs +134 -51
- package/dist/behaviors/ai-composer.d.mts +28 -3
- package/dist/behaviors/ai-composer.mjs +399 -229
- package/dist/behaviors/ai-core.d.mts +9 -4
- package/dist/behaviors/ai-core.mjs +21 -8
- package/dist/behaviors/ai-diff.d.mts +2 -2
- package/dist/behaviors/ai-diff.mjs +172 -41
- package/dist/behaviors/ai-feedback.d.mts +26 -0
- package/dist/behaviors/ai-feedback.mjs +303 -0
- package/dist/behaviors/ai-panel.d.mts +26 -9
- package/dist/behaviors/ai-panel.mjs +211 -110
- package/dist/behaviors/ai-provider.mjs +54 -48
- package/dist/behaviors/ai-threads.d.mts +38 -0
- package/dist/behaviors/ai-threads.mjs +319 -0
- package/dist/behaviors/ai-tool.d.mts +12 -4
- package/dist/behaviors/ai-tool.mjs +316 -263
- package/dist/behaviors/ai-tools.d.mts +9 -6
- package/dist/behaviors/ai-tools.mjs +264 -155
- package/dist/behaviors/back-top.mjs +16 -1
- package/dist/behaviors/carousel.d.mts +5 -2
- package/dist/behaviors/carousel.mjs +53 -26
- package/dist/behaviors/charts.d.mts +170 -22
- package/dist/behaviors/charts.mjs +1016 -150
- package/dist/behaviors/command-palette.d.mts +40 -6
- package/dist/behaviors/command-palette.mjs +143 -15
- package/dist/behaviors/context-menu.d.mts +21 -4
- package/dist/behaviors/context-menu.mjs +122 -38
- package/dist/behaviors/controls.d.mts +59 -0
- package/dist/behaviors/controls.mjs +601 -0
- package/dist/behaviors/copy.d.mts +5 -2
- package/dist/behaviors/copy.mjs +37 -12
- package/dist/behaviors/datatable.d.mts +12 -0
- package/dist/behaviors/datatable.mjs +226 -43
- package/dist/behaviors/date-picker.d.mts +2 -2
- package/dist/behaviors/date-picker.mjs +171 -32
- package/dist/behaviors/dropdown.d.mts +6 -2
- package/dist/behaviors/dropdown.mjs +164 -25
- package/dist/behaviors/events.d.mts +523 -0
- package/dist/behaviors/events.mjs +208 -0
- package/dist/behaviors/input.mjs +25 -12
- package/dist/behaviors/kb-access.d.mts +34 -0
- package/dist/behaviors/kb-access.mjs +569 -0
- package/dist/behaviors/kb-acl.d.mts +50 -0
- package/dist/behaviors/kb-acl.mjs +405 -0
- package/dist/behaviors/kb-answer.d.mts +38 -0
- package/dist/behaviors/kb-answer.mjs +2055 -0
- package/dist/behaviors/kb-audit.d.mts +23 -0
- package/dist/behaviors/kb-audit.mjs +375 -0
- package/dist/behaviors/kb-canvas.d.mts +44 -0
- package/dist/behaviors/kb-canvas.mjs +589 -0
- package/dist/behaviors/kb-chain.d.mts +15 -0
- package/dist/behaviors/kb-chain.mjs +270 -0
- package/dist/behaviors/kb-checkpoint.d.mts +16 -0
- package/dist/behaviors/kb-checkpoint.mjs +342 -0
- package/dist/behaviors/kb-chunks.d.mts +31 -0
- package/dist/behaviors/kb-chunks.mjs +475 -0
- package/dist/behaviors/kb-citation.d.mts +38 -0
- package/dist/behaviors/kb-citation.mjs +677 -0
- package/dist/behaviors/kb-clarify.d.mts +20 -0
- package/dist/behaviors/kb-clarify.mjs +248 -0
- package/dist/behaviors/kb-conflict.d.mts +22 -0
- package/dist/behaviors/kb-conflict.mjs +284 -0
- package/dist/behaviors/kb-connector.d.mts +17 -0
- package/dist/behaviors/kb-connector.mjs +375 -0
- package/dist/behaviors/kb-core.d.mts +529 -0
- package/dist/behaviors/kb-core.mjs +890 -0
- package/dist/behaviors/kb-eval.d.mts +22 -0
- package/dist/behaviors/kb-eval.mjs +408 -0
- package/dist/behaviors/kb-explain.d.mts +11 -0
- package/dist/behaviors/kb-explain.mjs +243 -0
- package/dist/behaviors/kb-filter.d.mts +31 -0
- package/dist/behaviors/kb-filter.mjs +587 -0
- package/dist/behaviors/kb-gap.d.mts +27 -0
- package/dist/behaviors/kb-gap.mjs +273 -0
- package/dist/behaviors/kb-hittest.d.mts +34 -0
- package/dist/behaviors/kb-hittest.mjs +409 -0
- package/dist/behaviors/kb-metadata.d.mts +29 -0
- package/dist/behaviors/kb-metadata.mjs +326 -0
- package/dist/behaviors/kb-passage.d.mts +41 -0
- package/dist/behaviors/kb-passage.mjs +529 -0
- package/dist/behaviors/kb-pipeline.d.mts +18 -0
- package/dist/behaviors/kb-pipeline.mjs +381 -0
- package/dist/behaviors/kb-qa.d.mts +26 -0
- package/dist/behaviors/kb-qa.mjs +332 -0
- package/dist/behaviors/kb-rerank.d.mts +20 -0
- package/dist/behaviors/kb-rerank.mjs +315 -0
- package/dist/behaviors/kb-retrieval.d.mts +49 -0
- package/dist/behaviors/kb-retrieval.mjs +746 -0
- package/dist/behaviors/kb-review.d.mts +44 -0
- package/dist/behaviors/kb-review.mjs +451 -0
- package/dist/behaviors/kb-sandbox.d.mts +33 -0
- package/dist/behaviors/kb-sandbox.mjs +325 -0
- package/dist/behaviors/kb-segment.d.mts +32 -0
- package/dist/behaviors/kb-segment.mjs +499 -0
- package/dist/behaviors/kb-sources.d.mts +27 -0
- package/dist/behaviors/kb-sources.mjs +373 -0
- package/dist/behaviors/kb-sql.d.mts +43 -0
- package/dist/behaviors/kb-sql.mjs +948 -0
- package/dist/behaviors/kb-trace.d.mts +16 -0
- package/dist/behaviors/kb-trace.mjs +414 -0
- package/dist/behaviors/kb-visibility.d.mts +44 -0
- package/dist/behaviors/kb-visibility.mjs +403 -0
- package/dist/behaviors/kb-who-can.d.mts +32 -0
- package/dist/behaviors/kb-who-can.mjs +379 -0
- package/dist/behaviors/modal.d.mts +24 -5
- package/dist/behaviors/modal.mjs +76 -10
- package/dist/behaviors/nav.d.mts +1 -1
- package/dist/behaviors/nav.mjs +33 -7
- package/dist/behaviors/notification.d.mts +20 -4
- package/dist/behaviors/notification.mjs +53 -73
- package/dist/behaviors/popover.d.mts +39 -3
- package/dist/behaviors/popover.mjs +90 -4
- package/dist/behaviors/select.d.mts +4 -2
- package/dist/behaviors/select.mjs +161 -24
- package/dist/behaviors/sidebar.d.mts +5 -2
- package/dist/behaviors/sidebar.mjs +43 -13
- package/dist/behaviors/slider.d.mts +5 -2
- package/dist/behaviors/slider.mjs +54 -43
- package/dist/behaviors/split-pane.d.mts +6 -2
- package/dist/behaviors/split-pane.mjs +62 -21
- package/dist/behaviors/tabs.d.mts +3 -2
- package/dist/behaviors/tabs.mjs +65 -14
- package/dist/behaviors/tag-input.d.mts +5 -2
- package/dist/behaviors/tag-input.mjs +61 -21
- package/dist/behaviors/theme.d.mts +13 -2
- package/dist/behaviors/theme.mjs +48 -4
- package/dist/behaviors/toast.d.mts +30 -8
- package/dist/behaviors/toast.mjs +74 -7
- package/dist/behaviors/tree.d.mts +5 -2
- package/dist/behaviors/tree.mjs +38 -15
- package/dist/behaviors/upload.mjs +21 -23
- package/dist/cli.mjs +1 -1
- package/dist/components/ai-branch.mjs +3 -0
- package/dist/components/ai-chat.css +295 -0
- package/dist/components/ai-feedback.mjs +3 -0
- package/dist/components/ai-panel.css +218 -1
- package/dist/components/ai-threads.mjs +3 -0
- package/dist/components/ai-tool-call.mjs +1 -1
- package/dist/components/badge.css +1 -1
- package/dist/components/breadcrumb.css +0 -32
- package/dist/components/chart-calendar.mjs +1 -2
- package/dist/components/chart-graph.css +57 -0
- package/dist/components/chart-graph.mjs +4 -0
- package/dist/components/chart-map.mjs +3 -0
- package/dist/components/chart-pie.css +0 -6
- package/dist/components/chart-scatter.mjs +1 -1
- package/dist/components/charts.css +33 -1
- package/dist/components/charts.mjs +1 -0
- package/dist/components/content.css +3 -16
- package/dist/components/datatable.css +1 -1
- package/dist/components/date-picker.css +1 -1
- package/dist/components/kb-access.mjs +4 -0
- package/dist/components/kb-acl.mjs +4 -0
- package/dist/components/kb-agent.css +481 -0
- package/dist/components/kb-answer.mjs +4 -0
- package/dist/components/kb-audit.mjs +4 -0
- package/dist/components/kb-canvas.mjs +4 -0
- package/dist/components/kb-chain.mjs +4 -0
- package/dist/components/kb-checkpoint.mjs +4 -0
- package/dist/components/kb-chunks.mjs +4 -0
- package/dist/components/kb-citation.mjs +4 -0
- package/dist/components/kb-clarify.mjs +4 -0
- package/dist/components/kb-conflict.mjs +4 -0
- package/dist/components/kb-connector.mjs +4 -0
- package/dist/components/kb-data.css +649 -0
- package/dist/components/kb-eval.mjs +4 -0
- package/dist/components/kb-explain.mjs +4 -0
- package/dist/components/kb-filter.mjs +4 -0
- package/dist/components/kb-gap.mjs +4 -0
- package/dist/components/kb-ground.css +566 -0
- package/dist/components/kb-hittest.mjs +4 -0
- package/dist/components/kb-ingest.css +898 -0
- package/dist/components/kb-metadata.mjs +4 -0
- package/dist/components/kb-ops.css +548 -0
- package/dist/components/kb-passage.mjs +4 -0
- package/dist/components/kb-perm.css +444 -0
- package/dist/components/kb-pipeline.mjs +4 -0
- package/dist/components/kb-qa.mjs +4 -0
- package/dist/components/kb-rerank.mjs +4 -0
- package/dist/components/kb-retrieval.mjs +4 -0
- package/dist/components/kb-review.mjs +4 -0
- package/dist/components/kb-sandbox.mjs +4 -0
- package/dist/components/kb-search.css +940 -0
- package/dist/components/kb-segment.mjs +4 -0
- package/dist/components/kb-sources.mjs +4 -0
- package/dist/components/kb-sql.mjs +4 -0
- package/dist/components/kb-trace.mjs +4 -0
- package/dist/components/kb-visibility.mjs +4 -0
- package/dist/components/kb-who-can.mjs +4 -0
- package/dist/components/kb.css +252 -0
- package/dist/components/kb.mjs +9 -0
- package/dist/components/notification.css +2 -3
- package/dist/components/pagination.css +3 -1
- package/dist/components/pagination.mjs +1 -0
- package/dist/components/rating.mjs +1 -0
- package/dist/components/segmented.css +9 -1
- package/dist/components/segmented.mjs +1 -1
- package/dist/components/select.css +67 -4
- package/dist/components/steps.css +13 -3
- package/dist/components/steps.mjs +1 -0
- package/dist/components/switch.css +3 -1
- package/dist/components/switch.mjs +1 -0
- package/dist/components/table.mjs +1 -0
- package/dist/components/tabs.css +23 -0
- package/dist/components/tag-input.css +1 -6
- package/dist/components/upload.css +0 -15
- package/dist/index.d.mts +48 -11
- package/dist/index.mjs +13205 -3615
- package/dist/registry.json +366 -15
- package/dist/retro-effects.css +3 -16
- package/dist/tokens/retro-effects.css +3 -16
- package/dist/tokens/style-profiles.css +3 -2
- package/dist/tokens/typography.css +2 -0
- package/dist/tokens.css +5 -2
- package/dist/ui.css +5713 -267
- package/docs/research/2026-10-03-ai-kb-components.md +392 -0
- package/docs/research/2026-10-03-kb-permissions.md +992 -0
- package/docs/spec/ai-native.md +7 -4
- package/docs/spec/kb-family.md +443 -0
- package/package.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ContextMenuItem } from './context-menu.js';
|
|
2
|
+
import './popover.js';
|
|
2
3
|
|
|
3
4
|
interface TableColumn<Row = Record<string, unknown>> {
|
|
4
5
|
key: string;
|
|
@@ -53,6 +54,14 @@ interface TableOptions<Row = Record<string, unknown>> {
|
|
|
53
54
|
onSelectionChange?: (rows: Row[]) => void;
|
|
54
55
|
onRowClick?: (row: Row) => void;
|
|
55
56
|
onSortChange?: (key: string | null, dir: 'asc' | 'desc') => void;
|
|
57
|
+
/** 搜索谓词(默认跨列 String includes;自定义拼音/高亮/索引匹配) */
|
|
58
|
+
searchFn?: (query: string, row: Row) => boolean;
|
|
59
|
+
/** 服务端/受控分页:开启后翻页只派事件与回调,不再本地切片(配 totalCount + setData/setPage 喂数) */
|
|
60
|
+
remote?: boolean;
|
|
61
|
+
/** remote 模式总条数(缺省 data.length) */
|
|
62
|
+
totalCount?: number;
|
|
63
|
+
onPageChange?: (page: number, pageSize: number) => void;
|
|
64
|
+
onPageSizeChange?: (pageSize: number) => void;
|
|
56
65
|
}
|
|
57
66
|
interface TableHandle<Row = Record<string, unknown>> {
|
|
58
67
|
setData(data: Row[]): void;
|
|
@@ -68,6 +77,9 @@ interface TableHandle<Row = Record<string, unknown>> {
|
|
|
68
77
|
exportCSV(filename?: string): void;
|
|
69
78
|
/** 显示/隐藏列 */
|
|
70
79
|
showColumn(key: string): void;
|
|
80
|
+
/** 外部驱动页码(remote 模式与服务端同步) */
|
|
81
|
+
setPage(page: number): void;
|
|
82
|
+
setPageSize(size: number): void;
|
|
71
83
|
hideColumn(key: string): void;
|
|
72
84
|
/** 数据被外部原地修改后重渲 */
|
|
73
85
|
refresh(): void;
|
|
@@ -1,10 +1,151 @@
|
|
|
1
|
+
// src/behaviors/popover.ts
|
|
2
|
+
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"
|
|
8
|
+
};
|
|
9
|
+
function readPanelSizing(el) {
|
|
10
|
+
const out = {};
|
|
11
|
+
if (!el) return out;
|
|
12
|
+
for (const key of Object.keys(PANEL_SIZING_ATTRS)) {
|
|
13
|
+
const raw = el.getAttribute(PANEL_SIZING_ATTRS[key]);
|
|
14
|
+
if (raw == null) continue;
|
|
15
|
+
const n = Number(raw);
|
|
16
|
+
if (Number.isFinite(n) && n > 0) out[key] = n;
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
function resolvePanelSizing(el, overrides) {
|
|
21
|
+
const base = readPanelSizing(el);
|
|
22
|
+
if (!overrides) return base;
|
|
23
|
+
for (const key of Object.keys(overrides)) {
|
|
24
|
+
const v = overrides[key];
|
|
25
|
+
if (typeof v === "number" && Number.isFinite(v) && v > 0) base[key] = v;
|
|
26
|
+
}
|
|
27
|
+
return base;
|
|
28
|
+
}
|
|
29
|
+
function applyPanelSizing(panel2, sizing) {
|
|
30
|
+
if (sizing == null) {
|
|
31
|
+
panel2.style.width = "";
|
|
32
|
+
panel2.style.minWidth = "";
|
|
33
|
+
panel2.style.maxWidth = "";
|
|
34
|
+
panel2.style.minHeight = "";
|
|
35
|
+
panel2.style.maxHeight = "";
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const vw = typeof document === "undefined" ? Number.POSITIVE_INFINITY : document.documentElement.clientWidth;
|
|
39
|
+
const fitW = (n) => Math.min(n, Math.max(80, vw - 24));
|
|
40
|
+
if (sizing.width != null) {
|
|
41
|
+
const w = fitW(sizing.width);
|
|
42
|
+
panel2.style.width = `${w}px`;
|
|
43
|
+
panel2.style.maxWidth = `${w}px`;
|
|
44
|
+
}
|
|
45
|
+
if (sizing.minWidth != null) panel2.style.minWidth = `${fitW(sizing.minWidth)}px`;
|
|
46
|
+
if (sizing.maxWidth != null && sizing.width == null) panel2.style.maxWidth = `${fitW(sizing.maxWidth)}px`;
|
|
47
|
+
if (sizing.minHeight != null) panel2.style.minHeight = `${sizing.minHeight}px`;
|
|
48
|
+
if (sizing.maxHeight != null) panel2.style.maxHeight = `${sizing.maxHeight}px`;
|
|
49
|
+
}
|
|
50
|
+
var DEFAULT_MIN_WIDTH = 220;
|
|
51
|
+
var DEFAULT_MAX_WIDTH = 420;
|
|
52
|
+
var DEFAULT_MIN_HEIGHT = 120;
|
|
53
|
+
var DEFAULT_MAX_HEIGHT = 420;
|
|
54
|
+
function clamp(value, min, max) {
|
|
55
|
+
if (max < min) return min;
|
|
56
|
+
return Math.min(max, Math.max(min, value));
|
|
57
|
+
}
|
|
58
|
+
function chooseSide(preferredSide, availableTop, availableBottom, desiredHeight) {
|
|
59
|
+
const preferredSpace = preferredSide === "top" ? availableTop : availableBottom;
|
|
60
|
+
const oppositeSpace = preferredSide === "top" ? availableBottom : availableTop;
|
|
61
|
+
if (preferredSpace >= desiredHeight) return preferredSide;
|
|
62
|
+
if (oppositeSpace >= desiredHeight) return preferredSide === "top" ? "bottom" : "top";
|
|
63
|
+
if (preferredSpace >= oppositeSpace) return preferredSide;
|
|
64
|
+
return preferredSide === "top" ? "bottom" : "top";
|
|
65
|
+
}
|
|
66
|
+
function horizontalAnchor(anchor, width, align) {
|
|
67
|
+
if (align === "center") return anchor.left + anchor.width / 2 - width / 2;
|
|
68
|
+
if (align === "end") return anchor.right - width;
|
|
69
|
+
return anchor.left;
|
|
70
|
+
}
|
|
71
|
+
function computePopoverLayout(anchor, viewport, opts = {}) {
|
|
72
|
+
const margin = opts.margin ?? 12;
|
|
73
|
+
const offset = opts.offset ?? 8;
|
|
74
|
+
const preferredSide = opts.side ?? "top";
|
|
75
|
+
const align = opts.align ?? "start";
|
|
76
|
+
if (preferredSide === "left" || preferredSide === "right") {
|
|
77
|
+
const availableLeft = Math.max(0, anchor.left - margin - offset);
|
|
78
|
+
const availableRight = Math.max(0, viewport.width - anchor.right - margin - offset);
|
|
79
|
+
const side2 = (preferredSide === "left" ? availableLeft : availableRight) >= DEFAULT_MIN_WIDTH ? preferredSide : preferredSide === "left" ? "right" : "left";
|
|
80
|
+
const minWidth2 = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
81
|
+
const maxWidth2 = Math.min(
|
|
82
|
+
Math.max(opts.maxWidth ?? DEFAULT_MAX_WIDTH, minWidth2),
|
|
83
|
+
side2 === "left" ? availableLeft : availableRight
|
|
84
|
+
);
|
|
85
|
+
const width2 = minWidth2;
|
|
86
|
+
const left2 = clamp(
|
|
87
|
+
side2 === "left" ? anchor.left - offset - width2 : anchor.right + offset,
|
|
88
|
+
margin,
|
|
89
|
+
viewport.width - margin - width2
|
|
90
|
+
);
|
|
91
|
+
const availableHeight2 = Math.max(80, viewport.height - margin * 2);
|
|
92
|
+
const maxHeight2 = Math.min(opts.maxHeight ?? DEFAULT_MAX_HEIGHT, availableHeight2);
|
|
93
|
+
const desired = clamp(opts.contentHeightHint ?? Math.min(anchor.height, maxHeight2), 40, maxHeight2);
|
|
94
|
+
const anchorTop = align === "center" ? anchor.top + anchor.height / 2 - desired / 2 : align === "end" ? anchor.bottom - desired : anchor.top;
|
|
95
|
+
const top2 = clamp(anchorTop, margin, viewport.height - margin - desired);
|
|
96
|
+
return { side: side2, left: left2, top: top2, width: width2, maxHeight: maxHeight2 };
|
|
97
|
+
}
|
|
98
|
+
const availableWidth = Math.max(80, viewport.width - margin * 2);
|
|
99
|
+
const requestedMinWidth = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
100
|
+
const requestedMaxWidth = Math.max(requestedMinWidth, opts.maxWidth ?? DEFAULT_MAX_WIDTH);
|
|
101
|
+
const maxWidth = Math.min(requestedMaxWidth, availableWidth);
|
|
102
|
+
const minWidth = Math.min(requestedMinWidth, maxWidth);
|
|
103
|
+
const availableTop = Math.max(0, anchor.top - margin - offset);
|
|
104
|
+
const availableBottom = Math.max(0, viewport.height - anchor.bottom - margin - offset);
|
|
105
|
+
const availableHeight = Math.max(80, viewport.height - margin * 2);
|
|
106
|
+
const minHeight = Math.min(Math.max(80, opts.minHeight ?? DEFAULT_MIN_HEIGHT), availableHeight);
|
|
107
|
+
const requestedMaxHeight = Math.max(minHeight, opts.maxHeight ?? DEFAULT_MAX_HEIGHT);
|
|
108
|
+
const fallbackHeight = Math.min(260, requestedMaxHeight);
|
|
109
|
+
const contentHeight = Math.max(opts.contentHeightHint ?? 0, fallbackHeight);
|
|
110
|
+
const desiredHeight = clamp(contentHeight, minHeight, Math.min(requestedMaxHeight, availableHeight));
|
|
111
|
+
const side = chooseSide(preferredSide, availableTop, availableBottom, desiredHeight);
|
|
112
|
+
const sideSpace = side === "top" ? availableTop : availableBottom;
|
|
113
|
+
const maxHeight = Math.min(
|
|
114
|
+
requestedMaxHeight,
|
|
115
|
+
Math.max(80, sideSpace || Math.max(availableTop, availableBottom)),
|
|
116
|
+
availableHeight
|
|
117
|
+
);
|
|
118
|
+
const positionedHeight = clamp(desiredHeight, 40, maxHeight);
|
|
119
|
+
const width = align === "stretch" ? clamp(anchor.width, minWidth, maxWidth) : maxWidth;
|
|
120
|
+
const left = clamp(horizontalAnchor(anchor, width, align), margin, viewport.width - margin - width);
|
|
121
|
+
const desiredTop = side === "top" ? anchor.top - offset - positionedHeight : anchor.bottom + offset;
|
|
122
|
+
const top = clamp(desiredTop, margin, viewport.height - margin - positionedHeight);
|
|
123
|
+
return { side, left, top, width, maxHeight };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/behaviors/events.ts
|
|
127
|
+
function emitIcen(source, type, detail) {
|
|
128
|
+
return source.dispatchEvent(
|
|
129
|
+
new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
1
133
|
// src/behaviors/context-menu.ts
|
|
2
134
|
var FLIP_MARGIN = 10;
|
|
3
135
|
var CLAMP_MARGIN = 8;
|
|
136
|
+
function emitMenuSelect(source, item) {
|
|
137
|
+
emitIcen(item, "icen:menu-select", {
|
|
138
|
+
source,
|
|
139
|
+
item,
|
|
140
|
+
value: item.dataset.value ?? "",
|
|
141
|
+
/* 优先专用 label 子元素(避开快捷键/图标文本混入),无则退整项文本 */
|
|
142
|
+
label: (item.querySelector(".menu-item-label, .command-item-label")?.textContent ?? item.textContent ?? "").trim().slice(0, 80)
|
|
143
|
+
});
|
|
144
|
+
}
|
|
4
145
|
var panel = null;
|
|
5
146
|
var focusableItems = [];
|
|
6
147
|
var focusedIndex = -1;
|
|
7
|
-
function buildItem(item) {
|
|
148
|
+
function buildItem(item, menuPanel) {
|
|
8
149
|
if ("type" in item && item.type === "separator") {
|
|
9
150
|
const sep = document.createElement("div");
|
|
10
151
|
sep.className = "menu-separator";
|
|
@@ -54,19 +195,21 @@ function buildItem(item) {
|
|
|
54
195
|
btn.appendChild(shortcut);
|
|
55
196
|
}
|
|
56
197
|
btn.addEventListener("click", () => {
|
|
198
|
+
emitMenuSelect(menuPanel, btn);
|
|
57
199
|
entry.onClick?.();
|
|
58
|
-
|
|
200
|
+
closeContextMenu();
|
|
59
201
|
});
|
|
60
202
|
return btn;
|
|
61
203
|
}
|
|
62
|
-
function openContextMenu(x, y, items) {
|
|
204
|
+
function openContextMenu(x, y, items, opts) {
|
|
63
205
|
if (typeof document === "undefined") return;
|
|
64
|
-
|
|
206
|
+
closeContextMenu();
|
|
65
207
|
panel = document.createElement("div");
|
|
66
208
|
panel.className = "menu menu--context";
|
|
67
209
|
panel.setAttribute("role", "menu");
|
|
68
210
|
panel.style.visibility = "hidden";
|
|
69
|
-
|
|
211
|
+
applyPanelSizing(panel, opts?.sizing ?? {});
|
|
212
|
+
for (const item of items) panel.appendChild(buildItem(item, panel));
|
|
70
213
|
document.body.appendChild(panel);
|
|
71
214
|
const rect = panel.getBoundingClientRect();
|
|
72
215
|
const vw = window.innerWidth;
|
|
@@ -77,6 +220,7 @@ function openContextMenu(x, y, items) {
|
|
|
77
220
|
if (y + rect.height > vh - FLIP_MARGIN) ny = y - rect.height;
|
|
78
221
|
if (nx < CLAMP_MARGIN) nx = CLAMP_MARGIN;
|
|
79
222
|
if (ny < CLAMP_MARGIN) ny = CLAMP_MARGIN;
|
|
223
|
+
panel.dataset.side = ny < y ? "top" : "bottom";
|
|
80
224
|
panel.style.left = `${nx}px`;
|
|
81
225
|
panel.style.top = `${ny}px`;
|
|
82
226
|
panel.style.visibility = "";
|
|
@@ -91,7 +235,7 @@ function openContextMenu(x, y, items) {
|
|
|
91
235
|
window.addEventListener("resize", onCloseSignal);
|
|
92
236
|
window.addEventListener("keydown", onKeyDown);
|
|
93
237
|
}
|
|
94
|
-
function
|
|
238
|
+
function closeContextMenu() {
|
|
95
239
|
if (!panel) return;
|
|
96
240
|
panel.remove();
|
|
97
241
|
panel = null;
|
|
@@ -110,20 +254,20 @@ function setFocused(next) {
|
|
|
110
254
|
function onOutsidePointerDown(e) {
|
|
111
255
|
if (!panel) return;
|
|
112
256
|
const target = e.target;
|
|
113
|
-
if (!(target instanceof Node) || !panel.contains(target))
|
|
257
|
+
if (!(target instanceof Node) || !panel.contains(target)) closeContextMenu();
|
|
114
258
|
}
|
|
115
259
|
function onScrollSignal(e) {
|
|
116
260
|
if (panel && e.target instanceof Node && panel.contains(e.target)) return;
|
|
117
|
-
|
|
261
|
+
closeContextMenu();
|
|
118
262
|
}
|
|
119
263
|
function onCloseSignal() {
|
|
120
|
-
|
|
264
|
+
closeContextMenu();
|
|
121
265
|
}
|
|
122
266
|
function onKeyDown(e) {
|
|
123
267
|
if (!panel) return;
|
|
124
268
|
if (e.key === "Escape") {
|
|
125
269
|
e.preventDefault();
|
|
126
|
-
|
|
270
|
+
closeContextMenu();
|
|
127
271
|
return;
|
|
128
272
|
}
|
|
129
273
|
if (focusableItems.length === 0) return;
|
|
@@ -179,7 +323,9 @@ function noopHandle() {
|
|
|
179
323
|
getData: () => [],
|
|
180
324
|
exportCSV: noop,
|
|
181
325
|
showColumn: noop,
|
|
182
|
-
hideColumn: noop
|
|
326
|
+
hideColumn: noop,
|
|
327
|
+
setPage: noop,
|
|
328
|
+
setPageSize: noop
|
|
183
329
|
};
|
|
184
330
|
}
|
|
185
331
|
function createTable(el, opts) {
|
|
@@ -227,7 +373,7 @@ function createTable(el, opts) {
|
|
|
227
373
|
if (search) {
|
|
228
374
|
const q = search.toLowerCase();
|
|
229
375
|
rows = rows.filter(
|
|
230
|
-
(row) => cols().some((c) => String(row[c.key] ?? "").toLowerCase().includes(q))
|
|
376
|
+
(row) => opts.searchFn ? opts.searchFn(search, row) : cols().some((c) => String(row[c.key] ?? "").toLowerCase().includes(q))
|
|
231
377
|
);
|
|
232
378
|
}
|
|
233
379
|
if (filters.size > 0) {
|
|
@@ -368,6 +514,20 @@ function createTable(el, opts) {
|
|
|
368
514
|
return input;
|
|
369
515
|
}
|
|
370
516
|
let openPanel = null;
|
|
517
|
+
function positionDropdownPanel(panel2, btn) {
|
|
518
|
+
panel2.hidden = false;
|
|
519
|
+
const sizing = resolvePanelSizing(el);
|
|
520
|
+
applyPanelSizing(panel2, sizing);
|
|
521
|
+
const rect = btn.getBoundingClientRect();
|
|
522
|
+
const pr = panel2.getBoundingClientRect();
|
|
523
|
+
const layout = computePopoverLayout(
|
|
524
|
+
{ left: rect.left, top: rect.top, right: rect.right, bottom: rect.bottom, width: rect.width, height: rect.height },
|
|
525
|
+
{ width: window.innerWidth, height: window.innerHeight },
|
|
526
|
+
{ side: "bottom", align: "end", offset: 4, margin: 8, minWidth: Math.max(pr.width, 180), maxWidth: Math.max(pr.width, 180), contentHeightHint: pr.height, maxHeight: sizing.maxHeight }
|
|
527
|
+
);
|
|
528
|
+
panel2.style.left = `${layout.left}px`;
|
|
529
|
+
panel2.style.top = `${layout.top}px`;
|
|
530
|
+
}
|
|
371
531
|
function onPanelScroll(ev) {
|
|
372
532
|
if (openPanel && ev.target instanceof Node && openPanel.el.contains(ev.target)) return;
|
|
373
533
|
closeFilterPanel();
|
|
@@ -385,15 +545,7 @@ function createTable(el, opts) {
|
|
|
385
545
|
document.body.appendChild(panel2);
|
|
386
546
|
openPanel = { key: col.key, el: panel2 };
|
|
387
547
|
btn.classList.add("is-open");
|
|
388
|
-
|
|
389
|
-
const pr = panel2.getBoundingClientRect();
|
|
390
|
-
let x = rect.right - pr.width;
|
|
391
|
-
let y = rect.bottom + 4;
|
|
392
|
-
if (x < 8) x = 8;
|
|
393
|
-
if (y + pr.height > window.innerHeight - 8) y = rect.top - pr.height - 4;
|
|
394
|
-
panel2.style.left = `${x}px`;
|
|
395
|
-
panel2.style.top = `${y}px`;
|
|
396
|
-
panel2.hidden = false;
|
|
548
|
+
positionDropdownPanel(panel2, btn);
|
|
397
549
|
window.addEventListener("scroll", onPanelScroll, true);
|
|
398
550
|
window.addEventListener("resize", onPanelScroll);
|
|
399
551
|
}
|
|
@@ -451,6 +603,7 @@ function createTable(el, opts) {
|
|
|
451
603
|
} else if (sortDir === "asc") sortDir = "desc";
|
|
452
604
|
else sortKey = null;
|
|
453
605
|
invalidate();
|
|
606
|
+
if (sortKey) emitIcen(root, "icen:table-sort-change", { key: sortKey, dir: sortDir });
|
|
454
607
|
opts.onSortChange?.(sortKey, sortDir);
|
|
455
608
|
renderAll();
|
|
456
609
|
};
|
|
@@ -569,6 +722,7 @@ function createTable(el, opts) {
|
|
|
569
722
|
box.addEventListener("change", () => {
|
|
570
723
|
if (box.checked) colHidden.delete(col.key);
|
|
571
724
|
else colHidden.set(col.key, true);
|
|
725
|
+
emitIcen(root, "icen:table-columns-change", { hidden: Array.from(colHidden.keys()) });
|
|
572
726
|
invalidate();
|
|
573
727
|
renderBody();
|
|
574
728
|
renderHeadKeepPanel();
|
|
@@ -581,20 +735,13 @@ function createTable(el, opts) {
|
|
|
581
735
|
panel2.appendChild(list);
|
|
582
736
|
document.body.appendChild(panel2);
|
|
583
737
|
openPanel = { key: "__cols", el: panel2 };
|
|
584
|
-
|
|
585
|
-
const pr = panel2.getBoundingClientRect();
|
|
586
|
-
let x = rect.right - pr.width;
|
|
587
|
-
let y = rect.bottom + 4;
|
|
588
|
-
if (x < 8) x = 8;
|
|
589
|
-
if (y + pr.height > window.innerHeight - 8) y = rect.top - pr.height - 4;
|
|
590
|
-
panel2.style.left = `${x}px`;
|
|
591
|
-
panel2.style.top = `${y}px`;
|
|
592
|
-
panel2.hidden = false;
|
|
738
|
+
positionDropdownPanel(panel2, btn);
|
|
593
739
|
window.addEventListener("scroll", onPanelScroll, true);
|
|
594
740
|
window.addEventListener("resize", onPanelScroll);
|
|
595
741
|
}
|
|
596
742
|
function exportCSV(filename) {
|
|
597
743
|
const rows = pipeline();
|
|
744
|
+
emitIcen(root, "icen:table-export", { count: rows.length });
|
|
598
745
|
const visible = cols();
|
|
599
746
|
const header = visible.map((c) => csvEscape(c.title)).join(",");
|
|
600
747
|
const lines = rows.map(
|
|
@@ -631,6 +778,7 @@ function createTable(el, opts) {
|
|
|
631
778
|
else cur.delete(v);
|
|
632
779
|
if (cur.size === values.length) filters.delete(col.key);
|
|
633
780
|
else filters.set(col.key, cur);
|
|
781
|
+
emitIcen(root, "icen:table-filter-change", { key: col.key, values: Array.from(filters.get(col.key) ?? []) });
|
|
634
782
|
invalidate();
|
|
635
783
|
page = 1;
|
|
636
784
|
renderBody();
|
|
@@ -661,7 +809,7 @@ function createTable(el, opts) {
|
|
|
661
809
|
panel2.append(list, footRow);
|
|
662
810
|
return panel2;
|
|
663
811
|
}
|
|
664
|
-
function makeRow(row, rowIndex, key) {
|
|
812
|
+
function makeRow(row, rowIndex, key, offsets) {
|
|
665
813
|
const tr = document.createElement("div");
|
|
666
814
|
tr.className = "dt-row";
|
|
667
815
|
tr.setAttribute("role", "row");
|
|
@@ -675,17 +823,19 @@ function createTable(el, opts) {
|
|
|
675
823
|
if (selectable) {
|
|
676
824
|
const cell = document.createElement("div");
|
|
677
825
|
cell.className = "dt-cell dt-cell--check";
|
|
678
|
-
if (
|
|
826
|
+
if (offsets.size > 0) {
|
|
679
827
|
cell.classList.add("dt-cell--frozen");
|
|
680
828
|
cell.style.left = "0px";
|
|
681
829
|
}
|
|
682
830
|
const box = makeCheck(selected.has(key), "\u9009\u62E9\u8BE5\u884C");
|
|
831
|
+
let shiftClicked = false;
|
|
683
832
|
box.addEventListener("click", (ev) => {
|
|
684
|
-
|
|
833
|
+
shiftClicked = ev.shiftKey;
|
|
685
834
|
ev.stopPropagation();
|
|
686
835
|
});
|
|
687
836
|
box.addEventListener("change", () => {
|
|
688
|
-
const shift =
|
|
837
|
+
const shift = shiftClicked;
|
|
838
|
+
shiftClicked = false;
|
|
689
839
|
if (shift && lastClickedIndex >= 0 && lastClickedIndex !== rowIndex) {
|
|
690
840
|
const rows = pipeline();
|
|
691
841
|
const [lo, hi] = lastClickedIndex < rowIndex ? [lastClickedIndex, rowIndex] : [rowIndex, lastClickedIndex];
|
|
@@ -704,7 +854,7 @@ function createTable(el, opts) {
|
|
|
704
854
|
lastClickedIndex = rowIndex;
|
|
705
855
|
emitSelection();
|
|
706
856
|
syncHeadState();
|
|
707
|
-
renderFoot(pipeline().length);
|
|
857
|
+
renderFoot(opts.remote ? opts.totalCount ?? data.length : pipeline().length);
|
|
708
858
|
});
|
|
709
859
|
cell.appendChild(box);
|
|
710
860
|
tr.appendChild(cell);
|
|
@@ -722,12 +872,12 @@ function createTable(el, opts) {
|
|
|
722
872
|
ev.stopPropagation();
|
|
723
873
|
if (expanded.has(key)) expanded.delete(key);
|
|
724
874
|
else expanded.add(key);
|
|
875
|
+
emitIcen(root, "icen:table-row-toggle", { row, index: rowIndex, expanded: expanded.has(key) });
|
|
725
876
|
renderBody();
|
|
726
877
|
});
|
|
727
878
|
cell.appendChild(btn);
|
|
728
879
|
tr.appendChild(cell);
|
|
729
880
|
}
|
|
730
|
-
const offsets = frozenOffsets();
|
|
731
881
|
for (const col of cols()) {
|
|
732
882
|
const cell = document.createElement("div");
|
|
733
883
|
cell.className = "dt-cell";
|
|
@@ -744,11 +894,16 @@ function createTable(el, opts) {
|
|
|
744
894
|
tr.classList.add("is-clickable");
|
|
745
895
|
tr.addEventListener("click", () => opts.onRowClick(row));
|
|
746
896
|
}
|
|
897
|
+
tr.addEventListener("click", () => {
|
|
898
|
+
emitIcen(root, "icen:table-row-click", { row, index: rowIndex });
|
|
899
|
+
});
|
|
747
900
|
if (opts.contextMenu) {
|
|
748
901
|
tr.addEventListener("contextmenu", (ev) => {
|
|
749
902
|
ev.preventDefault();
|
|
750
903
|
ev.stopPropagation();
|
|
751
|
-
openContextMenu(ev.clientX, ev.clientY, opts.contextMenu(row)
|
|
904
|
+
openContextMenu(ev.clientX, ev.clientY, opts.contextMenu(row), {
|
|
905
|
+
sizing: readPanelSizing(el)
|
|
906
|
+
});
|
|
752
907
|
});
|
|
753
908
|
}
|
|
754
909
|
return tr;
|
|
@@ -763,7 +918,7 @@ function createTable(el, opts) {
|
|
|
763
918
|
}
|
|
764
919
|
function visibleRange(total) {
|
|
765
920
|
if (!virtual) {
|
|
766
|
-
if (opts.pagination === false) return [0, total];
|
|
921
|
+
if (opts.remote || opts.pagination === false) return [0, total];
|
|
767
922
|
const start2 = (page - 1) * pageSize;
|
|
768
923
|
return [start2, Math.min(total, start2 + pageSize)];
|
|
769
924
|
}
|
|
@@ -775,7 +930,7 @@ function createTable(el, opts) {
|
|
|
775
930
|
}
|
|
776
931
|
function renderBody() {
|
|
777
932
|
const rows = pipeline();
|
|
778
|
-
const total = rows.length;
|
|
933
|
+
const total = opts.remote ? opts.totalCount ?? data.length : rows.length;
|
|
779
934
|
const keepTop = virtual ? scroll.scrollTop : 0;
|
|
780
935
|
body.textContent = "";
|
|
781
936
|
if (opts.loading) {
|
|
@@ -802,6 +957,7 @@ function createTable(el, opts) {
|
|
|
802
957
|
}
|
|
803
958
|
const [start, end] = visibleRange(total);
|
|
804
959
|
const frag = document.createDocumentFragment();
|
|
960
|
+
const offsets = frozenOffsets();
|
|
805
961
|
if (virtual && start > 0) {
|
|
806
962
|
const top = document.createElement("div");
|
|
807
963
|
top.style.height = `${start * rowH}px`;
|
|
@@ -810,7 +966,7 @@ function createTable(el, opts) {
|
|
|
810
966
|
for (let i = start; i < end; i++) {
|
|
811
967
|
const row = rows[i];
|
|
812
968
|
const key = keyOfRow(row);
|
|
813
|
-
frag.appendChild(makeRow(row, i, key));
|
|
969
|
+
frag.appendChild(makeRow(row, i, key, offsets));
|
|
814
970
|
if (expandable && expanded.has(key)) frag.appendChild(makeExpandRow(row));
|
|
815
971
|
}
|
|
816
972
|
if (virtual && end < total) {
|
|
@@ -863,6 +1019,10 @@ function createTable(el, opts) {
|
|
|
863
1019
|
sizeSel.addEventListener("change", () => {
|
|
864
1020
|
pageSize = Number(sizeSel.value);
|
|
865
1021
|
page = 1;
|
|
1022
|
+
emitIcen(root, "icen:table-page-size-change", { pageSize });
|
|
1023
|
+
emitIcen(root, "icen:table-page-change", { page, pageSize });
|
|
1024
|
+
opts.onPageSizeChange?.(pageSize);
|
|
1025
|
+
opts.onPageChange?.(page, pageSize);
|
|
866
1026
|
renderAll();
|
|
867
1027
|
});
|
|
868
1028
|
pager.appendChild(sizeSel);
|
|
@@ -876,6 +1036,8 @@ function createTable(el, opts) {
|
|
|
876
1036
|
if (opts2.aria) b.setAttribute("aria-label", opts2.aria);
|
|
877
1037
|
b.addEventListener("click", () => {
|
|
878
1038
|
page = target;
|
|
1039
|
+
emitIcen(root, "icen:table-page-change", { page, pageSize });
|
|
1040
|
+
opts.onPageChange?.(page, pageSize);
|
|
879
1041
|
renderAll();
|
|
880
1042
|
});
|
|
881
1043
|
return b;
|
|
@@ -904,6 +1066,7 @@ function createTable(el, opts) {
|
|
|
904
1066
|
window.clearTimeout(searchTimer);
|
|
905
1067
|
searchTimer = window.setTimeout(() => {
|
|
906
1068
|
search = searchInput?.value.trim() ?? "";
|
|
1069
|
+
emitIcen(root, "icen:table-search-change", { query: search });
|
|
907
1070
|
invalidate();
|
|
908
1071
|
page = 1;
|
|
909
1072
|
renderAll();
|
|
@@ -927,9 +1090,9 @@ function createTable(el, opts) {
|
|
|
927
1090
|
}
|
|
928
1091
|
document.addEventListener("keydown", onDocKeydown);
|
|
929
1092
|
function emitSelection() {
|
|
930
|
-
if (!opts.onSelectionChange) return;
|
|
931
1093
|
const rows = data.filter((r) => selected.has(keyOfRow(r)));
|
|
932
|
-
|
|
1094
|
+
emitIcen(root, "icen:table-selection-change", { selected: rows });
|
|
1095
|
+
opts.onSelectionChange?.(rows);
|
|
933
1096
|
}
|
|
934
1097
|
function renderAll() {
|
|
935
1098
|
if (destroyed) return;
|
|
@@ -938,7 +1101,26 @@ function createTable(el, opts) {
|
|
|
938
1101
|
}
|
|
939
1102
|
rekey();
|
|
940
1103
|
renderAll();
|
|
1104
|
+
function setPage(n) {
|
|
1105
|
+
if (!Number.isFinite(n) || n < 1) return;
|
|
1106
|
+
page = Math.round(n);
|
|
1107
|
+
emitIcen(root, "icen:table-page-change", { page, pageSize });
|
|
1108
|
+
opts.onPageChange?.(page, pageSize);
|
|
1109
|
+
renderAll();
|
|
1110
|
+
}
|
|
1111
|
+
function setPageSize(n) {
|
|
1112
|
+
if (!Number.isFinite(n) || n <= 0) return;
|
|
1113
|
+
pageSize = Math.round(n);
|
|
1114
|
+
page = 1;
|
|
1115
|
+
emitIcen(root, "icen:table-page-size-change", { pageSize });
|
|
1116
|
+
emitIcen(root, "icen:table-page-change", { page, pageSize });
|
|
1117
|
+
opts.onPageSizeChange?.(pageSize);
|
|
1118
|
+
opts.onPageChange?.(page, pageSize);
|
|
1119
|
+
renderAll();
|
|
1120
|
+
}
|
|
941
1121
|
return {
|
|
1122
|
+
setPage,
|
|
1123
|
+
setPageSize,
|
|
942
1124
|
setData(next) {
|
|
943
1125
|
data = next;
|
|
944
1126
|
rekey();
|
|
@@ -981,7 +1163,8 @@ function createTable(el, opts) {
|
|
|
981
1163
|
exportCSV,
|
|
982
1164
|
showColumn(key) {
|
|
983
1165
|
colHidden.delete(key);
|
|
984
|
-
opts.columns.find((c) => c.key === key)
|
|
1166
|
+
const col = opts.columns.find((c) => c.key === key);
|
|
1167
|
+
if (col) col.hidden = false;
|
|
985
1168
|
invalidate();
|
|
986
1169
|
renderAll();
|
|
987
1170
|
},
|