diff --git a/web/modules/custom/riverside_pt/js/components/rpt-booking.js b/web/modules/custom/riverside_pt/js/components/rpt-booking.js index 3b28a99..7f0bc0e 100644 --- a/web/modules/custom/riverside_pt/js/components/rpt-booking.js +++ b/web/modules/custom/riverside_pt/js/components/rpt-booking.js @@ -15,6 +15,23 @@ const CHECK = html` diff --git a/web/modules/custom/riverside_pt/js/phone-format.js b/web/modules/custom/riverside_pt/js/phone-format.js new file mode 100644 index 0000000..64027e0 --- /dev/null +++ b/web/modules/custom/riverside_pt/js/phone-format.js @@ -0,0 +1,66 @@ +(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(); + } +})(); diff --git a/web/modules/custom/riverside_pt/riverside_pt.libraries.yml b/web/modules/custom/riverside_pt/riverside_pt.libraries.yml index 7437410..b08f972 100644 --- a/web/modules/custom/riverside_pt/riverside_pt.libraries.yml +++ b/web/modules/custom/riverside_pt/riverside_pt.libraries.yml @@ -4,6 +4,7 @@ app: css/app.css: {} js: js/nav.js: {} + js/phone-format.js: {} js/components/rpt-toggle.js: { attributes: { type: module } } js/components/rpt-carousel.js: { attributes: { type: module } } js/components/rpt-testimonials.js: { attributes: { type: module } } diff --git a/web/modules/custom/riverside_pt/src/Form/BookingForm.php b/web/modules/custom/riverside_pt/src/Form/BookingForm.php index 08a8348..86b75e1 100644 --- a/web/modules/custom/riverside_pt/src/Form/BookingForm.php +++ b/web/modules/custom/riverside_pt/src/Form/BookingForm.php @@ -72,21 +72,25 @@ class BookingForm extends FormBase { ]; $form['last_name'] = [ - '#type' => 'textfield', - '#title' => $this->t('Last name'), - '#required' => TRUE, + '#type' => 'textfield', + '#title' => $this->t('Last name'), + '#required' => TRUE, + '#default_value' => $slot['last_name'] ?? '', ]; $form['phone'] = [ - '#type' => 'tel', - '#title' => $this->t('Phone number'), - '#required' => TRUE, + '#type' => 'tel', + '#title' => $this->t('Phone number'), + '#required' => TRUE, + '#default_value' => $slot['phone'] ?? '', + '#attributes' => ['class' => ['rpt-phone']], ]; $form['comments'] = [ - '#type' => 'textarea', - '#title' => $this->t('Comments'), - '#rows' => 4, + '#type' => 'textarea', + '#title' => $this->t('Comments'), + '#rows' => 4, + '#default_value' => $slot['comments'] ?? '', ]; $form['actions'] = ['#type' => 'actions'];