@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
|
@@ -1,8 +1,132 @@
|
|
|
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;
|
|
4
127
|
var outsideHandler = null;
|
|
5
128
|
var escHandler = null;
|
|
129
|
+
var repositionHandler = null;
|
|
6
130
|
function pad(n) {
|
|
7
131
|
return n < 10 ? "0" + n : String(n);
|
|
8
132
|
}
|
|
@@ -32,18 +156,54 @@ function ensurePanel() {
|
|
|
32
156
|
}
|
|
33
157
|
function positionPanel(panel, trigger) {
|
|
34
158
|
const r = trigger.getBoundingClientRect();
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
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`;
|
|
174
|
+
}
|
|
175
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
176
|
+
function buildCalIcon() {
|
|
177
|
+
const svg = document.createElementNS(SVG_NS, "svg");
|
|
178
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
179
|
+
svg.setAttribute("fill", "none");
|
|
180
|
+
svg.setAttribute("stroke", "currentColor");
|
|
181
|
+
svg.setAttribute("stroke-width", "2");
|
|
182
|
+
svg.setAttribute("stroke-linecap", "round");
|
|
183
|
+
svg.setAttribute("stroke-linejoin", "round");
|
|
184
|
+
const rect = document.createElementNS(SVG_NS, "rect");
|
|
185
|
+
rect.setAttribute("x", "3");
|
|
186
|
+
rect.setAttribute("y", "4");
|
|
187
|
+
rect.setAttribute("width", "18");
|
|
188
|
+
rect.setAttribute("height", "18");
|
|
189
|
+
rect.setAttribute("rx", "2");
|
|
190
|
+
rect.setAttribute("ry", "2");
|
|
191
|
+
svg.appendChild(rect);
|
|
192
|
+
const lines = [
|
|
193
|
+
["16", "16", "2", "6"],
|
|
194
|
+
["8", "8", "2", "6"],
|
|
195
|
+
["3", "21", "10", "10"]
|
|
196
|
+
];
|
|
197
|
+
for (const [x1, x2, y1, y2] of lines) {
|
|
198
|
+
const line = document.createElementNS(SVG_NS, "line");
|
|
199
|
+
line.setAttribute("x1", x1);
|
|
200
|
+
line.setAttribute("x2", x2);
|
|
201
|
+
line.setAttribute("y1", y1);
|
|
202
|
+
line.setAttribute("y2", y2);
|
|
203
|
+
svg.appendChild(line);
|
|
204
|
+
}
|
|
205
|
+
return svg;
|
|
206
|
+
}
|
|
47
207
|
var WEEK_LABELS_ZH = ["\u4E00", "\u4E8C", "\u4E09", "\u56DB", "\u4E94", "\u516D", "\u65E5"];
|
|
48
208
|
var WEEK_LABELS_SUN = ["\u65E5", "\u4E00", "\u4E8C", "\u4E09", "\u56DB", "\u4E94", "\u516D"];
|
|
49
209
|
function renderCalendar(panel, ctx) {
|
|
@@ -61,49 +221,72 @@ function renderCalendar(panel, ctx) {
|
|
|
61
221
|
for (let i = 0; i < 42; i++) {
|
|
62
222
|
cells.push(new Date(gridStart.getFullYear(), gridStart.getMonth(), gridStart.getDate() + i));
|
|
63
223
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
224
|
+
panel.textContent = "";
|
|
225
|
+
const head = document.createElement("div");
|
|
226
|
+
head.className = "date-picker-head";
|
|
227
|
+
const prevBtn = document.createElement("button");
|
|
228
|
+
prevBtn.type = "button";
|
|
229
|
+
prevBtn.className = "date-picker-nav";
|
|
230
|
+
prevBtn.setAttribute("aria-label", "\u4E0A\u4E00\u6708");
|
|
231
|
+
prevBtn.textContent = "\u2039";
|
|
232
|
+
const title = document.createElement("span");
|
|
233
|
+
title.className = "date-picker-title";
|
|
234
|
+
title.textContent = `${year} \u5E74 ${month + 1} \u6708`;
|
|
235
|
+
const nextBtn = document.createElement("button");
|
|
236
|
+
nextBtn.type = "button";
|
|
237
|
+
nextBtn.className = "date-picker-nav";
|
|
238
|
+
nextBtn.setAttribute("aria-label", "\u4E0B\u4E00\u6708");
|
|
239
|
+
nextBtn.textContent = "\u203A";
|
|
240
|
+
head.append(prevBtn, title, nextBtn);
|
|
241
|
+
const weekdays = document.createElement("div");
|
|
242
|
+
weekdays.className = "date-picker-weekdays";
|
|
243
|
+
for (const label of labels) {
|
|
244
|
+
const s = document.createElement("span");
|
|
245
|
+
s.textContent = label;
|
|
246
|
+
weekdays.appendChild(s);
|
|
247
|
+
}
|
|
248
|
+
const grid = document.createElement("div");
|
|
249
|
+
grid.className = "date-picker-grid";
|
|
69
250
|
for (const d of cells) {
|
|
70
251
|
const isOutside = d.getMonth() !== month;
|
|
71
|
-
const ds = toDateStr(d);
|
|
72
252
|
const isToday = d.getTime() === today.getTime();
|
|
73
|
-
const isSelected = selected &&
|
|
74
|
-
const disabled = min && d < min || max && d > max;
|
|
253
|
+
const isSelected = selected !== null && toDateStr(d) === toDateStr(selected);
|
|
254
|
+
const disabled = min !== null && d < min || max !== null && d > max;
|
|
75
255
|
const classes = ["date-picker-cell"];
|
|
76
256
|
if (isOutside) classes.push("is-outside");
|
|
77
257
|
if (isToday) classes.push("is-today");
|
|
78
258
|
if (isSelected) classes.push("is-selected");
|
|
79
259
|
if (disabled) classes.push("is-disabled");
|
|
80
|
-
|
|
260
|
+
const btn = document.createElement("button");
|
|
261
|
+
btn.type = "button";
|
|
262
|
+
btn.className = classes.join(" ");
|
|
263
|
+
btn.textContent = String(d.getDate());
|
|
264
|
+
if (disabled) btn.disabled = true;
|
|
265
|
+
btn.addEventListener("click", () => onPick(d));
|
|
266
|
+
grid.appendChild(btn);
|
|
81
267
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
268
|
+
const foot = document.createElement("div");
|
|
269
|
+
foot.className = "date-picker-foot";
|
|
270
|
+
const todayBtn = document.createElement("button");
|
|
271
|
+
todayBtn.type = "button";
|
|
272
|
+
todayBtn.className = "date-picker-today";
|
|
273
|
+
todayBtn.textContent = "\u4ECA\u5929";
|
|
274
|
+
const clearBtn = document.createElement("button");
|
|
275
|
+
clearBtn.type = "button";
|
|
276
|
+
clearBtn.className = "date-picker-clear";
|
|
277
|
+
clearBtn.textContent = "\u6E05\u9664";
|
|
278
|
+
foot.append(todayBtn, clearBtn);
|
|
279
|
+
panel.append(head, weekdays, grid, foot);
|
|
280
|
+
prevBtn.addEventListener("click", () => {
|
|
281
|
+
renderCalendar(panel, { ...ctx, view: new Date(year, month - 1, 15) });
|
|
90
282
|
});
|
|
91
|
-
|
|
92
|
-
ctx
|
|
93
|
-
renderCalendar(panel, { ...ctx, view: ctx.view });
|
|
283
|
+
nextBtn.addEventListener("click", () => {
|
|
284
|
+
renderCalendar(panel, { ...ctx, view: new Date(year, month + 1, 15) });
|
|
94
285
|
});
|
|
95
|
-
|
|
96
|
-
btn.addEventListener("click", () => {
|
|
97
|
-
const ds = btn.dataset.dpDate;
|
|
98
|
-
if (!ds) return;
|
|
99
|
-
const d = parseDateStr(ds);
|
|
100
|
-
if (d) onPick(d);
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
panel.querySelector("[data-dp-today]")?.addEventListener("click", () => {
|
|
286
|
+
todayBtn.addEventListener("click", () => {
|
|
104
287
|
onPick(new Date(today.getFullYear(), today.getMonth(), today.getDate()));
|
|
105
288
|
});
|
|
106
|
-
|
|
289
|
+
clearBtn.addEventListener("click", () => {
|
|
107
290
|
onPick(null);
|
|
108
291
|
});
|
|
109
292
|
}
|
|
@@ -120,11 +303,26 @@ function closePanel() {
|
|
|
120
303
|
window.removeEventListener("keydown", escHandler);
|
|
121
304
|
escHandler = null;
|
|
122
305
|
}
|
|
306
|
+
if (repositionHandler) {
|
|
307
|
+
window.removeEventListener("scroll", repositionHandler);
|
|
308
|
+
window.removeEventListener("resize", repositionHandler);
|
|
309
|
+
repositionHandler = null;
|
|
310
|
+
}
|
|
123
311
|
}
|
|
312
|
+
var pickersInit = /* @__PURE__ */ new WeakSet();
|
|
124
313
|
function setupPicker(container) {
|
|
314
|
+
if (pickersInit.has(container)) return () => {
|
|
315
|
+
};
|
|
316
|
+
pickersInit.add(container);
|
|
125
317
|
const el = container;
|
|
126
|
-
|
|
127
|
-
|
|
318
|
+
const ac = new AbortController();
|
|
319
|
+
const disposers = [
|
|
320
|
+
() => {
|
|
321
|
+
if (openPicker === el) closePanel();
|
|
322
|
+
ac.abort();
|
|
323
|
+
pickersInit.delete(container);
|
|
324
|
+
}
|
|
325
|
+
];
|
|
128
326
|
const fmt = el.dataset.datePickerFormat ?? "yyyy-mm-dd";
|
|
129
327
|
const minStr = el.dataset.datePickerMin;
|
|
130
328
|
const maxStr = el.dataset.datePickerMax;
|
|
@@ -140,7 +338,12 @@ function setupPicker(container) {
|
|
|
140
338
|
trigger = document.createElement("button");
|
|
141
339
|
trigger.type = "button";
|
|
142
340
|
trigger.className = "date-picker-trigger";
|
|
143
|
-
|
|
341
|
+
const value = document.createElement("span");
|
|
342
|
+
value.className = initialDate ? "date-picker-value" : "date-picker-value is-empty";
|
|
343
|
+
const icon = document.createElement("span");
|
|
344
|
+
icon.className = "date-picker-icon";
|
|
345
|
+
icon.appendChild(buildCalIcon());
|
|
346
|
+
trigger.append(value, icon);
|
|
144
347
|
el.prepend(trigger);
|
|
145
348
|
}
|
|
146
349
|
trigger.setAttribute("aria-haspopup", "dialog");
|
|
@@ -157,8 +360,9 @@ function setupPicker(container) {
|
|
|
157
360
|
if (hidden) {
|
|
158
361
|
const obs = new MutationObserver(() => paint());
|
|
159
362
|
obs.observe(hidden, { attributes: true, attributeFilter: ["value"] });
|
|
363
|
+
disposers.push(() => obs.disconnect());
|
|
160
364
|
}
|
|
161
|
-
|
|
365
|
+
const onTriggerClick = () => {
|
|
162
366
|
if (openPicker === el) {
|
|
163
367
|
closePanel();
|
|
164
368
|
return;
|
|
@@ -169,7 +373,7 @@ function setupPicker(container) {
|
|
|
169
373
|
const panel = ensurePanel();
|
|
170
374
|
panel.hidden = false;
|
|
171
375
|
const selected = hidden?.value ? parseDateStr(hidden.value) : null;
|
|
172
|
-
const view = selected
|
|
376
|
+
const view = selected ? new Date(selected.getTime()) : /* @__PURE__ */ new Date();
|
|
173
377
|
view.setDate(15);
|
|
174
378
|
positionPanel(panel, trigger);
|
|
175
379
|
renderCalendar(panel, {
|
|
@@ -179,17 +383,9 @@ function setupPicker(container) {
|
|
|
179
383
|
max,
|
|
180
384
|
weekStart,
|
|
181
385
|
onPick: (d) => {
|
|
182
|
-
if (d
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
hidden.dispatchEvent(new Event("change", { bubbles: true }));
|
|
186
|
-
}
|
|
187
|
-
} else {
|
|
188
|
-
if (hidden) {
|
|
189
|
-
hidden.value = toDateStr(d);
|
|
190
|
-
hidden.dispatchEvent(new Event("change", { bubbles: true }));
|
|
191
|
-
}
|
|
192
|
-
}
|
|
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 }));
|
|
193
389
|
paint();
|
|
194
390
|
closePanel();
|
|
195
391
|
}
|
|
@@ -208,20 +404,31 @@ function setupPicker(container) {
|
|
|
208
404
|
}
|
|
209
405
|
};
|
|
210
406
|
window.addEventListener("keydown", escHandler);
|
|
211
|
-
|
|
212
|
-
if (openPicker === el &&
|
|
407
|
+
repositionHandler = () => {
|
|
408
|
+
if (openPicker === el && !panel.hidden) positionPanel(panel, trigger);
|
|
213
409
|
};
|
|
214
|
-
window.addEventListener("scroll",
|
|
215
|
-
window.addEventListener("resize",
|
|
216
|
-
}
|
|
410
|
+
window.addEventListener("scroll", repositionHandler, { passive: true });
|
|
411
|
+
window.addEventListener("resize", repositionHandler, { passive: true });
|
|
412
|
+
};
|
|
413
|
+
trigger.addEventListener("click", onTriggerClick, { signal: ac.signal });
|
|
414
|
+
return () => {
|
|
415
|
+
for (const d of disposers) d();
|
|
416
|
+
};
|
|
217
417
|
}
|
|
218
|
-
function initDatePicker(root
|
|
219
|
-
if (typeof document === "undefined") return
|
|
220
|
-
|
|
418
|
+
function initDatePicker(root) {
|
|
419
|
+
if (typeof document === "undefined") return () => {
|
|
420
|
+
};
|
|
421
|
+
if (typeof window === "undefined") return () => {
|
|
422
|
+
};
|
|
423
|
+
const scope = root ?? document;
|
|
221
424
|
const pickers = [];
|
|
222
|
-
if (
|
|
223
|
-
pickers.push(...Array.from(
|
|
224
|
-
|
|
425
|
+
if (scope instanceof Element && scope.matches(".date-picker[data-date-picker]")) pickers.push(scope);
|
|
426
|
+
pickers.push(...Array.from(scope.querySelectorAll(".date-picker[data-date-picker]")));
|
|
427
|
+
const disposers = [];
|
|
428
|
+
for (const p of pickers) disposers.push(setupPicker(p));
|
|
429
|
+
return () => {
|
|
430
|
+
for (const d of disposers) d();
|
|
431
|
+
};
|
|
225
432
|
}
|
|
226
433
|
export {
|
|
227
434
|
initDatePicker
|