mirror of
https://github.com/umap-project/umap.git
synced 2025-04-28 19:42:36 +02:00
feat: add a global toolbox in browser (#2160)
fix #2097 cf #1500  This is an alternative proposal for #2097: - adding the button near the "Filter" toggle was a bit ugly, CSS/HTML talking, because this "Filters" box is a details/summary - I thought that other needs could live in the same place: a link to download the filtered/visible data, and a button to show the map data extent The "toggle all" button works this way: - if one or more layers are shown, it will hide those layers - otherwise it will show all layers I've preferred not to change the button icon according to map state, because I didn't find a reliable way to do so, thus I'm afraid to have inconsistency, but I can't try harder if needed. Related: I'm thinking of adding a "Download" button for each layers, as it's a request that comes quite often. @Aurelie-Jallut could you have a look ? :)
This commit is contained in:
commit
ab703e081b
5 changed files with 124 additions and 3 deletions
|
@ -1,6 +1,9 @@
|
||||||
import { DomEvent, DomUtil, stamp } from '../../vendors/leaflet/leaflet-src.esm.js'
|
import { DomEvent, DomUtil, stamp } from '../../vendors/leaflet/leaflet-src.esm.js'
|
||||||
import { translate } from './i18n.js'
|
import { translate } from './i18n.js'
|
||||||
import * as Icon from './rendering/icon.js'
|
import * as Icon from './rendering/icon.js'
|
||||||
|
import * as Utils from './utils.js'
|
||||||
|
import { EXPORT_FORMATS } from './formatter.js'
|
||||||
|
import ContextMenu from './ui/contextmenu.js'
|
||||||
|
|
||||||
export default class Browser {
|
export default class Browser {
|
||||||
constructor(map) {
|
constructor(map) {
|
||||||
|
@ -165,6 +168,7 @@ export default class Browser {
|
||||||
})
|
})
|
||||||
this.filtersTitle = container.querySelector('summary')
|
this.filtersTitle = container.querySelector('summary')
|
||||||
this.toggleBadge()
|
this.toggleBadge()
|
||||||
|
this.addMainToolbox(container)
|
||||||
this.dataContainer = DomUtil.create('div', '', container)
|
this.dataContainer = DomUtil.create('div', '', container)
|
||||||
|
|
||||||
let fields = [
|
let fields = [
|
||||||
|
@ -216,6 +220,48 @@ export default class Browser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addMainToolbox(container) {
|
||||||
|
const [toolbox, { toggle, fitBounds, download }] = Utils.loadTemplateWithRefs(`
|
||||||
|
<div class="main-toolbox">
|
||||||
|
<i class="icon icon-16 icon-eye" title="${translate('show/hide all layers')}" data-ref="toggle"></i>
|
||||||
|
<i class="icon icon-16 icon-zoom" title="${translate('zoom to data extent')}" data-ref="fitBounds"></i>
|
||||||
|
<i class="icon icon-16 icon-download" title="${translate('download visible data')}" data-ref="download"></i>
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
container.appendChild(toolbox)
|
||||||
|
toggle.addEventListener('click', () => this.toggleLayers())
|
||||||
|
fitBounds.addEventListener('click', () => this.map.fitDataBounds())
|
||||||
|
download.addEventListener('click', () => this.downloadVisible(download))
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadVisible(element) {
|
||||||
|
const menu = new ContextMenu({ fixed: true })
|
||||||
|
const items = []
|
||||||
|
for (const format of Object.keys(EXPORT_FORMATS)) {
|
||||||
|
items.push({
|
||||||
|
label: format,
|
||||||
|
action: () => this.map.share.download(format),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
menu.openBelow(element, items)
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleLayers() {
|
||||||
|
// If at least one layer is shown, hide it
|
||||||
|
// otherwise show all
|
||||||
|
let allHidden = true
|
||||||
|
this.map.eachBrowsableDataLayer((datalayer) => {
|
||||||
|
if (datalayer.isVisible()) allHidden = false
|
||||||
|
})
|
||||||
|
this.map.eachBrowsableDataLayer((datalayer) => {
|
||||||
|
if (allHidden) {
|
||||||
|
datalayer.show()
|
||||||
|
} else {
|
||||||
|
if (datalayer.isVisible()) datalayer.hide()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
static backButton(map) {
|
static backButton(map) {
|
||||||
const button = DomUtil.createButtonIcon(
|
const button = DomUtil.createButtonIcon(
|
||||||
DomUtil.create('li', '', undefined),
|
DomUtil.create('li', '', undefined),
|
||||||
|
|
|
@ -21,6 +21,11 @@ export default class ContextMenu extends Positioned {
|
||||||
this.openAt([left, top], items)
|
this.openAt([left, top], items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openBelow(element, items) {
|
||||||
|
const coords = this.getPosition(element)
|
||||||
|
this.openAt([coords.left, coords.bottom], items)
|
||||||
|
}
|
||||||
|
|
||||||
openAt([left, top], items) {
|
openAt([left, top], items) {
|
||||||
this.container.innerHTML = ''
|
this.container.innerHTML = ''
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
|
|
|
@ -657,9 +657,7 @@ const ControlsMixin = {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
const x = button.offsetLeft
|
menu.openBelow(button, actions)
|
||||||
const y = button.offsetTop + button.offsetHeight
|
|
||||||
menu.openAt([x, y], actions)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.help.getStartedLink(rightContainer)
|
this.help.getStartedLink(rightContainer)
|
||||||
|
|
|
@ -913,6 +913,16 @@ a.umap-control-caption,
|
||||||
.umap-caption .umap-map-author {
|
.umap-caption .umap-map-author {
|
||||||
padding-inline-start: 31px;
|
padding-inline-start: 31px;
|
||||||
}
|
}
|
||||||
|
.umap-browser .main-toolbox {
|
||||||
|
padding-left: 4px; /* Align with toolbox below */
|
||||||
|
border-top: 1px solid var(--color-mediumGray);
|
||||||
|
margin-top: var(--box-margin);
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
}
|
||||||
|
.umap-browser .main-toolbox i {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ********************************* */
|
/* ********************************* */
|
||||||
|
|
|
@ -414,3 +414,65 @@ def test_should_have_edit_buttons_in_edit_mode(live_server, openmap, page, boots
|
||||||
expect(delete_layer).to_be_visible()
|
expect(delete_layer).to_be_visible()
|
||||||
expect(edit_feature).to_have_count(3)
|
expect(edit_feature).to_have_count(3)
|
||||||
expect(delete_feature).to_have_count(3)
|
expect(delete_feature).to_have_count(3)
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_toolbox_toggle_all_layers(live_server, map, page):
|
||||||
|
map.settings["properties"]["onLoadPanel"] = "databrowser"
|
||||||
|
map.save()
|
||||||
|
data = {
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"properties": {"name": "one point"},
|
||||||
|
"geometry": {"type": "Point", "coordinates": [3.33, 46.92]},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
DataLayerFactory(map=map, data=data)
|
||||||
|
data = {
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"properties": {"name": "one other point"},
|
||||||
|
"geometry": {"type": "Point", "coordinates": [3.34, 46.94]},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
DataLayerFactory(map=map, data=data)
|
||||||
|
data = {
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"properties": {"name": "another point"},
|
||||||
|
"geometry": {"type": "Point", "coordinates": [3.35, 46.95]},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"_umap_options": {"displayOnLoad": False},
|
||||||
|
}
|
||||||
|
DataLayerFactory(map=map, data=data, settings={"displayOnLoad": False})
|
||||||
|
page.goto(f"{live_server.url}{map.get_absolute_url()}#10/46.93/3.33")
|
||||||
|
markers = page.locator(".leaflet-marker-icon")
|
||||||
|
expect(markers).to_have_count(2)
|
||||||
|
# Only one is off
|
||||||
|
expect(page.locator(".datalayer.off")).to_have_count(1)
|
||||||
|
|
||||||
|
# Click on button
|
||||||
|
page.locator(".umap-browser [data-ref=toggle]").click()
|
||||||
|
# Should have hidden the two other layers
|
||||||
|
expect(page.locator(".datalayer.off")).to_have_count(3)
|
||||||
|
expect(markers).to_have_count(0)
|
||||||
|
|
||||||
|
# Click again
|
||||||
|
page.locator(".umap-browser [data-ref=toggle]").click()
|
||||||
|
# Should shown all layers
|
||||||
|
expect(page.locator(".datalayer.off")).to_have_count(0)
|
||||||
|
expect(markers).to_have_count(3)
|
||||||
|
|
||||||
|
# Click again
|
||||||
|
page.locator(".umap-browser [data-ref=toggle]").click()
|
||||||
|
# Should hidden again all layers
|
||||||
|
expect(page.locator(".datalayer.off")).to_have_count(3)
|
||||||
|
expect(markers).to_have_count(0)
|
||||||
|
|
Loading…
Reference in a new issue