+
Click the map to place a note.
+
+
+ `;
+ this._button = this._container.querySelector(".notes-ctrl__button");
+ this._panel = this._container.querySelector(".notes-panel");
+ this._hint = this._container.querySelector(".notes-panel__hint");
+ this._form = this._container.querySelector(".notes-panel__form");
+ this._text = this._container.querySelector(".notes-panel__text");
+ this._target = this._container.querySelector(".notes-panel__target");
+ this._newName = this._container.querySelector(".notes-panel__new-name");
+
+ this._button.addEventListener("click", () => this._setActive(!this._active));
+ this._target.addEventListener("change", () => {
+ this._newName.hidden = this._target.value !== NEW_LAYER_VALUE;
+ if (!this._newName.hidden) this._newName.focus();
+ });
+ this._container.querySelector(".notes-panel__save").addEventListener("click", () => this._save());
+ this._container.querySelector(".notes-panel__cancel").addEventListener("click", () => this._resetForm());
+
+ this._onClick = (event) => {
+ if (!this._active) return;
+ this._pendingLngLat = event.lngLat;
+ this._openForm();
+ };
+ map.on("click", this._onClick);
+
+ return this._container;
+ }
+
+ onRemove() {
+ this._map.off("click", this._onClick);
+ this._container.remove();
+ }
+
+ getDefaultPosition() {
+ return "top-left";
+ }
+
+ _setActive(active) {
+ this._active = active;
+ this._button.classList.toggle("active", active);
+ this._panel.hidden = !active;
+ this._map.getCanvas().style.cursor = active ? "crosshair" : "";
+ if (!active) this._resetForm();
+ }
+
+ async _openForm() {
+ this._hint.hidden = true;
+ this._form.hidden = false;
+ this._text.value = "";
+ this._text.focus();
+ await this._populateTargets();
+ }
+
+ _resetForm() {
+ this._pendingLngLat = null;
+ this._hint.hidden = false;
+ this._form.hidden = true;
+ this._newName.hidden = true;
+ this._newName.value = "";
+ }
+
+ async _populateTargets() {
+ const previousValue = this._target.value;
+ let files = [];
+ try {
+ const tree = await (await fetch("/api/gpx/tree")).json();
+ files = geojsonFiles(tree);
+ } catch (err) {
+ // Tree fetch failed — target select just falls back to "new layer"
+ // only, still usable.
+ }
+
+ this._target.innerHTML =
+ files.map((file) => `