@icen.ai/ui 0.5.0 → 0.8.1
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 +262 -0
- package/README.md +52 -13
- package/dist/base.css +33 -5
- package/dist/behaviors/accordion.d.mts +7 -0
- package/dist/behaviors/accordion.mjs +37 -11
- package/dist/behaviors/ai-chat.d.mts +59 -0
- package/dist/behaviors/ai-chat.mjs +580 -0
- package/dist/behaviors/ai-composer.d.mts +137 -0
- package/dist/behaviors/ai-composer.mjs +2114 -0
- package/dist/behaviors/ai-core.d.mts +156 -0
- package/dist/behaviors/ai-core.mjs +504 -0
- package/dist/behaviors/ai-diff.d.mts +53 -0
- package/dist/behaviors/ai-diff.mjs +413 -0
- package/dist/behaviors/ai-panel.d.mts +101 -0
- package/dist/behaviors/ai-panel.mjs +1087 -0
- package/dist/behaviors/ai-provider.d.mts +198 -0
- package/dist/behaviors/ai-provider.mjs +1061 -0
- package/dist/behaviors/ai-tool.d.mts +26 -0
- package/dist/behaviors/ai-tool.mjs +2173 -0
- package/dist/behaviors/ai-tools.d.mts +84 -0
- package/dist/behaviors/ai-tools.mjs +1762 -0
- package/dist/behaviors/back-top.d.mts +20 -0
- package/dist/behaviors/back-top.mjs +64 -32
- package/dist/behaviors/carousel.d.mts +7 -0
- package/dist/behaviors/carousel.mjs +72 -27
- package/dist/behaviors/charts.d.mts +296 -0
- package/dist/behaviors/charts.mjs +1364 -440
- package/dist/behaviors/command-palette.d.mts +42 -0
- package/dist/behaviors/command-palette.mjs +167 -27
- package/dist/behaviors/context-menu.d.mts +53 -0
- package/dist/behaviors/context-menu.mjs +127 -39
- package/dist/behaviors/controls.d.mts +59 -0
- package/dist/behaviors/controls.mjs +601 -0
- package/dist/behaviors/copy.d.mts +7 -0
- package/dist/behaviors/copy.mjs +41 -15
- package/dist/behaviors/datatable.d.mts +90 -0
- package/dist/behaviors/datatable.mjs +279 -53
- package/dist/behaviors/date-picker.d.mts +4 -0
- package/dist/behaviors/date-picker.mjs +276 -69
- package/dist/behaviors/dropdown.d.mts +8 -0
- package/dist/behaviors/dropdown.mjs +171 -28
- package/dist/behaviors/events.d.mts +330 -0
- package/dist/behaviors/events.mjs +208 -0
- package/dist/behaviors/input.d.mts +4 -0
- package/dist/behaviors/input.mjs +52 -32
- package/dist/behaviors/modal.d.mts +30 -0
- package/dist/behaviors/modal.mjs +87 -16
- package/dist/behaviors/nav.d.mts +7 -0
- package/dist/behaviors/nav.mjs +57 -15
- package/dist/behaviors/notification.d.mts +188 -0
- package/dist/behaviors/notification.mjs +274 -140
- package/dist/behaviors/popover.d.mts +82 -0
- package/dist/behaviors/popover.mjs +92 -5
- package/dist/behaviors/select.d.mts +6 -0
- package/dist/behaviors/select.mjs +287 -45
- package/dist/behaviors/sidebar.d.mts +7 -0
- package/dist/behaviors/sidebar.mjs +44 -13
- package/dist/behaviors/slider.d.mts +7 -0
- package/dist/behaviors/slider.mjs +55 -43
- package/dist/behaviors/split-pane.d.mts +8 -0
- package/dist/behaviors/split-pane.mjs +70 -29
- package/dist/behaviors/tabs.d.mts +9 -0
- package/dist/behaviors/tabs.mjs +102 -26
- package/dist/behaviors/tag-input.d.mts +7 -0
- package/dist/behaviors/tag-input.mjs +66 -17
- package/dist/behaviors/theme.d.mts +36 -0
- package/dist/behaviors/theme.mjs +51 -5
- package/dist/behaviors/toast.d.mts +48 -0
- package/dist/behaviors/toast.mjs +98 -25
- package/dist/behaviors/tree.d.mts +7 -0
- package/dist/behaviors/tree.mjs +39 -15
- package/dist/behaviors/upload.d.mts +4 -0
- package/dist/behaviors/upload.mjs +79 -44
- package/dist/cli.mjs +1 -1
- package/dist/components/ai-chat.css +1037 -0
- package/dist/components/ai-chat.mjs +3 -0
- package/dist/components/ai-composer.mjs +3 -0
- package/dist/components/ai-context.mjs +2 -0
- package/dist/components/ai-diff.css +311 -0
- package/dist/components/ai-diff.mjs +3 -0
- package/dist/components/ai-files.mjs +3 -0
- package/dist/components/ai-message.mjs +3 -0
- package/dist/components/ai-panel.css +682 -0
- package/dist/components/ai-reasoning.mjs +3 -0
- package/dist/components/ai-subagent.mjs +2 -0
- package/dist/components/ai-todo.mjs +2 -0
- package/dist/components/ai-tool-call.mjs +2 -0
- package/dist/components/ai-tool.css +532 -0
- package/dist/components/ai-usage.mjs +3 -0
- package/dist/components/badge.css +5 -9
- package/dist/components/breadcrumb.css +0 -36
- package/dist/components/btn.css +4 -7
- package/dist/components/card.css +3 -6
- package/dist/components/chart-bar.css +10 -0
- package/dist/components/chart-calendar.mjs +4 -0
- package/dist/components/chart-gauge.css +1 -1
- package/dist/components/chart-heatmap.css +40 -0
- package/dist/components/chart-pie.css +0 -6
- package/dist/components/chart-scatter.css +24 -0
- package/dist/components/chart-scatter.mjs +4 -0
- package/dist/components/charts.css +87 -2
- package/dist/components/charts.mjs +1 -0
- package/dist/components/command-palette.css +2 -2
- package/dist/components/content.css +11 -25
- package/dist/components/copy.css +8 -8
- package/dist/components/datatable.css +5 -9
- package/dist/components/date-picker.css +4 -8
- package/dist/components/empty.css +0 -4
- package/dist/components/feedback.css +10 -17
- package/dist/components/form.css +13 -14
- package/dist/components/input.css +2 -2
- package/dist/components/layout.css +36 -36
- package/dist/components/media.css +12 -8
- package/dist/components/menu.css +2 -2
- package/dist/components/modal.css +1 -1
- package/dist/components/nav.css +11 -6
- package/dist/components/notification.css +27 -28
- package/dist/components/pagination.css +4 -2
- package/dist/components/pagination.mjs +1 -0
- package/dist/components/panel.css +16 -16
- package/dist/components/pill.css +15 -15
- package/dist/components/rating.mjs +1 -0
- package/dist/components/segmented.css +14 -6
- package/dist/components/segmented.mjs +1 -0
- package/dist/components/select.css +79 -24
- package/dist/components/sidebar.css +1 -1
- package/dist/components/split-pane.css +0 -1
- package/dist/components/stat.css +14 -18
- package/dist/components/steps.css +15 -5
- package/dist/components/steps.mjs +1 -0
- package/dist/components/switch.css +11 -13
- package/dist/components/switch.mjs +1 -0
- package/dist/components/table.css +46 -47
- package/dist/components/table.mjs +1 -0
- package/dist/components/tabs.css +57 -45
- package/dist/components/tag-input.css +5 -10
- package/dist/components/toast.css +16 -13
- package/dist/components/tooltip.css +2 -2
- package/dist/components/upload.css +0 -15
- package/dist/index.d.mts +39 -0
- package/dist/index.mjs +11243 -3312
- package/dist/registry.json +147 -9
- package/dist/retro-effects.css +23 -32
- package/dist/tokens/colors.css +4 -1
- package/dist/tokens/retro-effects.css +23 -32
- package/dist/tokens/style-profiles.css +24 -2
- package/dist/tokens/typography.css +2 -0
- package/dist/tokens.css +30 -3
- package/dist/ui.css +3420 -645
- package/docs/spec/ai-native.md +662 -0
- package/package.json +15 -5
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** 布局锚矩形(视口坐标,宽高齐全,元素锚或手工锚共用此形状)。 */
|
|
2
|
+
interface PopoverRect {
|
|
3
|
+
left: number;
|
|
4
|
+
top: number;
|
|
5
|
+
right: number;
|
|
6
|
+
bottom: number;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
}
|
|
10
|
+
/** 视口尺寸(可用空间计算的基准)。 */
|
|
11
|
+
interface PopoverViewport {
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
}
|
|
15
|
+
/** 浮层相对锚的展开侧。 */
|
|
16
|
+
type PopoverSide = 'top' | 'bottom' | 'left' | 'right';
|
|
17
|
+
/** 浮层与锚的对齐方式(stretch = 纵向侧时随锚宽拉伸)。 */
|
|
18
|
+
type PopoverAlign = 'start' | 'center' | 'end' | 'stretch';
|
|
19
|
+
interface PopoverLayoutOptions {
|
|
20
|
+
side?: PopoverSide;
|
|
21
|
+
align?: PopoverAlign;
|
|
22
|
+
offset?: number;
|
|
23
|
+
margin?: number;
|
|
24
|
+
minWidth?: number;
|
|
25
|
+
maxWidth?: number;
|
|
26
|
+
minHeight?: number;
|
|
27
|
+
maxHeight?: number;
|
|
28
|
+
/** 内容预期高度:避免用被裁剪的实测高度参与选侧 */
|
|
29
|
+
contentHeightHint?: number;
|
|
30
|
+
}
|
|
31
|
+
interface PanelSizing {
|
|
32
|
+
/** 固定宽(最高优先,跳过 min/max 夹取) */
|
|
33
|
+
width?: number;
|
|
34
|
+
/** 最小宽(默认 = 锚点/trigger 宽或组件 CSS 默认) */
|
|
35
|
+
minWidth?: number;
|
|
36
|
+
/** 最大宽 */
|
|
37
|
+
maxWidth?: number;
|
|
38
|
+
/** 最小高(与 PopoverLayoutOptions.minHeight 对齐) */
|
|
39
|
+
minHeight?: number;
|
|
40
|
+
/** 高度上限 */
|
|
41
|
+
maxHeight?: number;
|
|
42
|
+
}
|
|
43
|
+
/** 从元素读取 data-panel-* 尺寸属性(非法/缺失字段跳过,不抛异常)。 */
|
|
44
|
+
declare function readPanelSizing(el: Element | null | undefined): PanelSizing;
|
|
45
|
+
/** 合并尺寸:程序面 overrides 优先于属性面(undefined 字段不覆盖)。 */
|
|
46
|
+
declare function resolvePanelSizing(el: Element | null | undefined, overrides?: PanelSizing | null): PanelSizing;
|
|
47
|
+
/** 把 PanelSizing 折算进布局 opts:固定宽 → min=max;否则 min/max 夹取;maxHeight 透传。 */
|
|
48
|
+
declare function sizingToLayout<T extends {
|
|
49
|
+
minWidth?: number;
|
|
50
|
+
maxWidth?: number;
|
|
51
|
+
maxHeight?: number;
|
|
52
|
+
}>(sizing: PanelSizing, opts: T): T;
|
|
53
|
+
/** 把尺寸契约内联到自建面板(dropdown / context-menu / date-picker 等 fixed 面板)。
|
|
54
|
+
* 传 null 复位全部内联尺寸——单例面板跨实例复用时防「上次的设置串味」。
|
|
55
|
+
* 视口守卫:width / maxWidth 一律夹到 视口宽 − 24px(窄屏上过大的 data-panel-width 不溢出,
|
|
56
|
+
* 与 computePopoverLayout 的 availableWidth 同一纪律,不再两套标准)。 */
|
|
57
|
+
declare function applyPanelSizing(panel: HTMLElement, sizing: PanelSizing | null): void;
|
|
58
|
+
interface PopoverLayout {
|
|
59
|
+
side: PopoverSide;
|
|
60
|
+
left: number;
|
|
61
|
+
top: number;
|
|
62
|
+
width: number;
|
|
63
|
+
maxHeight: number;
|
|
64
|
+
}
|
|
65
|
+
interface OpenPopoverOptions extends PopoverLayoutOptions {
|
|
66
|
+
anchor: Element | PopoverRect;
|
|
67
|
+
/** 统一面板尺寸契约(优先于面板元素的 data-panel-* 与上方直接尺寸字段) */
|
|
68
|
+
sizing?: PanelSizing;
|
|
69
|
+
/** 关闭(含外点/Esc 触发的关闭)后的回调 */
|
|
70
|
+
onClose?: () => void;
|
|
71
|
+
}
|
|
72
|
+
/** 视口智能布局:返回 {side, left, top, width, maxHeight}。
|
|
73
|
+
* 纵向侧(top/bottom):选侧按上下空间,宽按 min/max 夹取,高按内容预期。
|
|
74
|
+
* 横向侧(left/right):锚点左右翻转,宽取 min(无内容测量,横向侧取下限),
|
|
75
|
+
* 高占满可用空间,纵向按 align(start/center/end;stretch 视作 start)贴锚。 */
|
|
76
|
+
declare function computePopoverLayout(anchor: PopoverRect, viewport: PopoverViewport, opts?: PopoverLayoutOptions): PopoverLayout;
|
|
77
|
+
/** 打开浮层:panel 移入 body 并按 anchor 布局;重复调用同一 panel 为先关后开。 */
|
|
78
|
+
declare function openPopover(panel: HTMLElement, options: OpenPopoverOptions): void;
|
|
79
|
+
/** 关闭浮层:移除监听并把 panel 还原到打开前的位置与 hidden 状态。 */
|
|
80
|
+
declare function closePopover(panel: HTMLElement): void;
|
|
81
|
+
|
|
82
|
+
export { type OpenPopoverOptions, type PanelSizing, type PopoverAlign, type PopoverLayout, type PopoverLayoutOptions, type PopoverRect, type PopoverSide, type PopoverViewport, applyPanelSizing, closePopover, computePopoverLayout, openPopover, readPanelSizing, resolvePanelSizing, sizingToLayout };
|
|
@@ -1,4 +1,63 @@
|
|
|
1
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 sizingToLayout(sizing, opts) {
|
|
30
|
+
const out = { ...opts };
|
|
31
|
+
if (sizing.minWidth != null) out.minWidth = sizing.minWidth;
|
|
32
|
+
if (sizing.maxWidth != null) out.maxWidth = sizing.maxWidth;
|
|
33
|
+
if (sizing.width != null) {
|
|
34
|
+
out.minWidth = sizing.width;
|
|
35
|
+
out.maxWidth = sizing.width;
|
|
36
|
+
}
|
|
37
|
+
if (sizing.maxHeight != null) out.maxHeight = sizing.maxHeight;
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
function applyPanelSizing(panel, sizing) {
|
|
41
|
+
if (sizing == null) {
|
|
42
|
+
panel.style.width = "";
|
|
43
|
+
panel.style.minWidth = "";
|
|
44
|
+
panel.style.maxWidth = "";
|
|
45
|
+
panel.style.minHeight = "";
|
|
46
|
+
panel.style.maxHeight = "";
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const vw = typeof document === "undefined" ? Number.POSITIVE_INFINITY : document.documentElement.clientWidth;
|
|
50
|
+
const fitW = (n) => Math.min(n, Math.max(80, vw - 24));
|
|
51
|
+
if (sizing.width != null) {
|
|
52
|
+
const w = fitW(sizing.width);
|
|
53
|
+
panel.style.width = `${w}px`;
|
|
54
|
+
panel.style.maxWidth = `${w}px`;
|
|
55
|
+
}
|
|
56
|
+
if (sizing.minWidth != null) panel.style.minWidth = `${fitW(sizing.minWidth)}px`;
|
|
57
|
+
if (sizing.maxWidth != null && sizing.width == null) panel.style.maxWidth = `${fitW(sizing.maxWidth)}px`;
|
|
58
|
+
if (sizing.minHeight != null) panel.style.minHeight = `${sizing.minHeight}px`;
|
|
59
|
+
if (sizing.maxHeight != null) panel.style.maxHeight = `${sizing.maxHeight}px`;
|
|
60
|
+
}
|
|
2
61
|
var DEFAULT_MIN_WIDTH = 220;
|
|
3
62
|
var DEFAULT_MAX_WIDTH = 420;
|
|
4
63
|
var DEFAULT_MIN_HEIGHT = 120;
|
|
@@ -23,13 +82,35 @@ function horizontalAnchor(anchor, width, align) {
|
|
|
23
82
|
function computePopoverLayout(anchor, viewport, opts = {}) {
|
|
24
83
|
const margin = opts.margin ?? 12;
|
|
25
84
|
const offset = opts.offset ?? 8;
|
|
85
|
+
const preferredSide = opts.side ?? "top";
|
|
86
|
+
const align = opts.align ?? "start";
|
|
87
|
+
if (preferredSide === "left" || preferredSide === "right") {
|
|
88
|
+
const availableLeft = Math.max(0, anchor.left - margin - offset);
|
|
89
|
+
const availableRight = Math.max(0, viewport.width - anchor.right - margin - offset);
|
|
90
|
+
const side2 = (preferredSide === "left" ? availableLeft : availableRight) >= DEFAULT_MIN_WIDTH ? preferredSide : preferredSide === "left" ? "right" : "left";
|
|
91
|
+
const minWidth2 = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
92
|
+
const maxWidth2 = Math.min(
|
|
93
|
+
Math.max(opts.maxWidth ?? DEFAULT_MAX_WIDTH, minWidth2),
|
|
94
|
+
side2 === "left" ? availableLeft : availableRight
|
|
95
|
+
);
|
|
96
|
+
const width2 = minWidth2;
|
|
97
|
+
const left2 = clamp(
|
|
98
|
+
side2 === "left" ? anchor.left - offset - width2 : anchor.right + offset,
|
|
99
|
+
margin,
|
|
100
|
+
viewport.width - margin - width2
|
|
101
|
+
);
|
|
102
|
+
const availableHeight2 = Math.max(80, viewport.height - margin * 2);
|
|
103
|
+
const maxHeight2 = Math.min(opts.maxHeight ?? DEFAULT_MAX_HEIGHT, availableHeight2);
|
|
104
|
+
const desired = clamp(opts.contentHeightHint ?? Math.min(anchor.height, maxHeight2), 40, maxHeight2);
|
|
105
|
+
const anchorTop = align === "center" ? anchor.top + anchor.height / 2 - desired / 2 : align === "end" ? anchor.bottom - desired : anchor.top;
|
|
106
|
+
const top2 = clamp(anchorTop, margin, viewport.height - margin - desired);
|
|
107
|
+
return { side: side2, left: left2, top: top2, width: width2, maxHeight: maxHeight2 };
|
|
108
|
+
}
|
|
26
109
|
const availableWidth = Math.max(80, viewport.width - margin * 2);
|
|
27
110
|
const requestedMinWidth = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
28
111
|
const requestedMaxWidth = Math.max(requestedMinWidth, opts.maxWidth ?? DEFAULT_MAX_WIDTH);
|
|
29
112
|
const maxWidth = Math.min(requestedMaxWidth, availableWidth);
|
|
30
113
|
const minWidth = Math.min(requestedMinWidth, maxWidth);
|
|
31
|
-
const preferredSide = opts.side ?? "top";
|
|
32
|
-
const align = opts.align ?? "start";
|
|
33
114
|
const availableTop = Math.max(0, anchor.top - margin - offset);
|
|
34
115
|
const availableBottom = Math.max(0, viewport.height - anchor.bottom - margin - offset);
|
|
35
116
|
const availableHeight = Math.max(80, viewport.height - margin * 2);
|
|
@@ -107,7 +188,8 @@ function onKeyDown(e) {
|
|
|
107
188
|
function openPopover(panel, options) {
|
|
108
189
|
if (typeof document === "undefined") return;
|
|
109
190
|
if (openPanels.has(panel)) closePopover(panel);
|
|
110
|
-
const { anchor, onClose, ...
|
|
191
|
+
const { anchor, onClose, sizing, ...rest } = options;
|
|
192
|
+
const layoutOpts = sizingToLayout(resolvePanelSizing(panel, sizing), rest);
|
|
111
193
|
const placeholder = document.createComment("popover-anchor");
|
|
112
194
|
panel.parentNode?.insertBefore(placeholder, panel);
|
|
113
195
|
const wasHidden = panel.hidden;
|
|
@@ -139,7 +221,8 @@ function closePopover(panel) {
|
|
|
139
221
|
if (!state) return;
|
|
140
222
|
openPanels.delete(panel);
|
|
141
223
|
panel.style.visibility = state.prevVisibility;
|
|
142
|
-
panel.hidden
|
|
224
|
+
if (typeof state.wasHidden === "string") panel.setAttribute("hidden", state.wasHidden);
|
|
225
|
+
else panel.hidden = state.wasHidden;
|
|
143
226
|
panel.style.left = "";
|
|
144
227
|
panel.style.top = "";
|
|
145
228
|
panel.style.width = "";
|
|
@@ -158,7 +241,11 @@ function closePopover(panel) {
|
|
|
158
241
|
state.onClose?.();
|
|
159
242
|
}
|
|
160
243
|
export {
|
|
244
|
+
applyPanelSizing,
|
|
161
245
|
closePopover,
|
|
162
246
|
computePopoverLayout,
|
|
163
|
-
openPopover
|
|
247
|
+
openPopover,
|
|
248
|
+
readPanelSizing,
|
|
249
|
+
resolvePanelSizing,
|
|
250
|
+
sizingToLayout
|
|
164
251
|
};
|
|
@@ -1,19 +1,188 @@
|
|
|
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
|
+
var DEFAULT_MIN_WIDTH = 220;
|
|
30
|
+
var DEFAULT_MAX_WIDTH = 420;
|
|
31
|
+
var DEFAULT_MIN_HEIGHT = 120;
|
|
32
|
+
var DEFAULT_MAX_HEIGHT = 420;
|
|
33
|
+
function clamp(value, min, max) {
|
|
34
|
+
if (max < min) return min;
|
|
35
|
+
return Math.min(max, Math.max(min, value));
|
|
36
|
+
}
|
|
37
|
+
function chooseSide(preferredSide, availableTop, availableBottom, desiredHeight) {
|
|
38
|
+
const preferredSpace = preferredSide === "top" ? availableTop : availableBottom;
|
|
39
|
+
const oppositeSpace = preferredSide === "top" ? availableBottom : availableTop;
|
|
40
|
+
if (preferredSpace >= desiredHeight) return preferredSide;
|
|
41
|
+
if (oppositeSpace >= desiredHeight) return preferredSide === "top" ? "bottom" : "top";
|
|
42
|
+
if (preferredSpace >= oppositeSpace) return preferredSide;
|
|
43
|
+
return preferredSide === "top" ? "bottom" : "top";
|
|
44
|
+
}
|
|
45
|
+
function horizontalAnchor(anchor, width, align) {
|
|
46
|
+
if (align === "center") return anchor.left + anchor.width / 2 - width / 2;
|
|
47
|
+
if (align === "end") return anchor.right - width;
|
|
48
|
+
return anchor.left;
|
|
49
|
+
}
|
|
50
|
+
function computePopoverLayout(anchor, viewport, opts = {}) {
|
|
51
|
+
const margin = opts.margin ?? 12;
|
|
52
|
+
const offset = opts.offset ?? 8;
|
|
53
|
+
const preferredSide = opts.side ?? "top";
|
|
54
|
+
const align = opts.align ?? "start";
|
|
55
|
+
if (preferredSide === "left" || preferredSide === "right") {
|
|
56
|
+
const availableLeft = Math.max(0, anchor.left - margin - offset);
|
|
57
|
+
const availableRight = Math.max(0, viewport.width - anchor.right - margin - offset);
|
|
58
|
+
const side2 = (preferredSide === "left" ? availableLeft : availableRight) >= DEFAULT_MIN_WIDTH ? preferredSide : preferredSide === "left" ? "right" : "left";
|
|
59
|
+
const minWidth2 = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
60
|
+
const maxWidth2 = Math.min(
|
|
61
|
+
Math.max(opts.maxWidth ?? DEFAULT_MAX_WIDTH, minWidth2),
|
|
62
|
+
side2 === "left" ? availableLeft : availableRight
|
|
63
|
+
);
|
|
64
|
+
const width2 = minWidth2;
|
|
65
|
+
const left2 = clamp(
|
|
66
|
+
side2 === "left" ? anchor.left - offset - width2 : anchor.right + offset,
|
|
67
|
+
margin,
|
|
68
|
+
viewport.width - margin - width2
|
|
69
|
+
);
|
|
70
|
+
const availableHeight2 = Math.max(80, viewport.height - margin * 2);
|
|
71
|
+
const maxHeight2 = Math.min(opts.maxHeight ?? DEFAULT_MAX_HEIGHT, availableHeight2);
|
|
72
|
+
const desired = clamp(opts.contentHeightHint ?? Math.min(anchor.height, maxHeight2), 40, maxHeight2);
|
|
73
|
+
const anchorTop = align === "center" ? anchor.top + anchor.height / 2 - desired / 2 : align === "end" ? anchor.bottom - desired : anchor.top;
|
|
74
|
+
const top2 = clamp(anchorTop, margin, viewport.height - margin - desired);
|
|
75
|
+
return { side: side2, left: left2, top: top2, width: width2, maxHeight: maxHeight2 };
|
|
76
|
+
}
|
|
77
|
+
const availableWidth = Math.max(80, viewport.width - margin * 2);
|
|
78
|
+
const requestedMinWidth = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
79
|
+
const requestedMaxWidth = Math.max(requestedMinWidth, opts.maxWidth ?? DEFAULT_MAX_WIDTH);
|
|
80
|
+
const maxWidth = Math.min(requestedMaxWidth, availableWidth);
|
|
81
|
+
const minWidth = Math.min(requestedMinWidth, maxWidth);
|
|
82
|
+
const availableTop = Math.max(0, anchor.top - margin - offset);
|
|
83
|
+
const availableBottom = Math.max(0, viewport.height - anchor.bottom - margin - offset);
|
|
84
|
+
const availableHeight = Math.max(80, viewport.height - margin * 2);
|
|
85
|
+
const minHeight = Math.min(Math.max(80, opts.minHeight ?? DEFAULT_MIN_HEIGHT), availableHeight);
|
|
86
|
+
const requestedMaxHeight = Math.max(minHeight, opts.maxHeight ?? DEFAULT_MAX_HEIGHT);
|
|
87
|
+
const fallbackHeight = Math.min(260, requestedMaxHeight);
|
|
88
|
+
const contentHeight = Math.max(opts.contentHeightHint ?? 0, fallbackHeight);
|
|
89
|
+
const desiredHeight = clamp(contentHeight, minHeight, Math.min(requestedMaxHeight, availableHeight));
|
|
90
|
+
const side = chooseSide(preferredSide, availableTop, availableBottom, desiredHeight);
|
|
91
|
+
const sideSpace = side === "top" ? availableTop : availableBottom;
|
|
92
|
+
const maxHeight = Math.min(
|
|
93
|
+
requestedMaxHeight,
|
|
94
|
+
Math.max(80, sideSpace || Math.max(availableTop, availableBottom)),
|
|
95
|
+
availableHeight
|
|
96
|
+
);
|
|
97
|
+
const positionedHeight = clamp(desiredHeight, 40, maxHeight);
|
|
98
|
+
const width = align === "stretch" ? clamp(anchor.width, minWidth, maxWidth) : maxWidth;
|
|
99
|
+
const left = clamp(horizontalAnchor(anchor, width, align), margin, viewport.width - margin - width);
|
|
100
|
+
const desiredTop = side === "top" ? anchor.top - offset - positionedHeight : anchor.bottom + offset;
|
|
101
|
+
const top = clamp(desiredTop, margin, viewport.height - margin - positionedHeight);
|
|
102
|
+
return { side, left, top, width, maxHeight };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/behaviors/events.ts
|
|
106
|
+
function emitIcen(source, type, detail) {
|
|
107
|
+
return source.dispatchEvent(
|
|
108
|
+
new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
1
112
|
// src/behaviors/select.ts
|
|
113
|
+
var openSelect = null;
|
|
114
|
+
var globalBound = false;
|
|
115
|
+
var PANEL_OFFSET = 4;
|
|
116
|
+
var PANEL_MAX_HEIGHT = 240;
|
|
117
|
+
function onGlobalPointerDown(ev) {
|
|
118
|
+
const cur = openSelect;
|
|
119
|
+
if (!cur) return;
|
|
120
|
+
const t = ev.target;
|
|
121
|
+
if (!(t instanceof Node)) return;
|
|
122
|
+
if (cur.container.contains(t) || cur.panel.contains(t)) return;
|
|
123
|
+
cur.close();
|
|
124
|
+
}
|
|
125
|
+
function onGlobalKeyDown(ev) {
|
|
126
|
+
if (ev.key !== "Escape") return;
|
|
127
|
+
const cur = openSelect;
|
|
128
|
+
if (!cur) return;
|
|
129
|
+
cur.close();
|
|
130
|
+
cur.trigger.focus();
|
|
131
|
+
}
|
|
132
|
+
function onWindowChange() {
|
|
133
|
+
const cur = openSelect;
|
|
134
|
+
if (!cur) return;
|
|
135
|
+
if (!cur.trigger.isConnected) {
|
|
136
|
+
cur.close();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
cur.reposition();
|
|
140
|
+
}
|
|
141
|
+
function bindGlobalListeners() {
|
|
142
|
+
if (globalBound) return;
|
|
143
|
+
globalBound = true;
|
|
144
|
+
document.addEventListener("pointerdown", onGlobalPointerDown);
|
|
145
|
+
document.addEventListener("keydown", onGlobalKeyDown);
|
|
146
|
+
}
|
|
147
|
+
function bindOpenListeners() {
|
|
148
|
+
window.addEventListener("scroll", onWindowChange, true);
|
|
149
|
+
window.addEventListener("resize", onWindowChange);
|
|
150
|
+
}
|
|
151
|
+
function unbindOpenListeners() {
|
|
152
|
+
window.removeEventListener("scroll", onWindowChange, true);
|
|
153
|
+
window.removeEventListener("resize", onWindowChange);
|
|
154
|
+
}
|
|
2
155
|
function setup(container) {
|
|
3
156
|
const el = container;
|
|
4
|
-
if (el.__icenSelectInit) return
|
|
5
|
-
|
|
157
|
+
if (el.__icenSelectInit) return () => {
|
|
158
|
+
};
|
|
159
|
+
const ac = new AbortController();
|
|
160
|
+
const on = (t, ev, fn) => {
|
|
161
|
+
const listener = (evt) => fn(evt);
|
|
162
|
+
t.addEventListener(ev, listener, { signal: ac.signal });
|
|
163
|
+
};
|
|
6
164
|
const triggerEl = container.querySelector(".select-trigger");
|
|
7
165
|
const panelEl = container.querySelector(".select-panel");
|
|
8
166
|
const valueEl = container.querySelector(".select-value");
|
|
9
167
|
const hiddenInput = container.querySelector("input[data-select-value]");
|
|
10
|
-
if (!triggerEl || !panelEl) return
|
|
168
|
+
if (!triggerEl || !panelEl) return () => {
|
|
169
|
+
};
|
|
170
|
+
el.__icenSelectInit = true;
|
|
11
171
|
const trigger = triggerEl;
|
|
12
172
|
const panel = panelEl;
|
|
13
173
|
const isMultiple = container.hasAttribute("data-select-multiple");
|
|
14
174
|
const isSearchable = container.hasAttribute("data-select-search");
|
|
15
175
|
const maxSelect = Number(container.getAttribute("data-select-max")) || 0;
|
|
176
|
+
const panelSizing = resolvePanelSizing(container);
|
|
177
|
+
function resolvedPanelWidth(triggerWidth) {
|
|
178
|
+
let w = triggerWidth;
|
|
179
|
+
if (panelSizing.minWidth != null) w = Math.max(w, panelSizing.minWidth);
|
|
180
|
+
if (panelSizing.maxWidth != null) w = Math.min(w, panelSizing.maxWidth);
|
|
181
|
+
if (panelSizing.width != null) w = panelSizing.width;
|
|
182
|
+
return w;
|
|
183
|
+
}
|
|
16
184
|
const placeholder = container.getAttribute("data-select-placeholder") ?? (valueEl?.classList.contains("is-empty") ? valueEl?.textContent ?? "" : "");
|
|
185
|
+
if (isMultiple) panel.setAttribute("data-select-multiple", "");
|
|
17
186
|
let searchInput = null;
|
|
18
187
|
let focusIdx = -1;
|
|
19
188
|
if (isSearchable && !panel.querySelector(".select-search-input")) {
|
|
@@ -79,8 +248,8 @@ function setup(container) {
|
|
|
79
248
|
setFocus(firstEnabled());
|
|
80
249
|
}
|
|
81
250
|
if (searchInput) {
|
|
82
|
-
searchInput
|
|
83
|
-
searchInput
|
|
251
|
+
on(searchInput, "input", () => filterOptions(searchInput.value));
|
|
252
|
+
on(searchInput, "keydown", (ev) => {
|
|
84
253
|
if (ev.key === "Escape") {
|
|
85
254
|
ev.stopPropagation();
|
|
86
255
|
searchInput.value = "";
|
|
@@ -89,11 +258,38 @@ function setup(container) {
|
|
|
89
258
|
}
|
|
90
259
|
});
|
|
91
260
|
}
|
|
261
|
+
function positionPanel() {
|
|
262
|
+
const rect = trigger.getBoundingClientRect();
|
|
263
|
+
const panelWidth = resolvedPanelWidth(rect.width);
|
|
264
|
+
const layout = computePopoverLayout(
|
|
265
|
+
rect,
|
|
266
|
+
{ width: window.innerWidth, height: window.innerHeight },
|
|
267
|
+
{
|
|
268
|
+
side: "bottom",
|
|
269
|
+
align: "stretch",
|
|
270
|
+
offset: PANEL_OFFSET,
|
|
271
|
+
minWidth: panelWidth,
|
|
272
|
+
maxWidth: panelWidth,
|
|
273
|
+
maxHeight: panelSizing.maxHeight ?? PANEL_MAX_HEIGHT
|
|
274
|
+
}
|
|
275
|
+
);
|
|
276
|
+
panel.style.left = `${layout.left + window.scrollX}px`;
|
|
277
|
+
panel.style.top = `${layout.top + window.scrollY}px`;
|
|
278
|
+
panel.style.minWidth = `${layout.width}px`;
|
|
279
|
+
panel.style.width = `${layout.width}px`;
|
|
280
|
+
panel.style.maxHeight = `${layout.maxHeight}px`;
|
|
281
|
+
panel.dataset.side = layout.side;
|
|
282
|
+
}
|
|
92
283
|
function openPanel() {
|
|
93
284
|
if (isOpen()) return;
|
|
285
|
+
openSelect?.close();
|
|
286
|
+
if (panel.parentElement !== document.body) document.body.appendChild(panel);
|
|
94
287
|
panel.hidden = false;
|
|
95
288
|
container.classList.add("is-open");
|
|
96
289
|
trigger.setAttribute("aria-expanded", "true");
|
|
290
|
+
positionPanel();
|
|
291
|
+
openSelect = { container, trigger, panel, close: closePanel, reposition: positionPanel };
|
|
292
|
+
bindOpenListeners();
|
|
97
293
|
if (searchInput) {
|
|
98
294
|
searchInput.value = "";
|
|
99
295
|
filterOptions("");
|
|
@@ -109,6 +305,8 @@ function setup(container) {
|
|
|
109
305
|
container.classList.remove("is-open");
|
|
110
306
|
trigger.setAttribute("aria-expanded", "false");
|
|
111
307
|
setFocus(-1);
|
|
308
|
+
if (openSelect?.panel === panel) openSelect = null;
|
|
309
|
+
unbindOpenListeners();
|
|
112
310
|
}
|
|
113
311
|
function getSelectedValues() {
|
|
114
312
|
return allOptions().filter((o) => o.classList.contains("is-selected")).map((o) => o.getAttribute("data-value") ?? "");
|
|
@@ -116,19 +314,59 @@ function setup(container) {
|
|
|
116
314
|
function updateMultiTrigger() {
|
|
117
315
|
if (!valueEl) return;
|
|
118
316
|
const selected = allOptions().filter((o) => o.classList.contains("is-selected"));
|
|
317
|
+
let chipsEl = trigger.querySelector(".select-chips");
|
|
119
318
|
if (selected.length === 0) {
|
|
120
319
|
valueEl.textContent = placeholder;
|
|
121
320
|
valueEl.classList.add("is-empty");
|
|
321
|
+
valueEl.hidden = false;
|
|
322
|
+
if (chipsEl) chipsEl.hidden = true;
|
|
122
323
|
container.removeAttribute("data-value");
|
|
123
324
|
} else {
|
|
124
325
|
valueEl.classList.remove("is-empty");
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
valueEl.
|
|
326
|
+
valueEl.hidden = true;
|
|
327
|
+
if (!chipsEl) {
|
|
328
|
+
chipsEl = document.createElement("span");
|
|
329
|
+
chipsEl.className = "select-chips";
|
|
330
|
+
valueEl.insertAdjacentElement("afterend", chipsEl);
|
|
130
331
|
}
|
|
332
|
+
chipsEl.hidden = false;
|
|
333
|
+
chipsEl.textContent = "";
|
|
334
|
+
const maxLabel = Number(container.getAttribute("data-select-max-label")) || 3;
|
|
335
|
+
const shown = selected.slice(0, maxLabel);
|
|
336
|
+
const rest = selected.length - shown.length;
|
|
337
|
+
for (const o of shown) {
|
|
338
|
+
const label = o.textContent ?? "";
|
|
339
|
+
const chip = document.createElement("span");
|
|
340
|
+
chip.className = "select-chip";
|
|
341
|
+
const lb = document.createElement("span");
|
|
342
|
+
lb.textContent = label;
|
|
343
|
+
chip.appendChild(lb);
|
|
344
|
+
const x = document.createElement("span");
|
|
345
|
+
x.className = "select-chip-x";
|
|
346
|
+
x.setAttribute("role", "button");
|
|
347
|
+
x.setAttribute("tabindex", "-1");
|
|
348
|
+
x.setAttribute("aria-label", `\u79FB\u9664 ${label}`);
|
|
349
|
+
x.textContent = "\xD7";
|
|
350
|
+
on(x, "pointerdown", (ev) => ev.stopPropagation());
|
|
351
|
+
on(x, "click", (ev) => {
|
|
352
|
+
ev.stopPropagation();
|
|
353
|
+
ev.preventDefault();
|
|
354
|
+
o.classList.remove("is-selected");
|
|
355
|
+
updateMultiTrigger();
|
|
356
|
+
syncHiddenInput();
|
|
357
|
+
});
|
|
358
|
+
chip.appendChild(x);
|
|
359
|
+
chipsEl.appendChild(chip);
|
|
360
|
+
}
|
|
361
|
+
if (rest > 0) {
|
|
362
|
+
const more = document.createElement("span");
|
|
363
|
+
more.className = "select-chip select-chip--more";
|
|
364
|
+
more.textContent = `+${rest}`;
|
|
365
|
+
chipsEl.appendChild(more);
|
|
366
|
+
}
|
|
367
|
+
container.setAttribute("data-value", getSelectedValues().join(","));
|
|
131
368
|
}
|
|
369
|
+
if (clearBtn) clearBtn.hidden = selected.length === 0;
|
|
132
370
|
if (maxSelect > 0) {
|
|
133
371
|
const atMax = selected.length >= maxSelect;
|
|
134
372
|
allOptions().forEach((o) => {
|
|
@@ -139,8 +377,10 @@ function setup(container) {
|
|
|
139
377
|
}
|
|
140
378
|
}
|
|
141
379
|
function syncHiddenInput() {
|
|
142
|
-
if (!hiddenInput) return;
|
|
143
380
|
const values = getSelectedValues();
|
|
381
|
+
const label = allOptions().filter((o) => o.classList.contains("is-selected")).map((o) => (o.textContent ?? "").trim()).join(", ");
|
|
382
|
+
emitIcen(container, "icen:select-change", { value: values[0] ?? "", values, label });
|
|
383
|
+
if (!hiddenInput) return;
|
|
144
384
|
hiddenInput.value = isMultiple ? values.join(",") : values[0] ?? "";
|
|
145
385
|
hiddenInput.dispatchEvent(new Event("change", { bubbles: true }));
|
|
146
386
|
}
|
|
@@ -171,16 +411,16 @@ function setup(container) {
|
|
|
171
411
|
}
|
|
172
412
|
const clearBtn = container.querySelector(".select-clear");
|
|
173
413
|
if (clearBtn && isMultiple) {
|
|
174
|
-
clearBtn
|
|
414
|
+
on(clearBtn, "click", (ev) => {
|
|
175
415
|
ev.stopPropagation();
|
|
176
416
|
allOptions().forEach((o) => o.classList.remove("is-selected"));
|
|
177
417
|
updateMultiTrigger();
|
|
178
418
|
syncHiddenInput();
|
|
179
419
|
});
|
|
180
|
-
clearBtn
|
|
420
|
+
on(clearBtn, "pointerdown", (ev) => ev.stopPropagation());
|
|
181
421
|
}
|
|
182
|
-
trigger
|
|
183
|
-
trigger
|
|
422
|
+
on(trigger, "click", () => isOpen() ? closePanel() : openPanel());
|
|
423
|
+
on(trigger, "keydown", (ev) => {
|
|
184
424
|
if (!isOpen()) {
|
|
185
425
|
if (ev.key === "ArrowDown" || ev.key === "ArrowUp" || ev.key === "Enter" || ev.key === " ") {
|
|
186
426
|
ev.preventDefault();
|
|
@@ -188,20 +428,6 @@ function setup(container) {
|
|
|
188
428
|
}
|
|
189
429
|
return;
|
|
190
430
|
}
|
|
191
|
-
if (searchInput && document.activeElement === searchInput) {
|
|
192
|
-
if (ev.key === "ArrowDown") {
|
|
193
|
-
ev.preventDefault();
|
|
194
|
-
moveFocus(1);
|
|
195
|
-
} else if (ev.key === "ArrowUp") {
|
|
196
|
-
ev.preventDefault();
|
|
197
|
-
moveFocus(-1);
|
|
198
|
-
} else if (ev.key === "Enter") {
|
|
199
|
-
ev.preventDefault();
|
|
200
|
-
const opt = visibleOptions()[focusIdx];
|
|
201
|
-
if (opt) choose(opt);
|
|
202
|
-
}
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
431
|
switch (ev.key) {
|
|
206
432
|
case "ArrowDown":
|
|
207
433
|
ev.preventDefault();
|
|
@@ -224,13 +450,13 @@ function setup(container) {
|
|
|
224
450
|
break;
|
|
225
451
|
}
|
|
226
452
|
});
|
|
227
|
-
panel
|
|
453
|
+
on(panel, "click", (ev) => {
|
|
228
454
|
const t = ev.target;
|
|
229
455
|
if (!(t instanceof Element)) return;
|
|
230
456
|
const opt = t.closest(".select-option");
|
|
231
457
|
if (opt instanceof HTMLButtonElement && panel.contains(opt)) choose(opt);
|
|
232
458
|
});
|
|
233
|
-
panel
|
|
459
|
+
on(panel, "mouseover", (ev) => {
|
|
234
460
|
const t = ev.target;
|
|
235
461
|
if (!(t instanceof Element)) return;
|
|
236
462
|
const opt = t.closest(".select-option");
|
|
@@ -238,23 +464,39 @@ function setup(container) {
|
|
|
238
464
|
const idx = visibleOptions().indexOf(opt);
|
|
239
465
|
if (idx >= 0 && !opt.classList.contains("is-disabled")) setFocus(idx);
|
|
240
466
|
});
|
|
241
|
-
document.addEventListener("pointerdown", (ev) => {
|
|
242
|
-
if (isOpen() && ev.target instanceof Node && !container.contains(ev.target)) closePanel();
|
|
243
|
-
});
|
|
244
|
-
document.addEventListener("keydown", (ev) => {
|
|
245
|
-
if (ev.key === "Escape" && isOpen()) {
|
|
246
|
-
closePanel();
|
|
247
|
-
trigger.focus();
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
467
|
if (isMultiple) updateMultiTrigger();
|
|
468
|
+
return () => {
|
|
469
|
+
if (openSelect?.panel === panel) closePanel();
|
|
470
|
+
ac.abort();
|
|
471
|
+
el.__icenSelectInit = false;
|
|
472
|
+
};
|
|
251
473
|
}
|
|
252
|
-
|
|
253
|
-
|
|
474
|
+
var activeSetups = /* @__PURE__ */ new Set();
|
|
475
|
+
function initSelect(root) {
|
|
476
|
+
if (typeof document === "undefined") return () => {
|
|
477
|
+
};
|
|
478
|
+
const scope = root ?? document;
|
|
479
|
+
bindGlobalListeners();
|
|
254
480
|
const containers = [];
|
|
255
|
-
if (
|
|
256
|
-
containers.push(...Array.from(
|
|
257
|
-
|
|
481
|
+
if (scope instanceof Element && scope.matches("[data-select]")) containers.push(scope);
|
|
482
|
+
containers.push(...Array.from(scope.querySelectorAll("[data-select]")));
|
|
483
|
+
const disposers = [];
|
|
484
|
+
for (const c of containers) {
|
|
485
|
+
const dispose = setup(c);
|
|
486
|
+
activeSetups.add(dispose);
|
|
487
|
+
disposers.push(dispose);
|
|
488
|
+
}
|
|
489
|
+
return () => {
|
|
490
|
+
for (const d of disposers) {
|
|
491
|
+
d();
|
|
492
|
+
activeSetups.delete(d);
|
|
493
|
+
}
|
|
494
|
+
if (activeSetups.size === 0 && globalBound) {
|
|
495
|
+
document.removeEventListener("pointerdown", onGlobalPointerDown);
|
|
496
|
+
document.removeEventListener("keydown", onGlobalKeyDown);
|
|
497
|
+
globalBound = false;
|
|
498
|
+
}
|
|
499
|
+
};
|
|
258
500
|
}
|
|
259
501
|
export {
|
|
260
502
|
initSelect
|