HEL-373: 主题化 combobox 覆盖夜间下拉全状态,日期面板支持年月直选
原生 select 弹层无法可靠套黑金 token,改为可访问 listbox;日期标题增加年份/月份下拉,越月不提交非法日。 Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Cursor
multica-agent
parent
1061f125a8
commit
6eccbe9765
+102
-3
@@ -4,10 +4,14 @@
|
||||
var panel = null;
|
||||
var grid = null;
|
||||
var titleEl = null;
|
||||
var yearSelect = null;
|
||||
var monthSelect = null;
|
||||
var activeInput = null;
|
||||
var viewYear = 0;
|
||||
var viewMonth = 0;
|
||||
var bound = false;
|
||||
var YEAR_PAD = 80;
|
||||
var YEAR_AHEAD = 20;
|
||||
|
||||
function pad(n) {
|
||||
return n < 10 ? "0" + n : String(n);
|
||||
@@ -46,7 +50,10 @@
|
||||
panel.innerHTML =
|
||||
'<div class="ds-dp-head">' +
|
||||
'<button type="button" class="ds-dp-nav" data-dp-nav="-1" aria-label="上一月">‹</button>' +
|
||||
'<div class="ds-dp-title"></div>' +
|
||||
'<div class="ds-dp-title">' +
|
||||
'<select class="select ds-dp-ym" data-dp-year aria-label="年份"></select>' +
|
||||
'<select class="select ds-dp-ym" data-dp-month aria-label="月份"></select>' +
|
||||
"</div>" +
|
||||
'<button type="button" class="ds-dp-nav" data-dp-nav="1" aria-label="下一月">›</button>' +
|
||||
"</div>" +
|
||||
'<div class="ds-dp-week">' + WEEK.map(function (d) { return "<span>" + d + "</span>"; }).join("") + "</div>" +
|
||||
@@ -57,11 +64,27 @@
|
||||
"</div>";
|
||||
document.body.appendChild(panel);
|
||||
titleEl = panel.querySelector(".ds-dp-title");
|
||||
yearSelect = panel.querySelector("[data-dp-year]");
|
||||
monthSelect = panel.querySelector("[data-dp-month]");
|
||||
grid = panel.querySelector(".ds-dp-grid");
|
||||
yearSelect.addEventListener("change", function () {
|
||||
var next = Number(yearSelect.value);
|
||||
if (!next) return;
|
||||
viewYear = next;
|
||||
render();
|
||||
});
|
||||
monthSelect.addEventListener("change", function () {
|
||||
var next = Number(monthSelect.value);
|
||||
if (Number.isNaN(next)) return;
|
||||
viewMonth = next;
|
||||
render();
|
||||
});
|
||||
panel.addEventListener("mousedown", function (event) {
|
||||
if (event.target.closest(".ds-combo, select, input, textarea")) return;
|
||||
event.preventDefault();
|
||||
});
|
||||
panel.addEventListener("click", function (event) {
|
||||
if (event.target.closest(".ds-combo, .ds-listbox, select")) return;
|
||||
var nav = event.target.closest("[data-dp-nav]");
|
||||
if (nav) {
|
||||
shiftMonth(Number(nav.getAttribute("data-dp-nav")));
|
||||
@@ -105,9 +128,75 @@
|
||||
document.querySelectorAll('input[type="date"]').forEach(enhance);
|
||||
}
|
||||
|
||||
function boundYears() {
|
||||
var now = new Date().getFullYear();
|
||||
var minY = now - YEAR_PAD;
|
||||
var maxY = now + YEAR_AHEAD;
|
||||
var hasMin = false;
|
||||
var hasMax = false;
|
||||
if (activeInput && activeInput.min) {
|
||||
var minDate = parseISO(activeInput.min);
|
||||
if (minDate) {
|
||||
minY = minDate.getFullYear();
|
||||
hasMin = true;
|
||||
}
|
||||
}
|
||||
if (activeInput && activeInput.max) {
|
||||
var maxDate = parseISO(activeInput.max);
|
||||
if (maxDate) {
|
||||
maxY = maxDate.getFullYear();
|
||||
hasMax = true;
|
||||
}
|
||||
}
|
||||
if (!hasMin && viewYear < minY) minY = viewYear;
|
||||
if (!hasMax && viewYear > maxY) maxY = viewYear;
|
||||
if (hasMin && viewYear < minY) minY = viewYear;
|
||||
if (hasMax && viewYear > maxY) maxY = viewYear;
|
||||
return { minY: minY, maxY: maxY };
|
||||
}
|
||||
|
||||
function lastDayOfMonth(year, month) {
|
||||
return new Date(year, month + 1, 0).getDate();
|
||||
}
|
||||
|
||||
function monthHasValidDay(year, month) {
|
||||
var min = activeInput && activeInput.min ? activeInput.min : "";
|
||||
var max = activeInput && activeInput.max ? activeInput.max : "";
|
||||
var start = toISO(year, month, 1);
|
||||
var end = toISO(year, month, lastDayOfMonth(year, month));
|
||||
if (min && end < min) return false;
|
||||
if (max && start > max) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function syncYearMonthSelects() {
|
||||
if (!yearSelect || !monthSelect) return;
|
||||
var years = boundYears();
|
||||
var yearHtml = "";
|
||||
for (var year = years.minY; year <= years.maxY; year += 1) {
|
||||
yearHtml += '<option value="' + year + '"' + (year === viewYear ? " selected" : "") + ">" + year + "年</option>";
|
||||
}
|
||||
yearSelect.innerHTML = yearHtml;
|
||||
yearSelect.value = String(viewYear);
|
||||
var monthHtml = "";
|
||||
for (var month = 0; month < 12; month += 1) {
|
||||
var disabled = !monthHasValidDay(viewYear, month);
|
||||
monthHtml +=
|
||||
'<option value="' + month + '"' +
|
||||
(month === viewMonth ? " selected" : "") +
|
||||
(disabled ? " disabled" : "") +
|
||||
">" + pad(month + 1) + "月</option>";
|
||||
}
|
||||
monthSelect.innerHTML = monthHtml;
|
||||
monthSelect.value = String(viewMonth);
|
||||
if (global.JinniuSelect && typeof global.JinniuSelect.scan === "function") {
|
||||
global.JinniuSelect.scan(panel);
|
||||
}
|
||||
}
|
||||
|
||||
function render() {
|
||||
if (!grid || !titleEl) return;
|
||||
titleEl.textContent = viewYear + "年" + pad(viewMonth + 1) + "月";
|
||||
syncYearMonthSelects();
|
||||
var first = new Date(viewYear, viewMonth, 1);
|
||||
var start = first.getDay();
|
||||
var selected = activeInput ? parseISO(activeInput.value) : null;
|
||||
@@ -186,6 +275,9 @@
|
||||
|
||||
function closePicker() {
|
||||
if (!panel || panel.hidden) return;
|
||||
if (global.JinniuSelect && typeof global.JinniuSelect.close === "function") {
|
||||
global.JinniuSelect.close();
|
||||
}
|
||||
panel.hidden = true;
|
||||
if (activeInput) activeInput.setAttribute("aria-expanded", "false");
|
||||
activeInput = null;
|
||||
@@ -221,7 +313,14 @@
|
||||
openPicker(input);
|
||||
return;
|
||||
}
|
||||
if (panel && !panel.hidden && !panel.contains(event.target)) closePicker();
|
||||
if (
|
||||
panel &&
|
||||
!panel.hidden &&
|
||||
!panel.contains(event.target) &&
|
||||
!(event.target.closest && event.target.closest(".ds-listbox"))
|
||||
) {
|
||||
closePicker();
|
||||
}
|
||||
}
|
||||
|
||||
function onKey(event) {
|
||||
|
||||
Reference in New Issue
Block a user