82 lines
3.5 KiB
JavaScript
82 lines
3.5 KiB
JavaScript
(() => {
|
|
"use strict";
|
|
|
|
const pad = (value) => String(value).padStart(2, "0");
|
|
|
|
const parseDisplayValue = (value, mode) => {
|
|
const pattern = mode === "datetime"
|
|
? /^(\d{2})\/(\d{2})\/(\d{4}) (\d{2}):(\d{2}):(\d{2})$/
|
|
: /^(\d{2})\/(\d{2})\/(\d{4})$/;
|
|
const match = value.trim().match(pattern);
|
|
if (!match) return "";
|
|
|
|
const [, day, month, year, hour = "00", minute = "00", second = "00"] = match;
|
|
const date = new Date(Number(year), Number(month) - 1, Number(day), Number(hour), Number(minute), Number(second));
|
|
if (date.getFullYear() !== Number(year) || date.getMonth() !== Number(month) - 1
|
|
|| date.getDate() !== Number(day) || date.getHours() !== Number(hour)
|
|
|| date.getMinutes() !== Number(minute) || date.getSeconds() !== Number(second)) return "";
|
|
|
|
const isoDate = `${year}-${month}-${day}`;
|
|
return mode === "datetime" ? `${isoDate}T${hour}:${minute}:${second}` : isoDate;
|
|
};
|
|
|
|
const formatPickerValue = (value, mode) => {
|
|
if (!value) return "";
|
|
const [datePart, timePart = "00:00:00"] = value.split("T");
|
|
const [year, month, day] = datePart.split("-");
|
|
if (!year || !month || !day) return "";
|
|
if (mode === "date") return `${day}/${month}/${year}`;
|
|
const [hour = "00", minute = "00", second = "00"] = timePart.split(":");
|
|
return `${day}/${month}/${year} ${pad(hour)}:${pad(minute)}:${pad(second)}`;
|
|
};
|
|
|
|
const initialise = (input) => {
|
|
if (input.dataset.datePickerReady === "true") return;
|
|
input.dataset.datePickerReady = "true";
|
|
|
|
const mode = input.dataset.dateMode === "date" ? "date" : "datetime";
|
|
const wrapper = document.createElement("span");
|
|
wrapper.className = "matrix-date-control";
|
|
input.parentNode.insertBefore(wrapper, input);
|
|
wrapper.appendChild(input);
|
|
|
|
const picker = document.createElement("input");
|
|
picker.type = mode === "datetime" ? "datetime-local" : "date";
|
|
picker.className = "matrix-date-control__picker";
|
|
picker.tabIndex = -1;
|
|
picker.setAttribute("aria-hidden", "true");
|
|
if (mode === "datetime") picker.step = "1";
|
|
|
|
const button = document.createElement("button");
|
|
button.type = "button";
|
|
button.className = "matrix-date-control__button";
|
|
button.setAttribute("aria-label", mode === "datetime" ? "Choose date and time" : "Choose date");
|
|
button.innerHTML = "📅";
|
|
|
|
wrapper.append(picker, button);
|
|
input.closest(".widget")?.classList.add("matrix-date-field");
|
|
|
|
const syncPicker = () => {
|
|
const parsed = parseDisplayValue(input.value, mode);
|
|
if (parsed) picker.value = parsed;
|
|
};
|
|
syncPicker();
|
|
|
|
picker.addEventListener("change", () => {
|
|
input.value = formatPickerValue(picker.value, mode);
|
|
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
input.focus();
|
|
});
|
|
input.addEventListener("change", syncPicker);
|
|
button.addEventListener("click", () => {
|
|
syncPicker();
|
|
if (typeof picker.showPicker === "function") picker.showPicker();
|
|
else picker.click();
|
|
});
|
|
};
|
|
|
|
const initialiseAll = (root = document) => root.querySelectorAll(".matrix-date-input").forEach(initialise);
|
|
document.addEventListener("DOMContentLoaded", () => initialiseAll());
|
|
window.CygnusDateTimeInputs = { initialise: initialiseAll };
|
|
})();
|