- New pure-vanilla progressive formatter that turns typed digits into
(123) 456-7890 (or 1 (800) 555-1212 for +1 numbers) on the fly.
- Works for free-form input: typing, pasting, editing anywhere, backspacing,
extra chars, etc. — no hard mask or input restrictions.
- Applied to:
- The Preact <rpt-booking> widget phone field (home page quick booking + details).
- The final Drupal confirmation form at /schedule/book (via lightweight
enhancer + .rpt-phone class).
- Prefills last_name / phone / comments on the confirmation form when the
widget was used to pick the slot (so collected phone isn't lost).
- No new dependencies whatsoever:
* Zero npm / package.json / composer changes.
* Zero additional CDN / external scripts (uses native String.replace,
regex, and input event + selection APIs only).
* The new js/phone-format.js is simply attached via the pre-existing
'app' library (already loaded on all riverside_pt.* routes).
* Formatter logic duplicated in the ESM component (tiny pure function).
- The two other modified files (calendar.css, calendar.js) were left
uncommitted as they are unrelated to this feature.
66 lines
2 KiB
JavaScript
66 lines
2 KiB
JavaScript
(function () {
|
|
function formatPhone(raw) {
|
|
let d = String(raw || "").replace(/\D/g, "");
|
|
if (d.length === 11 && d[0] === "1") {
|
|
// NANP with leading 1: show "1 (xxx) xxx-xxxx"
|
|
const rest = d.slice(1);
|
|
return "1 (" + rest.slice(0, 3) + ") " + rest.slice(3, 6) + "-" + rest.slice(6);
|
|
}
|
|
d = d.slice(0, 10);
|
|
if (d.length === 0) return "";
|
|
if (d.length <= 3) return d;
|
|
if (d.length <= 6) return "(" + d.slice(0, 3) + ") " + d.slice(3);
|
|
return "(" + d.slice(0, 3) + ") " + d.slice(3, 6) + "-" + d.slice(6);
|
|
}
|
|
|
|
function enhancePhoneInput(input) {
|
|
if (input.dataset.phoneEnhanced) return;
|
|
input.dataset.phoneEnhanced = "true";
|
|
|
|
input.addEventListener("input", function () {
|
|
const oldValue = input.value;
|
|
const oldStart = input.selectionStart || 0;
|
|
|
|
const formatted = formatPhone(input.value);
|
|
if (oldValue !== formatted) {
|
|
input.value = formatted;
|
|
|
|
// Try to keep cursor relative to the digit sequence the user was editing.
|
|
const oldDigitsBefore = (oldValue.slice(0, oldStart).match(/\d/g) || []).length;
|
|
|
|
let newPos = 0;
|
|
let digitsSeen = 0;
|
|
for (; newPos < formatted.length; newPos++) {
|
|
if (/\d/.test(formatted[newPos])) {
|
|
digitsSeen++;
|
|
}
|
|
if (digitsSeen >= oldDigitsBefore) {
|
|
newPos++;
|
|
break;
|
|
}
|
|
}
|
|
if (newPos > formatted.length) newPos = formatted.length;
|
|
|
|
try {
|
|
input.setSelectionRange(newPos, newPos);
|
|
} catch (_) {}
|
|
}
|
|
});
|
|
|
|
// Format any server-provided default value on load (e.g. prefilled from tempstore)
|
|
if (input.value) {
|
|
const f = formatPhone(input.value);
|
|
if (input.value !== f) input.value = f;
|
|
}
|
|
}
|
|
|
|
function scan() {
|
|
document.querySelectorAll("input.rpt-phone").forEach(enhancePhoneInput);
|
|
}
|
|
|
|
if (document.readyState === "loading") {
|
|
document.addEventListener("DOMContentLoaded", scan);
|
|
} else {
|
|
scan();
|
|
}
|
|
})();
|