From a044bfd6725e5f580d3119a1a3e6de994c9b37fe Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 6 Mar 2025 15:48:51 +0100 Subject: [PATCH 1/3] chore: refactor search UX cf #419 --- umap/static/umap/img/16.svg | 2 +- umap/static/umap/img/24.svg | 8 ++--- umap/static/umap/img/source/16.svg | 2 +- umap/static/umap/img/source/24.svg | 10 +++--- umap/static/umap/js/umap.controls.js | 53 +++++++++++----------------- umap/static/umap/map.css | 17 +++++++-- 6 files changed, 45 insertions(+), 47 deletions(-) diff --git a/umap/static/umap/img/16.svg b/umap/static/umap/img/16.svg index 3c1eb12c..6950d1f7 100644 --- a/umap/static/umap/img/16.svg +++ b/umap/static/umap/img/16.svg @@ -1 +1 @@ -image/svg+xml   +image/svg+xml   diff --git a/umap/static/umap/img/24.svg b/umap/static/umap/img/24.svg index a1f6f6fd..8ae40765 100644 --- a/umap/static/umap/img/24.svg +++ b/umap/static/umap/img/24.svg @@ -13,10 +13,10 @@ 1 - - + + - + @@ -47,7 +47,7 @@ - + diff --git a/umap/static/umap/img/source/16.svg b/umap/static/umap/img/source/16.svg index fb7d31ef..aad5d758 100644 --- a/umap/static/umap/img/source/16.svg +++ b/umap/static/umap/img/source/16.svg @@ -1,4 +1,4 @@ -image/svg+xml   +image/svg+xml   diff --git a/umap/static/umap/img/source/24.svg b/umap/static/umap/img/source/24.svg index c6eaab05..72eddd1b 100644 --- a/umap/static/umap/img/source/24.svg +++ b/umap/static/umap/img/source/24.svg @@ -2,7 +2,7 @@ - + @@ -26,10 +26,10 @@ 1 - - + + - + @@ -60,7 +60,7 @@ - + diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js index 287ca885..438f2235 100644 --- a/umap/static/umap/js/umap.controls.js +++ b/umap/static/umap/js/umap.controls.js @@ -430,44 +430,31 @@ U.Search = L.PhotonSearch.extend({ }, formatResult: function (feature, el) { - const tools = L.DomUtil.create('span', 'search-result-tools', el) - const zoom = L.DomUtil.createButtonIcon( - tools, - 'icon-zoom', - L._('Zoom to this place') - ) - const edit = L.DomUtil.createButtonIcon( - tools, - 'icon-edit', - L._('Save this location as new feature') - ) - // We need to use "mousedown" because Leaflet.Photon listen to mousedown - // on el. - L.DomEvent.on(zoom, 'mousedown', (e) => { - L.DomEvent.stop(e) - this.zoomToFeature(feature) - }) - L.DomEvent.on(edit, 'mousedown', (e) => { - L.DomEvent.stop(e) + const [tools, { button }] = U.Utils.loadTemplateWithRefs(` + + `) + button.addEventListener('mousedown', (event) => { + event.stopPropagation() const datalayer = this.map._umap.defaultEditDataLayer() - const layer = datalayer.makeFeature(feature) - layer.isDirty = true - layer.edit() + const marker = datalayer.makeFeature(feature) + marker.isDirty = true + marker.edit() + this.map._umap.panel.close() }) + el.appendChild(tools) this._formatResult(feature, el) }, - zoomToFeature: function (feature) { - const zoom = Math.max(this.map.getZoom(), 16) // Never unzoom. - this.map.setView( - [feature.geometry.coordinates[1], feature.geometry.coordinates[0]], - zoom - ) - }, - - onSelected: function (feature) { - this.zoomToFeature(feature) - this.map.panel.close() + setChoice: function (choice) { + choice = choice || this.RESULTS[this.CURRENT] + if (choice) { + const feature = choice.feature + const zoom = Math.max(this.map.getZoom(), 14) // Never unzoom. + this.map.setView( + [feature.geometry.coordinates[1], feature.geometry.coordinates[0]], + zoom + ) + } }, }) diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css index fd61b9b6..84d2d9e9 100644 --- a/umap/static/umap/map.css +++ b/umap/static/umap/map.css @@ -263,18 +263,26 @@ ul.photon-autocomplete { .photon-autocomplete li { min-height: 40px; line-height: 1em; - padding: 5px 10px; + padding-inline-start: 10px; overflow: hidden; white-space: nowrap; font-size: 1em; - border-inline-start: 4px solid #efefef; + border-inline-start: 4px solid var(--color-lightGray); } .photon-autocomplete li strong { display: block; + margin-top: 5px; +} +.photon-autocomplete button { + margin: 0; +} +.photon-autocomplete button:hover { + background-color: var(--color-brightCyan); } .photon-autocomplete li.on { - border-inline-start: 4px solid #2980b9; + border-inline-start: 4px solid var(--color-brightCyan); cursor: pointer; + background-color: var(--color-lightCyan); } .photon-autocomplete li.photon-no-result { text-align: center; @@ -292,6 +300,9 @@ ul.photon-autocomplete { } .search-result-tools { float: inline-end; + display: none; +} +.photon-autocomplete li.on .search-result-tools { display: block; } From ae5bc9746cd446dc732993ab0a43a20989b3eaca Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 6 Mar 2025 17:03:13 +0100 Subject: [PATCH 2/3] feat: allow to import full geometries from search results Co-authored-by: David Larlet --- umap/static/umap/js/modules/data/layer.js | 2 ++ umap/static/umap/js/modules/global.js | 6 ++++- umap/static/umap/js/umap.controls.js | 31 ++++++++++++++++++++--- umap/static/umap/map.css | 2 +- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/umap/static/umap/js/modules/data/layer.js b/umap/static/umap/js/modules/data/layer.js index 281c6e02..a7fb4603 100644 --- a/umap/static/umap/js/modules/data/layer.js +++ b/umap/static/umap/js/modules/data/layer.js @@ -332,6 +332,7 @@ export class DataLayer extends ServerStored { .parse(raw, this.options.remoteData.format) .then((geojson) => this.fromGeoJSON(geojson)) .catch((error) => { + console.debug(error) Alert.error( translate('Cannot parse remote data for layer "{layer}" with url "{url}"', { layer: this.getName(), @@ -532,6 +533,7 @@ export class DataLayer extends ServerStored { return data }) .catch((error) => { + console.debug(error) Alert.error(translate('Import failed: invalid data')) }) } diff --git a/umap/static/umap/js/modules/global.js b/umap/static/umap/js/modules/global.js index d6502b2f..4ef04502 100644 --- a/umap/static/umap/js/modules/global.js +++ b/umap/static/umap/js/modules/global.js @@ -1,5 +1,9 @@ import { uMapAlert as Alert } from '../components/alerts/alert.js' -import { AjaxAutocomplete, AjaxAutocompleteMultiple, AutocompleteDatalist } from './autocomplete.js' +import { + AjaxAutocomplete, + AjaxAutocompleteMultiple, + AutocompleteDatalist, +} from './autocomplete.js' import Help from './help.js' import { ServerRequest } from './request.js' import { SCHEMA } from './schema.js' diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js index 438f2235..7a35cde6 100644 --- a/umap/static/umap/js/umap.controls.js +++ b/umap/static/umap/js/umap.controls.js @@ -430,10 +430,14 @@ U.Search = L.PhotonSearch.extend({ }, formatResult: function (feature, el) { - const [tools, { button }] = U.Utils.loadTemplateWithRefs(` - + const [tools, { point, geom }] = U.Utils.loadTemplateWithRefs(` + + + + `) - button.addEventListener('mousedown', (event) => { + geom.hidden = !['R', 'W'].includes(feature.properties.osm_type) + point.addEventListener('mousedown', (event) => { event.stopPropagation() const datalayer = this.map._umap.defaultEditDataLayer() const marker = datalayer.makeFeature(feature) @@ -441,6 +445,27 @@ U.Search = L.PhotonSearch.extend({ marker.edit() this.map._umap.panel.close() }) + geom.addEventListener('mousedown', async (event) => { + event.stopPropagation() + const osm_id = feature.properties.osm_id + const types = { + R: 'relation', + W: 'way', + N: 'node', + } + const osm_type = types[feature.properties.osm_type] + if (!osm_type || !osm_id) return + const url = `https://www.openstreetmap.org/api/0.6/${osm_type}/${osm_id}/full` + const response = await this.map._umap.request.get(url) + if (response?.ok) { + const importer = this.map._umap.importer + importer.build() + importer.format = 'osm' + importer.raw = await response.text() + importer.submit() + this.map._umap.panel.close() + } + }) el.appendChild(tools) this._formatResult(feature, el) }, diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css index 84d2d9e9..c78baa4d 100644 --- a/umap/static/umap/map.css +++ b/umap/static/umap/map.css @@ -303,7 +303,7 @@ ul.photon-autocomplete { display: none; } .photon-autocomplete li.on .search-result-tools { - display: block; + display: flex; } From 782386bad45a870dc1cee9593f578e1f9116d763 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 6 Mar 2025 17:25:40 +0100 Subject: [PATCH 3/3] feat: display a target on hover on search results Co-authored-by: David Larlet --- umap/static/umap/img/target.svg | 1 + umap/static/umap/js/umap.controls.js | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 umap/static/umap/img/target.svg diff --git a/umap/static/umap/img/target.svg b/umap/static/umap/img/target.svg new file mode 100644 index 00000000..d7b58538 --- /dev/null +++ b/umap/static/umap/img/target.svg @@ -0,0 +1 @@ + diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js index 7a35cde6..3aee1cf2 100644 --- a/umap/static/umap/js/umap.controls.js +++ b/umap/static/umap/js/umap.controls.js @@ -468,6 +468,20 @@ U.Search = L.PhotonSearch.extend({ }) el.appendChild(tools) this._formatResult(feature, el) + const path = U.SCHEMA.iconUrl.default.replace('marker.svg', 'target.svg') + const icon = L.icon({ + iconUrl: path, + iconSize: [24, 24], + iconAnchor: [12, 12], + }) + const coords = feature.geometry.coordinates + const target = L.marker([coords[1], coords[0]], { icon }) + el.addEventListener('mouseover', (event) => { + target.addTo(this.map) + }) + el.addEventListener('mouseout', (event) => { + target.removeFrom(this.map) + }) }, setChoice: function (choice) {