Skip to main content

max / makenotwork

3.5 KB · 80 lines History Blame Raw
1 (function() {
2 'use strict';
3
4 // Handle browser back/forward: reload the step content via HTMX
5 window.addEventListener('popstate', function() {
6 var stepArea = document.getElementById('wizard-step');
7 if (!stepArea) return;
8 // Let HTMX handle the URL change by fetching the current URL's step content
9 var path = window.location.pathname;
10 if (path.indexOf('/step/') !== -1) {
11 htmx.ajax('GET', path, { target: '#wizard-step', swap: 'innerHTML' });
12 }
13 });
14
15 // After HTMX swaps, re-attach the OOB sidebar update
16 document.addEventListener('htmx:afterSwap', function(event) {
17 // The step_nav.html is included at the top of each partial,
18 // so the sidebar nav gets updated via include (not OOB).
19 // We need to move the nav from inside #wizard-step to the sidebar.
20 var wizardStep = document.getElementById('wizard-step');
21 if (!wizardStep) return;
22
23 var inlineNav = wizardStep.querySelector('#wizard-nav');
24 if (!inlineNav) return;
25
26 var sidebar = document.querySelector('.wizard-sidebar');
27 if (!sidebar) return;
28
29 var existingNav = sidebar.querySelector('#wizard-nav');
30 if (existingNav) {
31 existingNav.replaceWith(inlineNav);
32 } else {
33 sidebar.appendChild(inlineNav);
34 }
35 });
36 // Custom HTML5 validation messages for wizard forms.
37 // Replaces browser defaults ("Please fill out this field") with
38 // field-specific messages using data-* attributes or fallback rules.
39 function applyCustomValidation(root) {
40 var inputs = root.querySelectorAll('input[required], input[minlength], input[pattern], textarea[minlength]');
41 inputs.forEach(function(input) {
42 input.addEventListener('invalid', function(e) {
43 var v = e.target.validity;
44 var label = (e.target.closest('.form-group')?.querySelector('label')?.textContent || '').trim();
45 var custom = e.target.dataset.validationMessage;
46
47 if (custom) {
48 e.target.setCustomValidity(custom);
49 } else if (v.valueMissing) {
50 e.target.setCustomValidity(label ? label + ' is required' : 'This field is required');
51 } else if (v.tooShort) {
52 var min = e.target.getAttribute('minlength');
53 e.target.setCustomValidity('Must be at least ' + min + ' characters');
54 } else if (v.tooLong) {
55 var max = e.target.getAttribute('maxlength');
56 e.target.setCustomValidity('Must be ' + max + ' characters or fewer');
57 } else if (v.patternMismatch) {
58 e.target.setCustomValidity(e.target.title || 'Please use the expected format');
59 } else if (v.typeMismatch) {
60 e.target.setCustomValidity('Please enter a valid ' + (e.target.type || 'value'));
61 } else {
62 e.target.setCustomValidity('');
63 }
64 });
65 // Clear custom message on input so re-validation works
66 input.addEventListener('input', function() {
67 this.setCustomValidity('');
68 });
69 });
70 }
71
72 // Apply to initial page content
73 applyCustomValidation(document.body);
74
75 // Re-apply after HTMX swaps new wizard steps in
76 document.addEventListener('htmx:afterSwap', function(event) {
77 applyCustomValidation(event.detail.target);
78 });
79 })();
80