Files
cv-site/templates/partials/modals/contact-modal.html
T
juanatsap 3c49f8f7cf refactor: use idiomatic hyperscript selector syntax
Replace verbose document.getElementById() and document.querySelectorAll()
with cleaner hyperscript syntax:
- #id for ID selectors
- .class and the first .class for class selectors
- <selector/> query literals for complex selectors
- #{variable} for dynamic ID interpolation

Files changed:
- utils._hs: scrollHeight, details, footer buttons, scrollToSection
- zoom._hs: all zoom control element selectors (14 changes)
- pdf-modal._hs: modal selector
- keyboard._hs: dynamic toggle and modal selectors
- contact-modal.html: response div and modal close
- index.html: ninja-keys bar selector
2025-12-02 16:23:40 +00:00

163 lines
7.3 KiB
HTML

{{define "contact-modal"}}
<!-- Contact Form Modal - Native Dialog -->
<dialog id="contact-modal" class="info-modal no-print"
_="on click call closeOnBackdrop(me, event)
on toggle if me.open call resetContactForm()">
<div class="info-modal-content">
<button class="info-modal-close" commandfor="contact-modal" command="close" aria-label="{{.UI.ContactModal.Close}}">
<iconify-icon icon="mdi:close" width="24" height="24"></iconify-icon>
</button>
<div class="info-modal-header">
<h2>{{.UI.ContactModal.Title}}</h2>
<div class="info-modal-cv-title">
<iconify-icon icon="mdi:email-outline" width="32" height="32"></iconify-icon>
{{.UI.ContactModal.Subtitle}}
</div>
</div>
<div class="info-modal-body">
<p class="contact-modal-description">
{{.UI.ContactModal.Description}}
</p>
<form id="contact-form"
hx-post="/api/contact?lang={{.Lang}}"
hx-target="#contact-response"
hx-swap="innerHTML"
hx-indicator="#contact-spinner"
hx-headers='{"X-Requested-With": "XMLHttpRequest"}'
_="on htmx:afterRequest
-- Check if response contains success message (not validation error)
set responseDiv to #contact-response
if responseDiv is not null and responseDiv.querySelector('.contact-success') is not null
-- Hide all form fields and show only success message
set formFields to me.querySelectorAll('.form-group')
repeat for field in formFields
add .hidden to field
end
add .hidden to me.querySelector('.form-actions')
add .hidden to me.querySelector('.form-note')
-- Close modal after 3 seconds
wait 3s then call #contact-modal.close()
end">
<!-- Honeypot field - hidden, should be empty -->
<div style="position: absolute; left: -9999px;" aria-hidden="true">
<label for="contact-website">Website</label>
<input type="text"
name="website"
id="contact-website"
tabindex="-1"
autocomplete="off">
</div>
<!-- Timing field - set on page load, reset on modal open -->
<input type="hidden" name="form_loaded_at" id="contact-form-loaded-at"
_="on load set my.value to Date.now()">
<!-- Email (required) -->
<div class="form-group">
<label for="contact-email" class="form-label">
{{.UI.ContactModal.Form.Email}} <span class="required-indicator">*</span>
</label>
<input type="email"
id="contact-email"
name="email"
class="form-input"
required
autocomplete="email"
placeholder="{{.UI.ContactModal.Form.EmailPlaceholder}}"
aria-required="true">
</div>
<!-- Name (optional) -->
<div class="form-group">
<label for="contact-name" class="form-label">
{{.UI.ContactModal.Form.Name}}
</label>
<input type="text"
id="contact-name"
name="name"
class="form-input"
autocomplete="name"
placeholder="{{.UI.ContactModal.Form.NamePlaceholder}}">
</div>
<!-- Company (optional) -->
<div class="form-group">
<label for="contact-company" class="form-label">
{{.UI.ContactModal.Form.Company}}
</label>
<input type="text"
id="contact-company"
name="company"
class="form-input"
autocomplete="organization"
placeholder="{{.UI.ContactModal.Form.CompanyPlaceholder}}">
</div>
<!-- Subject (optional) -->
<div class="form-group">
<label for="contact-subject" class="form-label">
{{.UI.ContactModal.Form.Subject}}
</label>
<input type="text"
id="contact-subject"
name="subject"
class="form-input"
placeholder="{{.UI.ContactModal.Form.SubjectPlaceholder}}">
</div>
<!-- Message (required) -->
<div class="form-group">
<label for="contact-message" class="form-label">
{{.UI.ContactModal.Form.Message}} <span class="required-indicator">*</span>
</label>
<textarea id="contact-message"
name="message"
class="form-textarea"
required
rows="5"
placeholder="{{.UI.ContactModal.Form.MessagePlaceholder}}"
aria-required="true"></textarea>
</div>
<!-- Response area for success/error messages -->
<div id="contact-response" class="contact-response" role="status" aria-live="polite"></div>
<!-- Submit button with loading indicator -->
<div class="form-actions">
<button type="submit" class="contact-submit-btn">
<iconify-icon icon="mdi:send" width="20" height="20"></iconify-icon>
<span>{{.UI.ContactModal.Form.Submit}}</span>
<iconify-icon id="contact-spinner"
icon="mdi:loading"
class="htmx-indicator spinning"
width="20"
height="20"
aria-label="{{.UI.ContactModal.Form.Sending}}"></iconify-icon>
</button>
</div>
<p class="form-note">{{.UI.ContactModal.Form.Note}}</p>
</form>
</div>
</div>
</dialog>
<script>
function resetContactForm() {
const form = document.getElementById('contact-form');
if (form) {
form.reset();
form.querySelectorAll('.form-group, .form-actions, .form-note').forEach(el => el.classList.remove('hidden'));
const resp = document.getElementById('contact-response');
if (resp) resp.innerHTML = '';
}
const ts = document.getElementById('contact-form-loaded-at');
if (ts) ts.value = Date.now();
}
</script>
{{end}}