@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,3 +1,126 @@
|
|
|
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 applyPanelSizing(panel, sizing) {
|
|
21
|
+
if (sizing == null) {
|
|
22
|
+
panel.style.width = "";
|
|
23
|
+
panel.style.minWidth = "";
|
|
24
|
+
panel.style.maxWidth = "";
|
|
25
|
+
panel.style.minHeight = "";
|
|
26
|
+
panel.style.maxHeight = "";
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const vw = typeof document === "undefined" ? Number.POSITIVE_INFINITY : document.documentElement.clientWidth;
|
|
30
|
+
const fitW = (n) => Math.min(n, Math.max(80, vw - 24));
|
|
31
|
+
if (sizing.width != null) {
|
|
32
|
+
const w = fitW(sizing.width);
|
|
33
|
+
panel.style.width = `${w}px`;
|
|
34
|
+
panel.style.maxWidth = `${w}px`;
|
|
35
|
+
}
|
|
36
|
+
if (sizing.minWidth != null) panel.style.minWidth = `${fitW(sizing.minWidth)}px`;
|
|
37
|
+
if (sizing.maxWidth != null && sizing.width == null) panel.style.maxWidth = `${fitW(sizing.maxWidth)}px`;
|
|
38
|
+
if (sizing.minHeight != null) panel.style.minHeight = `${sizing.minHeight}px`;
|
|
39
|
+
if (sizing.maxHeight != null) panel.style.maxHeight = `${sizing.maxHeight}px`;
|
|
40
|
+
}
|
|
41
|
+
var DEFAULT_MIN_WIDTH = 220;
|
|
42
|
+
var DEFAULT_MAX_WIDTH = 420;
|
|
43
|
+
var DEFAULT_MIN_HEIGHT = 120;
|
|
44
|
+
var DEFAULT_MAX_HEIGHT = 420;
|
|
45
|
+
function clamp(value, min, max) {
|
|
46
|
+
if (max < min) return min;
|
|
47
|
+
return Math.min(max, Math.max(min, value));
|
|
48
|
+
}
|
|
49
|
+
function chooseSide(preferredSide, availableTop, availableBottom, desiredHeight) {
|
|
50
|
+
const preferredSpace = preferredSide === "top" ? availableTop : availableBottom;
|
|
51
|
+
const oppositeSpace = preferredSide === "top" ? availableBottom : availableTop;
|
|
52
|
+
if (preferredSpace >= desiredHeight) return preferredSide;
|
|
53
|
+
if (oppositeSpace >= desiredHeight) return preferredSide === "top" ? "bottom" : "top";
|
|
54
|
+
if (preferredSpace >= oppositeSpace) return preferredSide;
|
|
55
|
+
return preferredSide === "top" ? "bottom" : "top";
|
|
56
|
+
}
|
|
57
|
+
function horizontalAnchor(anchor, width, align) {
|
|
58
|
+
if (align === "center") return anchor.left + anchor.width / 2 - width / 2;
|
|
59
|
+
if (align === "end") return anchor.right - width;
|
|
60
|
+
return anchor.left;
|
|
61
|
+
}
|
|
62
|
+
function computePopoverLayout(anchor, viewport, opts = {}) {
|
|
63
|
+
const margin = opts.margin ?? 12;
|
|
64
|
+
const offset = opts.offset ?? 8;
|
|
65
|
+
const preferredSide = opts.side ?? "top";
|
|
66
|
+
const align = opts.align ?? "start";
|
|
67
|
+
if (preferredSide === "left" || preferredSide === "right") {
|
|
68
|
+
const availableLeft = Math.max(0, anchor.left - margin - offset);
|
|
69
|
+
const availableRight = Math.max(0, viewport.width - anchor.right - margin - offset);
|
|
70
|
+
const side2 = (preferredSide === "left" ? availableLeft : availableRight) >= DEFAULT_MIN_WIDTH ? preferredSide : preferredSide === "left" ? "right" : "left";
|
|
71
|
+
const minWidth2 = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
72
|
+
const maxWidth2 = Math.min(
|
|
73
|
+
Math.max(opts.maxWidth ?? DEFAULT_MAX_WIDTH, minWidth2),
|
|
74
|
+
side2 === "left" ? availableLeft : availableRight
|
|
75
|
+
);
|
|
76
|
+
const width2 = minWidth2;
|
|
77
|
+
const left2 = clamp(
|
|
78
|
+
side2 === "left" ? anchor.left - offset - width2 : anchor.right + offset,
|
|
79
|
+
margin,
|
|
80
|
+
viewport.width - margin - width2
|
|
81
|
+
);
|
|
82
|
+
const availableHeight2 = Math.max(80, viewport.height - margin * 2);
|
|
83
|
+
const maxHeight2 = Math.min(opts.maxHeight ?? DEFAULT_MAX_HEIGHT, availableHeight2);
|
|
84
|
+
const desired = clamp(opts.contentHeightHint ?? Math.min(anchor.height, maxHeight2), 40, maxHeight2);
|
|
85
|
+
const anchorTop = align === "center" ? anchor.top + anchor.height / 2 - desired / 2 : align === "end" ? anchor.bottom - desired : anchor.top;
|
|
86
|
+
const top2 = clamp(anchorTop, margin, viewport.height - margin - desired);
|
|
87
|
+
return { side: side2, left: left2, top: top2, width: width2, maxHeight: maxHeight2 };
|
|
88
|
+
}
|
|
89
|
+
const availableWidth = Math.max(80, viewport.width - margin * 2);
|
|
90
|
+
const requestedMinWidth = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
91
|
+
const requestedMaxWidth = Math.max(requestedMinWidth, opts.maxWidth ?? DEFAULT_MAX_WIDTH);
|
|
92
|
+
const maxWidth = Math.min(requestedMaxWidth, availableWidth);
|
|
93
|
+
const minWidth = Math.min(requestedMinWidth, maxWidth);
|
|
94
|
+
const availableTop = Math.max(0, anchor.top - margin - offset);
|
|
95
|
+
const availableBottom = Math.max(0, viewport.height - anchor.bottom - margin - offset);
|
|
96
|
+
const availableHeight = Math.max(80, viewport.height - margin * 2);
|
|
97
|
+
const minHeight = Math.min(Math.max(80, opts.minHeight ?? DEFAULT_MIN_HEIGHT), availableHeight);
|
|
98
|
+
const requestedMaxHeight = Math.max(minHeight, opts.maxHeight ?? DEFAULT_MAX_HEIGHT);
|
|
99
|
+
const fallbackHeight = Math.min(260, requestedMaxHeight);
|
|
100
|
+
const contentHeight = Math.max(opts.contentHeightHint ?? 0, fallbackHeight);
|
|
101
|
+
const desiredHeight = clamp(contentHeight, minHeight, Math.min(requestedMaxHeight, availableHeight));
|
|
102
|
+
const side = chooseSide(preferredSide, availableTop, availableBottom, desiredHeight);
|
|
103
|
+
const sideSpace = side === "top" ? availableTop : availableBottom;
|
|
104
|
+
const maxHeight = Math.min(
|
|
105
|
+
requestedMaxHeight,
|
|
106
|
+
Math.max(80, sideSpace || Math.max(availableTop, availableBottom)),
|
|
107
|
+
availableHeight
|
|
108
|
+
);
|
|
109
|
+
const positionedHeight = clamp(desiredHeight, 40, maxHeight);
|
|
110
|
+
const width = align === "stretch" ? clamp(anchor.width, minWidth, maxWidth) : maxWidth;
|
|
111
|
+
const left = clamp(horizontalAnchor(anchor, width, align), margin, viewport.width - margin - width);
|
|
112
|
+
const desiredTop = side === "top" ? anchor.top - offset - positionedHeight : anchor.bottom + offset;
|
|
113
|
+
const top = clamp(desiredTop, margin, viewport.height - margin - positionedHeight);
|
|
114
|
+
return { side, left, top, width, maxHeight };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/behaviors/events.ts
|
|
118
|
+
function emitIcen(source, type, detail) {
|
|
119
|
+
return source.dispatchEvent(
|
|
120
|
+
new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
1
124
|
// src/behaviors/date-picker.ts
|
|
2
125
|
var openPicker = null;
|
|
3
126
|
var panelEl = null;
|
|
@@ -33,16 +156,21 @@ function ensurePanel() {
|
|
|
33
156
|
}
|
|
34
157
|
function positionPanel(panel, trigger) {
|
|
35
158
|
const r = trigger.getBoundingClientRect();
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
159
|
+
const sizing = readPanelSizing(trigger.closest("[data-date-picker]"));
|
|
160
|
+
applyPanelSizing(panel, null);
|
|
161
|
+
if (sizing.width != null || sizing.minWidth != null) {
|
|
162
|
+
applyPanelSizing(panel, { width: sizing.width ?? sizing.minWidth, maxWidth: sizing.maxWidth, maxHeight: sizing.maxHeight });
|
|
163
|
+
} else {
|
|
164
|
+
applyPanelSizing(panel, sizing);
|
|
165
|
+
}
|
|
166
|
+
const pw = sizing.width ?? sizing.minWidth ?? 280;
|
|
167
|
+
const layout = computePopoverLayout(
|
|
168
|
+
r,
|
|
169
|
+
{ width: window.innerWidth, height: window.innerHeight },
|
|
170
|
+
{ side: "bottom", offset: 4, margin: 12, minWidth: pw, maxWidth: pw, maxHeight: sizing.maxHeight }
|
|
171
|
+
);
|
|
172
|
+
panel.style.left = `${layout.left}px`;
|
|
173
|
+
panel.style.top = `${layout.top}px`;
|
|
46
174
|
}
|
|
47
175
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
48
176
|
function buildCalIcon() {
|
|
@@ -150,12 +278,10 @@ function renderCalendar(panel, ctx) {
|
|
|
150
278
|
foot.append(todayBtn, clearBtn);
|
|
151
279
|
panel.append(head, weekdays, grid, foot);
|
|
152
280
|
prevBtn.addEventListener("click", () => {
|
|
153
|
-
ctx
|
|
154
|
-
renderCalendar(panel, { ...ctx, view: ctx.view });
|
|
281
|
+
renderCalendar(panel, { ...ctx, view: new Date(year, month - 1, 15) });
|
|
155
282
|
});
|
|
156
283
|
nextBtn.addEventListener("click", () => {
|
|
157
|
-
ctx
|
|
158
|
-
renderCalendar(panel, { ...ctx, view: ctx.view });
|
|
284
|
+
renderCalendar(panel, { ...ctx, view: new Date(year, month + 1, 15) });
|
|
159
285
|
});
|
|
160
286
|
todayBtn.addEventListener("click", () => {
|
|
161
287
|
onPick(new Date(today.getFullYear(), today.getMonth(), today.getDate()));
|
|
@@ -183,10 +309,20 @@ function closePanel() {
|
|
|
183
309
|
repositionHandler = null;
|
|
184
310
|
}
|
|
185
311
|
}
|
|
312
|
+
var pickersInit = /* @__PURE__ */ new WeakSet();
|
|
186
313
|
function setupPicker(container) {
|
|
314
|
+
if (pickersInit.has(container)) return () => {
|
|
315
|
+
};
|
|
316
|
+
pickersInit.add(container);
|
|
187
317
|
const el = container;
|
|
188
|
-
|
|
189
|
-
|
|
318
|
+
const ac = new AbortController();
|
|
319
|
+
const disposers = [
|
|
320
|
+
() => {
|
|
321
|
+
if (openPicker === el) closePanel();
|
|
322
|
+
ac.abort();
|
|
323
|
+
pickersInit.delete(container);
|
|
324
|
+
}
|
|
325
|
+
];
|
|
190
326
|
const fmt = el.dataset.datePickerFormat ?? "yyyy-mm-dd";
|
|
191
327
|
const minStr = el.dataset.datePickerMin;
|
|
192
328
|
const maxStr = el.dataset.datePickerMax;
|
|
@@ -224,8 +360,9 @@ function setupPicker(container) {
|
|
|
224
360
|
if (hidden) {
|
|
225
361
|
const obs = new MutationObserver(() => paint());
|
|
226
362
|
obs.observe(hidden, { attributes: true, attributeFilter: ["value"] });
|
|
363
|
+
disposers.push(() => obs.disconnect());
|
|
227
364
|
}
|
|
228
|
-
|
|
365
|
+
const onTriggerClick = () => {
|
|
229
366
|
if (openPicker === el) {
|
|
230
367
|
closePanel();
|
|
231
368
|
return;
|
|
@@ -246,17 +383,9 @@ function setupPicker(container) {
|
|
|
246
383
|
max,
|
|
247
384
|
weekStart,
|
|
248
385
|
onPick: (d) => {
|
|
249
|
-
if (d
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
hidden.dispatchEvent(new Event("change", { bubbles: true }));
|
|
253
|
-
}
|
|
254
|
-
} else {
|
|
255
|
-
if (hidden) {
|
|
256
|
-
hidden.value = toDateStr(d);
|
|
257
|
-
hidden.dispatchEvent(new Event("change", { bubbles: true }));
|
|
258
|
-
}
|
|
259
|
-
}
|
|
386
|
+
if (hidden) hidden.value = d ? toDateStr(d) : "";
|
|
387
|
+
emitIcen(el, "icen:date-change", { value: d ? toDateStr(d) : "", date: d });
|
|
388
|
+
if (hidden) hidden.dispatchEvent(new Event("change", { bubbles: true }));
|
|
260
389
|
paint();
|
|
261
390
|
closePanel();
|
|
262
391
|
}
|
|
@@ -280,16 +409,26 @@ function setupPicker(container) {
|
|
|
280
409
|
};
|
|
281
410
|
window.addEventListener("scroll", repositionHandler, { passive: true });
|
|
282
411
|
window.addEventListener("resize", repositionHandler, { passive: true });
|
|
283
|
-
}
|
|
412
|
+
};
|
|
413
|
+
trigger.addEventListener("click", onTriggerClick, { signal: ac.signal });
|
|
414
|
+
return () => {
|
|
415
|
+
for (const d of disposers) d();
|
|
416
|
+
};
|
|
284
417
|
}
|
|
285
418
|
function initDatePicker(root) {
|
|
286
|
-
if (typeof document === "undefined") return
|
|
287
|
-
|
|
419
|
+
if (typeof document === "undefined") return () => {
|
|
420
|
+
};
|
|
421
|
+
if (typeof window === "undefined") return () => {
|
|
422
|
+
};
|
|
288
423
|
const scope = root ?? document;
|
|
289
424
|
const pickers = [];
|
|
290
425
|
if (scope instanceof Element && scope.matches(".date-picker[data-date-picker]")) pickers.push(scope);
|
|
291
426
|
pickers.push(...Array.from(scope.querySelectorAll(".date-picker[data-date-picker]")));
|
|
292
|
-
|
|
427
|
+
const disposers = [];
|
|
428
|
+
for (const p of pickers) disposers.push(setupPicker(p));
|
|
429
|
+
return () => {
|
|
430
|
+
for (const d of disposers) d();
|
|
431
|
+
};
|
|
293
432
|
}
|
|
294
433
|
export {
|
|
295
434
|
initDatePicker
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 绑定全部 [data-dropdown-trigger](document 级委托,重复调用安全)。
|
|
3
|
+
* 返回销毁函数:摘除 document 委托并复位幂等标记(销毁后可重新 init);
|
|
4
|
+
* 销毁时若有面板开着会顺带收起。SSR 下返回 no-op。
|
|
5
|
+
*/
|
|
6
|
+
declare function initDropdown(root?: ParentNode): () => void;
|
|
3
7
|
|
|
4
8
|
export { initDropdown };
|
|
@@ -1,12 +1,142 @@
|
|
|
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 applyPanelSizing(panel, sizing) {
|
|
21
|
+
if (sizing == null) {
|
|
22
|
+
panel.style.width = "";
|
|
23
|
+
panel.style.minWidth = "";
|
|
24
|
+
panel.style.maxWidth = "";
|
|
25
|
+
panel.style.minHeight = "";
|
|
26
|
+
panel.style.maxHeight = "";
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const vw = typeof document === "undefined" ? Number.POSITIVE_INFINITY : document.documentElement.clientWidth;
|
|
30
|
+
const fitW = (n) => Math.min(n, Math.max(80, vw - 24));
|
|
31
|
+
if (sizing.width != null) {
|
|
32
|
+
const w = fitW(sizing.width);
|
|
33
|
+
panel.style.width = `${w}px`;
|
|
34
|
+
panel.style.maxWidth = `${w}px`;
|
|
35
|
+
}
|
|
36
|
+
if (sizing.minWidth != null) panel.style.minWidth = `${fitW(sizing.minWidth)}px`;
|
|
37
|
+
if (sizing.maxWidth != null && sizing.width == null) panel.style.maxWidth = `${fitW(sizing.maxWidth)}px`;
|
|
38
|
+
if (sizing.minHeight != null) panel.style.minHeight = `${sizing.minHeight}px`;
|
|
39
|
+
if (sizing.maxHeight != null) panel.style.maxHeight = `${sizing.maxHeight}px`;
|
|
40
|
+
}
|
|
41
|
+
var DEFAULT_MIN_WIDTH = 220;
|
|
42
|
+
var DEFAULT_MAX_WIDTH = 420;
|
|
43
|
+
var DEFAULT_MIN_HEIGHT = 120;
|
|
44
|
+
var DEFAULT_MAX_HEIGHT = 420;
|
|
45
|
+
function clamp(value, min, max) {
|
|
46
|
+
if (max < min) return min;
|
|
47
|
+
return Math.min(max, Math.max(min, value));
|
|
48
|
+
}
|
|
49
|
+
function chooseSide(preferredSide, availableTop, availableBottom, desiredHeight) {
|
|
50
|
+
const preferredSpace = preferredSide === "top" ? availableTop : availableBottom;
|
|
51
|
+
const oppositeSpace = preferredSide === "top" ? availableBottom : availableTop;
|
|
52
|
+
if (preferredSpace >= desiredHeight) return preferredSide;
|
|
53
|
+
if (oppositeSpace >= desiredHeight) return preferredSide === "top" ? "bottom" : "top";
|
|
54
|
+
if (preferredSpace >= oppositeSpace) return preferredSide;
|
|
55
|
+
return preferredSide === "top" ? "bottom" : "top";
|
|
56
|
+
}
|
|
57
|
+
function horizontalAnchor(anchor, width, align) {
|
|
58
|
+
if (align === "center") return anchor.left + anchor.width / 2 - width / 2;
|
|
59
|
+
if (align === "end") return anchor.right - width;
|
|
60
|
+
return anchor.left;
|
|
61
|
+
}
|
|
62
|
+
function computePopoverLayout(anchor, viewport, opts = {}) {
|
|
63
|
+
const margin = opts.margin ?? 12;
|
|
64
|
+
const offset = opts.offset ?? 8;
|
|
65
|
+
const preferredSide = opts.side ?? "top";
|
|
66
|
+
const align = opts.align ?? "start";
|
|
67
|
+
if (preferredSide === "left" || preferredSide === "right") {
|
|
68
|
+
const availableLeft = Math.max(0, anchor.left - margin - offset);
|
|
69
|
+
const availableRight = Math.max(0, viewport.width - anchor.right - margin - offset);
|
|
70
|
+
const side2 = (preferredSide === "left" ? availableLeft : availableRight) >= DEFAULT_MIN_WIDTH ? preferredSide : preferredSide === "left" ? "right" : "left";
|
|
71
|
+
const minWidth2 = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
72
|
+
const maxWidth2 = Math.min(
|
|
73
|
+
Math.max(opts.maxWidth ?? DEFAULT_MAX_WIDTH, minWidth2),
|
|
74
|
+
side2 === "left" ? availableLeft : availableRight
|
|
75
|
+
);
|
|
76
|
+
const width2 = minWidth2;
|
|
77
|
+
const left2 = clamp(
|
|
78
|
+
side2 === "left" ? anchor.left - offset - width2 : anchor.right + offset,
|
|
79
|
+
margin,
|
|
80
|
+
viewport.width - margin - width2
|
|
81
|
+
);
|
|
82
|
+
const availableHeight2 = Math.max(80, viewport.height - margin * 2);
|
|
83
|
+
const maxHeight2 = Math.min(opts.maxHeight ?? DEFAULT_MAX_HEIGHT, availableHeight2);
|
|
84
|
+
const desired = clamp(opts.contentHeightHint ?? Math.min(anchor.height, maxHeight2), 40, maxHeight2);
|
|
85
|
+
const anchorTop = align === "center" ? anchor.top + anchor.height / 2 - desired / 2 : align === "end" ? anchor.bottom - desired : anchor.top;
|
|
86
|
+
const top2 = clamp(anchorTop, margin, viewport.height - margin - desired);
|
|
87
|
+
return { side: side2, left: left2, top: top2, width: width2, maxHeight: maxHeight2 };
|
|
88
|
+
}
|
|
89
|
+
const availableWidth = Math.max(80, viewport.width - margin * 2);
|
|
90
|
+
const requestedMinWidth = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
91
|
+
const requestedMaxWidth = Math.max(requestedMinWidth, opts.maxWidth ?? DEFAULT_MAX_WIDTH);
|
|
92
|
+
const maxWidth = Math.min(requestedMaxWidth, availableWidth);
|
|
93
|
+
const minWidth = Math.min(requestedMinWidth, maxWidth);
|
|
94
|
+
const availableTop = Math.max(0, anchor.top - margin - offset);
|
|
95
|
+
const availableBottom = Math.max(0, viewport.height - anchor.bottom - margin - offset);
|
|
96
|
+
const availableHeight = Math.max(80, viewport.height - margin * 2);
|
|
97
|
+
const minHeight = Math.min(Math.max(80, opts.minHeight ?? DEFAULT_MIN_HEIGHT), availableHeight);
|
|
98
|
+
const requestedMaxHeight = Math.max(minHeight, opts.maxHeight ?? DEFAULT_MAX_HEIGHT);
|
|
99
|
+
const fallbackHeight = Math.min(260, requestedMaxHeight);
|
|
100
|
+
const contentHeight = Math.max(opts.contentHeightHint ?? 0, fallbackHeight);
|
|
101
|
+
const desiredHeight = clamp(contentHeight, minHeight, Math.min(requestedMaxHeight, availableHeight));
|
|
102
|
+
const side = chooseSide(preferredSide, availableTop, availableBottom, desiredHeight);
|
|
103
|
+
const sideSpace = side === "top" ? availableTop : availableBottom;
|
|
104
|
+
const maxHeight = Math.min(
|
|
105
|
+
requestedMaxHeight,
|
|
106
|
+
Math.max(80, sideSpace || Math.max(availableTop, availableBottom)),
|
|
107
|
+
availableHeight
|
|
108
|
+
);
|
|
109
|
+
const positionedHeight = clamp(desiredHeight, 40, maxHeight);
|
|
110
|
+
const width = align === "stretch" ? clamp(anchor.width, minWidth, maxWidth) : maxWidth;
|
|
111
|
+
const left = clamp(horizontalAnchor(anchor, width, align), margin, viewport.width - margin - width);
|
|
112
|
+
const desiredTop = side === "top" ? anchor.top - offset - positionedHeight : anchor.bottom + offset;
|
|
113
|
+
const top = clamp(desiredTop, margin, viewport.height - margin - positionedHeight);
|
|
114
|
+
return { side, left, top, width, maxHeight };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/behaviors/events.ts
|
|
118
|
+
function emitIcen(source, type, detail) {
|
|
119
|
+
return source.dispatchEvent(
|
|
120
|
+
new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/behaviors/context-menu.ts
|
|
125
|
+
function emitMenuSelect(source, item) {
|
|
126
|
+
emitIcen(item, "icen:menu-select", {
|
|
127
|
+
source,
|
|
128
|
+
item,
|
|
129
|
+
value: item.dataset.value ?? "",
|
|
130
|
+
/* 优先专用 label 子元素(避开快捷键/图标文本混入),无则退整项文本 */
|
|
131
|
+
label: (item.querySelector(".menu-item-label, .command-item-label")?.textContent ?? item.textContent ?? "").trim().slice(0, 80)
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
1
135
|
// src/behaviors/dropdown.ts
|
|
2
136
|
var VIEWPORT_MARGIN = 12;
|
|
3
137
|
var SIDE_OFFSET = 4;
|
|
4
138
|
var active = null;
|
|
5
139
|
var initialized = false;
|
|
6
|
-
function clamp(value, min, max) {
|
|
7
|
-
if (max < min) return min;
|
|
8
|
-
return Math.min(max, Math.max(min, value));
|
|
9
|
-
}
|
|
10
140
|
function menuItems(panel) {
|
|
11
141
|
return Array.from(panel.querySelectorAll(".menu-item:not(.is-disabled):not(:disabled)"));
|
|
12
142
|
}
|
|
@@ -19,21 +149,14 @@ function place() {
|
|
|
19
149
|
if (!active) return;
|
|
20
150
|
const { trigger, panel } = active;
|
|
21
151
|
const rect = trigger.getBoundingClientRect();
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
top = rect.top - SIDE_OFFSET - ph;
|
|
31
|
-
side = "top";
|
|
32
|
-
}
|
|
33
|
-
top = clamp(top, VIEWPORT_MARGIN, vh - VIEWPORT_MARGIN - ph);
|
|
34
|
-
panel.style.left = `${left}px`;
|
|
35
|
-
panel.style.top = `${top}px`;
|
|
36
|
-
panel.dataset.side = side;
|
|
152
|
+
const layout = computePopoverLayout(
|
|
153
|
+
rect,
|
|
154
|
+
{ width: window.innerWidth, height: window.innerHeight },
|
|
155
|
+
{ side: "bottom", offset: SIDE_OFFSET, margin: VIEWPORT_MARGIN }
|
|
156
|
+
);
|
|
157
|
+
panel.style.left = `${layout.left}px`;
|
|
158
|
+
panel.style.top = `${layout.top}px`;
|
|
159
|
+
panel.dataset.side = layout.side;
|
|
37
160
|
}
|
|
38
161
|
function openDropdown(trigger, highlightFirst = false) {
|
|
39
162
|
closeDropdown();
|
|
@@ -44,6 +167,7 @@ function openDropdown(trigger, highlightFirst = false) {
|
|
|
44
167
|
panel.className = "menu";
|
|
45
168
|
panel.setAttribute("role", "menu");
|
|
46
169
|
panel.style.visibility = "hidden";
|
|
170
|
+
applyPanelSizing(panel, readPanelSizing(root));
|
|
47
171
|
while (template.firstChild) panel.appendChild(template.firstChild);
|
|
48
172
|
panel.querySelectorAll(".menu-item").forEach((el) => el.setAttribute("role", "menuitem"));
|
|
49
173
|
document.body.appendChild(panel);
|
|
@@ -100,22 +224,28 @@ function onKeyDown(e) {
|
|
|
100
224
|
if (e.key === "Enter" && active.highlight >= 0) {
|
|
101
225
|
e.preventDefault();
|
|
102
226
|
const item = items[active.highlight];
|
|
227
|
+
emitMenuSelect(active.panel, item);
|
|
103
228
|
closeDropdown(true);
|
|
104
229
|
item.click();
|
|
105
230
|
}
|
|
106
231
|
}
|
|
107
232
|
function initDropdown(root) {
|
|
108
|
-
if (typeof document === "undefined") return
|
|
233
|
+
if (typeof document === "undefined") return () => {
|
|
234
|
+
};
|
|
109
235
|
const scope = root ?? document;
|
|
110
236
|
scope.querySelectorAll("[data-dropdown-trigger]").forEach((el) => el.setAttribute("aria-expanded", "false"));
|
|
111
|
-
if (initialized) return
|
|
237
|
+
if (initialized) return () => {
|
|
238
|
+
};
|
|
112
239
|
initialized = true;
|
|
113
|
-
|
|
240
|
+
const onClick = (e) => {
|
|
114
241
|
const target = e.target instanceof Element ? e.target : null;
|
|
115
242
|
if (!target) return;
|
|
116
243
|
if (active && target.closest(".menu-item") && active.panel.contains(target)) {
|
|
117
244
|
const item = target.closest(".menu-item");
|
|
118
245
|
if (item) {
|
|
246
|
+
if (!item.matches(".is-disabled") && !item.matches(":disabled")) {
|
|
247
|
+
emitMenuSelect(active.panel, item);
|
|
248
|
+
}
|
|
119
249
|
closeDropdown();
|
|
120
250
|
return;
|
|
121
251
|
}
|
|
@@ -124,8 +254,9 @@ function initDropdown(root) {
|
|
|
124
254
|
if (!trigger) return;
|
|
125
255
|
if (active && active.trigger === trigger) closeDropdown();
|
|
126
256
|
else openDropdown(trigger);
|
|
127
|
-
}
|
|
128
|
-
document.addEventListener("
|
|
257
|
+
};
|
|
258
|
+
document.addEventListener("click", onClick);
|
|
259
|
+
const onKeyDown2 = (e) => {
|
|
129
260
|
if (e.key !== "ArrowDown") return;
|
|
130
261
|
if (active) return;
|
|
131
262
|
const target = e.target instanceof Element ? e.target : null;
|
|
@@ -133,7 +264,15 @@ function initDropdown(root) {
|
|
|
133
264
|
if (!trigger) return;
|
|
134
265
|
e.preventDefault();
|
|
135
266
|
openDropdown(trigger, true);
|
|
136
|
-
}
|
|
267
|
+
};
|
|
268
|
+
document.addEventListener("keydown", onKeyDown2);
|
|
269
|
+
const destroy = () => {
|
|
270
|
+
document.removeEventListener("click", onClick);
|
|
271
|
+
document.removeEventListener("keydown", onKeyDown2);
|
|
272
|
+
closeDropdown();
|
|
273
|
+
initialized = false;
|
|
274
|
+
};
|
|
275
|
+
return destroy;
|
|
137
276
|
}
|
|
138
277
|
export {
|
|
139
278
|
initDropdown
|