feat: standardize admin form limits and guidance

This commit is contained in:
Tống Thành Đạt
2026-04-10 15:55:15 +07:00
parent 7ce5921fe0
commit 51c6303437
34 changed files with 1692 additions and 361 deletions

View File

@@ -446,92 +446,11 @@
toast.addEventListener("hidden.bs.toast", () => toast.remove());
}
function ensureCharacterHint(input) {
if (!input || (!input.dataset.maxlength && !input.dataset.maxwords)) {
return null;
}
if (!input.id) {
input.id = `homeField_${Math.random().toString(36).slice(2, 10)}`;
}
const field = input.closest(".col-md-12, .col-md-9, .col-md-6, .col-md-4, .col-md-3, .col-lg-12, .col-lg-8, .col-lg-6, .col-lg-4, .col-12") || input.parentElement;
const anchor = input.closest(".input-group") || input;
const parent = anchor?.parentElement || field;
if (!field || !anchor || !parent) {
return null;
}
let hint = field.querySelector(`[data-counter-for="${input.id}"]`);
if (!hint) {
hint = document.createElement("small");
hint.className = "form-text home-limit-counter text-secondary";
hint.dataset.counterFor = input.id;
}
if (hint.previousElementSibling !== anchor) {
parent.insertBefore(hint, anchor.nextSibling);
}
return hint;
}
function updateCharacterHint(input) {
const hint = ensureCharacterHint(input);
if (!hint) {
return;
}
const hasWordLimit = Boolean(input.dataset.maxwords);
const hasCharLimit = Boolean(input.dataset.maxlength);
if (hasWordLimit) {
const maxWords = Number(input.dataset.maxwords);
const normalized = (input.value || "").replace(/\s+/g, " ").trim();
const words = normalized ? normalized.split(" ") : [];
if (Number.isFinite(maxWords) && maxWords > 0 && words.length > maxWords) {
input.value = words.slice(0, maxWords).join(" ");
} else if (normalized !== input.value) {
input.value = normalized;
}
}
if (hasCharLimit) {
const max = Number(input.dataset.maxlength);
if (Number.isFinite(max) && max > 0 && (input.value || "").length > max) {
input.value = (input.value || "").slice(0, max);
}
}
if (hasWordLimit) {
const maxWords = Number(input.dataset.maxwords);
const currentWords = input.value ? input.value.split(" ").filter(Boolean).length : 0;
const currentLength = (input.value || "").length;
const maxLength = Number(input.dataset.maxlength);
hint.textContent = hasCharLimit
? `${currentWords}/${maxWords} words, ${currentLength}/${maxLength} characters`
: `${currentWords}/${maxWords} words`;
hint.classList.toggle("text-danger", currentWords >= maxWords || (hasCharLimit && currentLength >= maxLength));
return;
}
const max = Number(input.dataset.maxlength);
const length = (input.value || "").length;
hint.textContent = `${length}/${max} characters`;
hint.classList.toggle("text-danger", length >= max);
}
function initHomeCharacterCounters(scope = document) {
scope.querySelectorAll("input[data-maxlength], textarea[data-maxlength], input[data-maxwords], textarea[data-maxwords]").forEach((input) => {
updateCharacterHint(input);
scope.querySelectorAll(".home-limit-counter").forEach((node) => node.remove());
if (input.dataset.counterBound === "true") {
return;
}
input.dataset.counterBound = "true";
input.addEventListener("input", () => updateCharacterHint(input));
});
if (window.AdminFormHelpers) {
window.AdminFormHelpers.refresh(scope);
}
}
</script>