From 6c02f9b4c15a73d342099856a5a6e0d3d9bb1714 Mon Sep 17 00:00:00 2001 From: myrmidex Date: Fri, 5 Jun 2026 21:56:56 +0000 Subject: [PATCH] 4 - Replace prompt() with anchored Leaflet popup, themed to aqua --- assets/controllers/map_controller.js | 94 ++++++++++++++++++---------- assets/styles/app.css | 49 +++++++++++++++ templates/base.html.twig | 2 +- templates/index/page.html.twig | 26 +------- 4 files changed, 113 insertions(+), 58 deletions(-) diff --git a/assets/controllers/map_controller.js b/assets/controllers/map_controller.js index 294caf1..ca36d82 100644 --- a/assets/controllers/map_controller.js +++ b/assets/controllers/map_controller.js @@ -17,22 +17,11 @@ export default class extends Controller { maxZoom: 19, }).addTo(this.map); - this.pendingLatlng = null; - - this.dialog = document.getElementById('add-subject-dialog'); - this.nameInput = document.getElementById('subject-name'); - this.submitBtn = document.getElementById('subject-submit'); - this.cancelBtn = document.getElementById('subject-cancel'); + this.popup = null; this.loadSubjects(); - this.map.on('click', (e) => this.openModal(e.latlng)); - - this.submitBtn.addEventListener('click', () => this.handleSubmit()); - this.cancelBtn.addEventListener('click', () => this.closeModal()); - - // Esc key / backdrop close — clear pending state - this.dialog.addEventListener('close', () => this.resetModal()); + this.map.on('click', (e) => this.openPopup(e.latlng)); } loadSubjects() { @@ -47,28 +36,69 @@ export default class extends Controller { }); } - openModal(latlng) { - this.pendingLatlng = latlng; - this.nameInput.value = ''; - this.dialog.showModal(); - this.nameInput.focus(); + openPopup(latlng) { + if (this.popup) { + this.popup.remove(); + } + + const content = this.buildPopupContent(latlng); + + this.popup = L.popup({ closeOnClick: false, autoClose: false, minWidth: 280 }) + .setLatLng(latlng) + .setContent(content) + .openOn(this.map); + + // Autofocus after Leaflet inserts the DOM + this.popup.once('add', () => { + const input = this.popup.getElement()?.querySelector('.add-place-input'); + if (input) input.focus(); + }); } - closeModal() { - this.dialog.close(); + buildPopupContent(latlng) { + const container = document.createElement('div'); + container.className = 'add-place-popup'; + container.innerHTML = ` +

Add a place

+
+ Name + +
+
+ + +
+ `; + + const input = container.querySelector('.add-place-input'); + const submitBtn = container.querySelector('.add-place-submit'); + const cancelBtn = container.querySelector('.add-place-cancel'); + + const submit = () => { + const name = input.value.trim(); + if (!name) { + input.focus(); + return; + } + this.createSubject(name, latlng); + }; + + submitBtn.addEventListener('click', submit); + cancelBtn.addEventListener('click', () => this.popup.remove()); + input.addEventListener('keydown', (e) => { + if (e.key === 'Enter') submit(); + if (e.key === 'Escape') this.popup.remove(); + }); + + return container; } - resetModal() { - this.pendingLatlng = null; - this.nameInput.value = ''; - } - - handleSubmit() { - const name = this.nameInput.value.trim(); - if (!name || !this.pendingLatlng) return; - - const latlng = this.pendingLatlng; - + createSubject(name, latlng) { fetch('/api/subjects', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -81,7 +111,7 @@ export default class extends Controller { .then(r => r.json()) .then(() => { this.addMarker(latlng.lat, latlng.lng, name); - this.closeModal(); + this.popup.remove(); }); } diff --git a/assets/styles/app.css b/assets/styles/app.css index 2722a1d..a91872c 100644 --- a/assets/styles/app.css +++ b/assets/styles/app.css @@ -5,3 +5,52 @@ html, body { height: 100%; margin: 0; } .map-container { height: calc(100vh - 4rem); width: 100%; } + +/* Leaflet popup chrome — match aqua theme */ +.leaflet-popup-content-wrapper { + background: oklch(37% 0.146 265.522) !important; + color: oklch(90% 0.058 230.902) !important; + border-radius: 0.75rem !important; + box-shadow: 0 4px 24px oklch(0% 0 0 / 0.4) !important; + padding: 0 !important; + border: none !important; +} +.leaflet-popup-content { + margin: 0 !important; + width: auto !important; +} +.leaflet-popup-tip { + background: oklch(37% 0.146 265.522) !important; +} +.leaflet-popup-close-button { + color: oklch(90% 0.058 230.902) !important; + top: 0.5rem !important; + right: 0.5rem !important; + font-size: 1.1rem !important; +} +.leaflet-popup-close-button:hover { + color: oklch(85.661% 0.144 198.645) !important; + background: transparent !important; +} + + +/* Add-place popup layout */ +.add-place-popup { + padding: 0.25rem 1rem 1rem; + width: 300px; +} +.add-place-heading { + font-size: 1rem; + font-weight: 700; + margin-bottom: 0.75rem; + color: oklch(85.661% 0.144 198.645); +} +.add-place-popup .fieldset-legend { + padding-left: 2px !important; +} +.add-place-actions { + display: flex; + justify-content: flex-end; + gap: 0.5rem; + margin-top: 1rem; +} diff --git a/templates/base.html.twig b/templates/base.html.twig index c7bb70d..6db0265 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -22,7 +22,7 @@ {% block navbar %}