From 688ef19c04228aead041f6e83329ef47e8a0d66c Mon Sep 17 00:00:00 2001 From: David Larlet Date: Tue, 29 Aug 2023 15:06:43 -0400 Subject: [PATCH 01/16] A11Y: switch from links to buttons when pertinent --- umap/static/umap/js/umap.controls.js | 326 ++++++++++++------------ umap/static/umap/js/umap.core.js | 34 ++- umap/static/umap/js/umap.features.js | 32 ++- umap/static/umap/js/umap.forms.js | 40 ++- umap/static/umap/js/umap.js | 101 ++++---- umap/static/umap/js/umap.layer.js | 55 ++-- umap/static/umap/js/umap.permissions.js | 30 +-- umap/static/umap/js/umap.popup.js | 2 +- umap/static/umap/js/umap.ui.js | 34 +-- 9 files changed, 322 insertions(+), 332 deletions(-) diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js index 75a8371c..2e740fd7 100644 --- a/umap/static/umap/js/umap.controls.js +++ b/umap/static/umap/js/umap.controls.js @@ -319,15 +319,11 @@ L.U.EditControl = L.Control.extend({ }, onAdd: function (map) { - const container = L.DomUtil.create('div', 'leaflet-control-edit-enable'), - edit = L.DomUtil.create('a', '', container) - edit.href = '#' - edit.title = `${L._('Enable editing')} (Ctrl+E)` - edit.textContent = L._('Edit') - - L.DomEvent.addListener(edit, 'click', L.DomEvent.stop).addListener( - edit, - 'click', + const container = L.DomUtil.create('div', 'leaflet-control-edit-enable') + L.DomUtil.createButton( + '', + container, + `${L._('Enable editing')} (Ctrl+E)`, map.enableEdit, map ) @@ -343,15 +339,14 @@ L.Control.Embed = L.Control.extend({ onAdd: function (map) { const container = L.DomUtil.create('div', 'leaflet-control-embed umap-control') - - const link = L.DomUtil.create('a', '', container) - link.href = '#' - link.title = L._('Embed and share this map') - - L.DomEvent.on(link, 'click', L.DomEvent.stop) - .on(link, 'click', map.renderShareBox, map) - .on(link, 'dblclick', L.DomEvent.stopPropagation) - + const shareButton = L.DomUtil.createButton( + '', + container, + L._('Embed and share this map'), + map.renderShareBox, + map + ) + L.DomEvent.on(shareButton, 'dblclick', L.DomEvent.stopPropagation) return container }, }) @@ -362,19 +357,21 @@ L.U.MoreControls = L.Control.extend({ }, onAdd: function () { - const container = L.DomUtil.create('div', 'umap-control-text'), - more = L.DomUtil.create('a', 'umap-control-more', container), - less = L.DomUtil.create('a', 'umap-control-less', container) - more.href = '#' - more.title = L._('More controls') - - L.DomEvent.on(more, 'click', L.DomEvent.stop).on(more, 'click', this.toggle, this) - - less.href = '#' - less.title = L._('Hide controls') - - L.DomEvent.on(less, 'click', L.DomEvent.stop).on(less, 'click', this.toggle, this) - + const container = L.DomUtil.create('div', 'umap-control-text') + const moreButton = L.DomUtil.createButton( + 'umap-control-more', + container, + L._('More controls'), + this.toggle, + this + ) + const lessButton = L.DomUtil.createButton( + 'umap-control-less', + container, + L._('Hide controls'), + this.toggle, + this + ) return container }, @@ -452,23 +449,21 @@ L.U.DataLayersControl = L.Control.extend({ actions ) - const link = L.DomUtil.create('a', 'umap-browse-link', actions) - link.href = '#' - link.title = link.textContent = L._('Browse data') - - const toggle = L.DomUtil.create('a', 'umap-browse-toggle', container) - toggle.href = '#' - toggle.title = L._('See data layers') - - L.DomEvent.on(toggle, 'click', L.DomEvent.stop) - - L.DomEvent.on(link, 'click', L.DomEvent.stop).on( - link, - 'click', + L.DomUtil.createButton( + 'umap-browse-link', + actions, + L._('Browse data'), map.openBrowser, map ) + const toggleButton = L.DomUtil.createButton( + 'umap-browse-toggle', + container, + L._('See data layers') + ) + L.DomEvent.on(toggleButton, 'click', L.DomEvent.stop) + map.whenReady(function () { this.update() }, this) @@ -489,8 +484,8 @@ L.U.DataLayersControl = L.Control.extend({ ) } else { L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation) - L.DomEvent.on(toggle, 'click', L.DomEvent.stop).on( - toggle, + L.DomEvent.on(toggleButton, 'click', L.DomEvent.stop).on( + toggleButton, 'click', this.expand, this @@ -580,14 +575,11 @@ L.U.DataLayersControl = L.Control.extend({ this ) - const bar = L.DomUtil.create('div', 'button-bar', container), - add = L.DomUtil.create('a', 'show-on-edit block add-datalayer button', bar) - add.href = '#' - add.textContent = add.title = L._('Add a layer') - - L.DomEvent.on(add, 'click', L.DomEvent.stop).on( - add, - 'click', + const bar = L.DomUtil.create('div', 'button-bar', container) + L.DomUtil.createButton( + 'show-on-edit block add-datalayer button', + bar, + L._('Add a layer'), this.newDataLayer, this ) @@ -677,7 +669,6 @@ L.U.DataLayer.addInitHook(function () { }) L.U.Map.include({ - _openFacet: function () { const container = L.DomUtil.create('div', 'umap-facet-search'), title = L.DomUtil.add('h3', 'umap-filter-title', container, L._('Facet search')), @@ -777,13 +768,17 @@ L.U.Map.include({ } if (this.options.licence) { const licence = L.DomUtil.add( - 'p', - '', - credits, - `${L._('Map user content has been published under licence')} ` - ), - link = L.DomUtil.add('a', '', licence, this.options.licence.name) - link.href = this.options.licence.url + 'p', + '', + credits, + `${L._('Map user content has been published under licence')} ` + ) + L.DomUtil.createLink( + '', + licence, + this.options.licence.name, + this.options.licence.url + ) } else { L.DomUtil.add('p', '', credits, L._('No licence has been set')) } @@ -865,78 +860,75 @@ L.U.Map.include({ renderEditToolbar: function () { const container = L.DomUtil.create( - 'div', - 'umap-main-edit-toolbox with-transition dark', - this._controlContainer - ), - logo = L.DomUtil.add('a', 'logo', container), - name = L.DomUtil.create('a', 'map-name', container), - share_status = L.DomUtil.create('a', 'share-status', container), - update = () => { - const status = this.permissions.getShareStatusDisplay() - name.textContent = this.getDisplayName() - // status is not set until map is saved once - if (status) - share_status.textContent = L._('Visibility: {status}', { - status: status, - }) + 'div', + 'umap-main-edit-toolbox with-transition dark', + this._controlContainer + ) + L.DomUtil.createLink('logo', container, '/') + const nameButton = L.DomUtil.createButton( + 'map-name', + container, + '', + this.edit, + this + ) + const shareStatusButton = L.DomUtil.createButton( + 'share-status', + container, + '', + this.permissions.edit, + this.permissions + ) + const update = () => { + const status = this.permissions.getShareStatusDisplay() + nameButton.textContent = this.getDisplayName() + // status is not set until map is saved once + if (status) { + shareStatusButton.textContent = L._('Visibility: {status}', { + status: status, + }) } + } update() this.once('saved', L.bind(update, this)) - logo.href = '/' if (this.options.editMode === 'advanced') { - name.href = '#' - share_status.href = '#' - L.DomEvent.on(name, 'click', this.edit, this) - L.DomEvent.on(share_status, 'click', this.permissions.edit, this.permissions) + L.DomEvent.on(nameButton, 'click', this.edit, this) + L.DomEvent.on(shareStatusButton, 'click', this.permissions.edit, this.permissions) } this.on('postsync', L.bind(update, this)) - const save = L.DomUtil.create('a', 'leaflet-control-edit-save button', container) - save.href = '#' - save.title = `${L._('Save current edits')} (Ctrl+S)` - save.textContent = L._('Save') - const cancel = L.DomUtil.create('a', 'leaflet-control-edit-cancel', container) - cancel.href = '#' - cancel.title = `${L._('Cancel edits')} (Ctrl+Z)` - cancel.textContent = L._('Cancel all') - const disable = L.DomUtil.create('a', 'leaflet-control-edit-disable', container) - disable.href = '#' - disable.textContent = L._('Disable editing') - disable.title = `${disable.textContent} (Ctrl+E)` - this.help.link(container, 'edit') - if (this.options.user) { - const userLabel = L.DomUtil.add( - 'a', - 'umap-user', - container, - L._(`My Dashboard ({username})`, { username: this.options.user.name }) - ) - userLabel.href = this.options.user.url - } - - L.DomEvent.addListener(disable, 'click', L.DomEvent.stop).addListener( - disable, - 'click', + L.DomUtil.createButton( + 'leaflet-control-edit-save button', + container, + `${L._('Save current edits')} (Ctrl+S)`, + this.save, + this + ) + L.DomUtil.createButton( + 'leaflet-control-edit-cancel', + container, + `${L._('Cancel edits')} (Ctrl+Z)`, + this.askForReset, + this + ) + L.DomUtil.createButton( + 'leaflet-control-edit-disable', + container, + `${L._('Disable editing')} (Ctrl+E)`, function (e) { this.disableEdit(e) this.ui.closePanel() }, this ) - - L.DomEvent.addListener(save, 'click', L.DomEvent.stop).addListener( - save, - 'click', - this.save, - this - ) - - L.DomEvent.addListener(cancel, 'click', L.DomEvent.stop).addListener( - cancel, - 'click', - this.askForReset, - this - ) + this.help.link(container, 'edit') + if (this.options.user) { + L.DomUtil.createLink( + 'umap-user', + container, + L._(`My Dashboard ({username})`, { username: this.options.user.name }), + this.options.user.url + ) + } }, renderShareBox: function () { @@ -1020,12 +1012,19 @@ L.U.Map.include({ } } toggleCaveat() - const download = L.DomUtil.create('a', 'button', container) - download.textContent = L._('Download data') - L.DomEvent.on(download, 'click', () => { - if (typeInput.value === 'umap') this.fullDownload() - else this.download(typeInput.value) - }) + L.DomUtil.createButton( + 'button', + container, + L._('Download data'), + () => { + if (typeInput.value === 'umap') { + this.fullDownload() + } else { + this.download(typeInput.value) + } + }, + this + ) this.ui.openPanel({ data: { html: container } }) }, @@ -1171,15 +1170,14 @@ L.U.TileLayerControl = L.Control.extend({ onAdd: function () { const container = L.DomUtil.create('div', 'leaflet-control-tilelayers umap-control') - - const link = L.DomUtil.create('a', '', container) - link.href = '#' - link.title = L._('Change map background') - - L.DomEvent.on(link, 'click', L.DomEvent.stop) - .on(link, 'click', this.openSwitcher, this) - .on(link, 'dblclick', L.DomEvent.stopPropagation) - + const changeMapBackgroundButton = L.DomUtil.createButton( + '', + container, + L._('Change map background'), + this.openSwitcher, + this + ) + L.DomEvent.on(changeMapBackgroundButton, 'dblclick', L.DomEvent.stopPropagation) return container }, @@ -1236,7 +1234,12 @@ L.U.AttributionControl = L.Control.Attribution.extend({ // Use our how container, so we can hide/show on small screens const credits = this._container.innerHTML this._container.innerHTML = '' - const container = L.DomUtil.add('div', 'attribution-container', this._container, credits) + const container = L.DomUtil.add( + 'div', + 'attribution-container', + this._container, + credits + ) if (this._map.options.shortCredit) { L.DomUtil.add( 'span', @@ -1253,8 +1256,7 @@ L.U.AttributionControl = L.Control.Attribution.extend({ } if (window.top === window.self && this._map.options.captionMenus) { // We are not in iframe mode - const home = L.DomUtil.add('a', '', container, ` — ${L._('Home')}`) - home.href = '/' + L.DomUtil.createLink('', container, ` — ${L._('Home')}`, '/') } if (this._map.options.captionMenus) { const poweredBy = L.DomUtil.add( @@ -1277,16 +1279,17 @@ L.U.StarControl = L.Control.extend({ onAdd: function (map) { const status = map.options.starred ? ' starred' : '' const container = L.DomUtil.create( - 'div', - `leaflet-control-star umap-control${status}` - ), - link = L.DomUtil.create('a', '', container) - link.href = '#' - link.title = L._('Star this map') - L.DomEvent.on(link, 'click', L.DomEvent.stop) - .on(link, 'click', map.star, map) - .on(link, 'dblclick', L.DomEvent.stopPropagation) - + 'div', + `leaflet-control-star umap-control${status}` + ) + const starMapButton = L.DomUtil.createButton( + '', + container, + L._('Star this map'), + map.star, + map + ) + L.DomEvent.on(starMapButton, 'dblclick', L.DomEvent.stopPropagation) return container }, }) @@ -1346,17 +1349,18 @@ L.U.SearchControl = L.Control.extend({ }, onAdd: function (map) { - const container = L.DomUtil.create('div', 'leaflet-control-search umap-control'), - self = this - + const container = L.DomUtil.create('div', 'leaflet-control-search umap-control') L.DomEvent.disableClickPropagation(container) - const link = L.DomUtil.create('a', '', container) - link.href = '#' - link.title = L._('Search a place name') - L.DomEvent.on(link, 'click', (e) => { - L.DomEvent.stop(e) - self.openPanel(map) - }) + L.DomUtil.createButton( + '', + container, + L._('Search a place name'), + (e) => { + L.DomEvent.stop(e) + this.openPanel(map) + }, + this + ) return container }, @@ -1562,7 +1566,7 @@ L.U.Editable = L.Editable.extend({ return new L.U.Marker(this.map, latlng, this._getDefaultProperties()) }, - _getDefaultProperties: function() { + _getDefaultProperties: function () { const result = {} if (this.map.options.featuresHaveOwner && this.map.options.hasOwnProperty('user')) { result.geojson = { properties: { owner: this.map.options.user.id } } diff --git a/umap/static/umap/js/umap.core.js b/umap/static/umap/js/umap.core.js index a9398945..41bfd4bd 100644 --- a/umap/static/umap/js/umap.core.js +++ b/umap/static/umap/js/umap.core.js @@ -314,14 +314,25 @@ L.DomUtil.createFieldset = (container, legend, options) => { } L.DomUtil.createButton = (className, container, content, callback, context) => { + // TODO: actually switch to buttons’ elements. const el = L.DomUtil.add('a', className, container, content) el.href = '#' + // const el = L.DomUtil.add('button', className, container, content) if (callback) { L.DomEvent.on(el, 'click', L.DomEvent.stop).on(el, 'click', callback, context) } return el } +L.DomUtil.createLink = (className, container, content, url, target) => { + const el = L.DomUtil.add('a', className, container, content) + el.href = url + if (target) { + el.target = target + } + return el +} + L.DomUtil.classIf = (el, className, bool) => { if (bool) L.DomUtil.addClass(el, className) else L.DomUtil.removeClass(el, className) @@ -418,13 +429,17 @@ L.U.Help = L.Class.extend({ 'umap-help-box with-transition dark', document.body ) - const closeLink = L.DomUtil.create('a', 'umap-close-link', this.box) - closeLink.href = '#' - L.DomUtil.add('i', 'umap-close-icon', closeLink) - const label = L.DomUtil.create('span', '', closeLink) + const closeButton = L.DomUtil.createButton( + 'umap-close-link', + this.box, + '', + this.hide, + this + ) + L.DomUtil.add('i', 'umap-close-icon', closeButton) + const label = L.DomUtil.create('span', '', closeButton) label.title = label.textContent = L._('Close') this.content = L.DomUtil.create('div', 'umap-help-content', this.box) - L.DomEvent.on(closeLink, 'click', this.hide, this) }, onKeyDown: function (e) { @@ -457,8 +472,11 @@ L.U.Help = L.Class.extend({ }, button: function (container, entries, classname) { - const helpButton = L.DomUtil.create('a', classname || 'umap-help-button', container) - helpButton.href = '#' + const helpButton = L.DomUtil.createButton( + classname || 'umap-help-button', + container, + L._('Help') + ) if (entries) { L.DomEvent.on(helpButton, 'click', L.DomEvent.stop).on( helpButton, @@ -475,7 +493,7 @@ L.U.Help = L.Class.extend({ link: function (container, entries) { const helpButton = this.button(container, entries, 'umap-help-link') - helpButton.textContent = L._("Help") + helpButton.textContent = L._('Help') return helpButton }, diff --git a/umap/static/umap/js/umap.features.js b/umap/static/umap/js/umap.features.js index 5b3b8f7b..471c002c 100644 --- a/umap/static/umap/js/umap.features.js +++ b/umap/static/umap/js/umap.features.js @@ -130,12 +130,10 @@ L.U.FeatureMixin = { }, getAdvancedEditActions: function (container) { - const deleteLink = L.DomUtil.create('a', 'button umap-delete', container) - deleteLink.href = '#' - deleteLink.textContent = L._('Delete') - L.DomEvent.on( - deleteLink, - 'click', + const deleteButton = L.DomUtil.createButton( + 'button umap-delete', + container, + L._('Delete'), function (e) { L.DomEvent.stop(e) if (this.confirmDelete()) this.map.ui.closePanel() @@ -1017,10 +1015,13 @@ L.U.Polyline = L.Polyline.extend({ getAdvancedEditActions: function (container) { L.U.FeatureMixin.getAdvancedEditActions.call(this, container) - const toPolygon = L.DomUtil.create('a', 'button umap-to-polygon', container) - toPolygon.href = '#' - toPolygon.textContent = L._('Transform to polygon') - L.DomEvent.on(toPolygon, 'click', this.toPolygon, this) + const toPolygon = L.DomUtil.createButton( + 'button umap-to-polygon', + container, + L._('Transform to polygon'), + this.toPolygon, + this + ) }, _mergeShapes: function (from, to) { @@ -1164,10 +1165,13 @@ L.U.Polygon = L.Polygon.extend({ getAdvancedEditActions: function (container) { L.U.FeatureMixin.getAdvancedEditActions.call(this, container) - const toPolyline = L.DomUtil.create('a', 'button umap-to-polyline', container) - toPolyline.href = '#' - toPolyline.textContent = L._('Transform to lines') - L.DomEvent.on(toPolyline, 'click', this.toPolyline, this) + const toPolyline = L.DomUtil.createButton( + 'button umap-to-polyline', + container, + L._('Transform to lines'), + this.toPolyline, + this + ) }, isMulti: function () { diff --git a/umap/static/umap/js/umap.forms.js b/umap/static/umap/js/umap.forms.js index c75d01ec..c81417c9 100644 --- a/umap/static/umap/js/umap.forms.js +++ b/umap/static/umap/js/umap.forms.js @@ -557,12 +557,10 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({ L.DomEvent.on(el, 'click', this.fetchIconList, this) } } - this.button = L.DomUtil.create('a', 'button action-button', this.buttonsContainer) - this.button.textContent = this.value() ? L._('Change') : L._('Add') - this.button.href = '#' - L.DomEvent.on(this.button, 'click', L.DomEvent.stop).on( - this.button, - 'click', + this.button = L.DomUtil.createButton( + 'button action-button', + this.buttonsContainer, + this.value() ? L._('Change') : L._('Add'), this.fetchIconList, this ) @@ -616,34 +614,30 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({ for (const idx in data.pictogram_list) { this.addIconPreview(data.pictogram_list[idx]) } - const closeButton = L.DomUtil.create('a', 'button action-button', this.pictogramsContainer) - closeButton.textContent = L._('Close') - closeButton.href = '#' - closeButton.style.display = 'block' - closeButton.style.clear = 'both' - L.DomEvent.on(closeButton, 'click', L.DomEvent.stop).on( - closeButton, - 'click', + const cancelButton = L.DomUtil.createButton( + 'button action-button', + this.pictogramsContainer, + L._('Close'), function (e) { this.pictogramsContainer.innerHTML = '' this.udpatePreview() }, this ) - const customButton = L.DomUtil.create('a', '', this.pictogramsContainer) - customButton.textContent = L._('Toggle direct input (advanced)') - customButton.href = '#' - customButton.style.display = 'block' - customButton.style.clear = 'both' - this.builder.map.help.button(customButton, 'formatIconSymbol') - L.DomEvent.on(customButton, 'click', L.DomEvent.stop).on( - customButton, - 'click', + cancelButton.style.display = 'block' + cancelButton.style.clear = 'both' + const customButton = L.DomUtil.createButton( + '', + this.pictogramsContainer, + L._('Toggle direct input (advanced)'), function (e) { this.input.type = this.input.type === 'text' ? 'hidden' : 'text' }, this ) + customButton.style.display = 'block' + customButton.style.clear = 'both' + this.builder.map.help.button(customButton, 'formatIconSymbol') }, fetchIconList: function (e) { diff --git a/umap/static/umap/js/umap.js b/umap/static/umap/js/umap.js index e762db14..b2f0cc6b 100644 --- a/umap/static/umap/js/umap.js +++ b/umap/static/umap/js/umap.js @@ -1532,16 +1532,10 @@ L.U.Map.include({ }) limitBounds.appendChild(boundsBuilder.build()) const boundsButtons = L.DomUtil.create('div', 'button-bar half', limitBounds) - const setCurrentButton = L.DomUtil.add( - 'a', + L.DomUtil.createButton( 'button', boundsButtons, - L._('Use current bounds') - ) - setCurrentButton.href = '#' - L.DomEvent.on( - setCurrentButton, - 'click', + L._('Use current bounds'), function () { const bounds = this.getBounds() this.options.limitBounds.south = L.Util.formatNum(bounds.getSouth()) @@ -1554,11 +1548,10 @@ L.U.Map.include({ }, this ) - const emptyBounds = L.DomUtil.add('a', 'button', boundsButtons, L._('Empty')) - emptyBounds.href = '#' - L.DomEvent.on( - emptyBounds, - 'click', + L.DomUtil.createButton( + 'button', + boundsButtons, + L._('Empty'), function () { this.options.limitBounds.south = null this.options.limitBounds.west = null @@ -1650,34 +1643,39 @@ L.U.Map.include({ const advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions')) const advancedButtons = L.DomUtil.create('div', 'button-bar half', advancedActions) if (this.permissions.isOwner()) { - const del = L.DomUtil.create('a', 'button umap-delete', advancedButtons) - del.href = '#' - del.title = L._('Delete map') - del.textContent = L._('Delete') - L.DomEvent.on(del, 'click', L.DomEvent.stop).on(del, 'click', this.del, this) - const empty = L.DomUtil.create('a', 'button umap-empty', advancedButtons) - empty.href = '#' - empty.textContent = L._('Empty') - empty.title = L._('Delete all layers') - L.DomEvent.on(empty, 'click', L.DomEvent.stop).on( - empty, - 'click', + L.DomUtil.createButton( + 'button umap-delete', + advancedButtons, + L._('Delete'), + this.del, + this + ) + L.DomUtil.createButton( + 'button umap-empty', + advancedButtons, + L._('Empty'), this.empty, this ) } - const clone = L.DomUtil.create('a', 'button umap-clone', advancedButtons) - clone.href = '#' - clone.textContent = L._('Clone') - clone.title = L._('Clone this map') - L.DomEvent.on(clone, 'click', L.DomEvent.stop).on(clone, 'click', this.clone, this) - const download = L.DomUtil.create('a', 'button umap-download', advancedButtons) - download.href = '#' - download.textContent = L._('Download') - download.title = L._('Open download panel') - L.DomEvent.on(download, 'click', L.DomEvent.stop).on( - download, - 'click', + L.DomUtil.createButton( + 'button umap-clone', + advancedButtons, + L._('Clone this map'), + this.clone, + this + ) + L.DomUtil.createButton( + 'button umap-empty', + advancedButtons, + L._('Delete all layers'), + this.empty, + this + ) + L.DomUtil.createButton( + 'button umap-download', + advancedButtons, + L._('Open download panel'), this.renderShareBox, this ) @@ -1739,38 +1737,25 @@ L.U.Map.include({ L.DomEvent.disableClickPropagation(container) this.permissions.addOwnerLink('span', container) if (this.options.captionMenus) { - const about = L.DomUtil.add( - 'a', + L.DomUtil.createButton( 'umap-about-link', container, - ` — ${L._('About')}` + ` — ${L._('About')}`, + this.displayCaption, + this ) - about.href = '#' - L.DomEvent.on(about, 'click', this.displayCaption, this) - const browser = L.DomUtil.add( - 'a', + L.DomUtil.createButton( 'umap-open-browser-link', container, - ` | ${L._('Browse data')}` - ) - browser.href = '#' - L.DomEvent.on(browser, 'click', L.DomEvent.stop).on( - browser, - 'click', + ` | ${L._('Browse data')}`, this.openBrowser, this ) if (this.options.facetKey) { - const filter = L.DomUtil.add( - 'a', + L.DomUtil.createButton( 'umap-open-filter-link', container, - ` | ${L._('Select data')}` - ) - filter.href = '#' - L.DomEvent.on(filter, 'click', L.DomEvent.stop).on( - filter, - 'click', + ` | ${L._('Select data')}`, this.openFacet, this ) diff --git a/umap/static/umap/js/umap.layer.js b/umap/static/umap/js/umap.layer.js index 1200f8c2..5a8066d3 100644 --- a/umap/static/umap/js/umap.layer.js +++ b/umap/static/umap/js/umap.layer.js @@ -1272,16 +1272,10 @@ L.U.DataLayer = L.Evented.extend({ const advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions')) const advancedButtons = L.DomUtil.create('div', 'button-bar half', advancedActions) - const deleteLink = L.DomUtil.create( - 'a', + const deleteLink = L.DomUtil.createButton( 'button delete_datalayer_button umap-delete', - advancedButtons - ) - deleteLink.textContent = L._('Delete') - deleteLink.href = '#' - L.DomEvent.on(deleteLink, 'click', L.DomEvent.stop).on( - deleteLink, - 'click', + advancedButtons, + L._('Delete'), function () { this._delete() this.map.ui.closePanel() @@ -1289,22 +1283,18 @@ L.U.DataLayer = L.Evented.extend({ this ) if (!this.isRemoteLayer()) { - const emptyLink = L.DomUtil.create('a', 'button umap-empty', advancedButtons) - emptyLink.textContent = L._('Empty') - emptyLink.href = '#' - L.DomEvent.on(emptyLink, 'click', L.DomEvent.stop).on( - emptyLink, - 'click', + const emptyLink = L.DomUtil.createButton( + 'button umap-empty', + advancedButtons, + L._('Empty'), this.empty, this ) } - const cloneLink = L.DomUtil.create('a', 'button umap-clone', advancedButtons) - cloneLink.textContent = L._('Clone') - cloneLink.href = '#' - L.DomEvent.on(cloneLink, 'click', L.DomEvent.stop).on( - cloneLink, - 'click', + const cloneLink = L.DomUtil.createButton( + 'button umap-clone', + advancedButtons, + L._('Clone'), function () { const datalayer = this.clone() datalayer.edit() @@ -1312,10 +1302,13 @@ L.U.DataLayer = L.Evented.extend({ this ) if (this.umap_id) { - const download = L.DomUtil.create('a', 'button umap-download', advancedButtons) - download.textContent = L._('Download') - download.href = this._dataUrl() - download.target = '_blank' + const download = L.DomUtil.createLink( + 'button umap-download', + advancedButtons, + L._('Download'), + this._dataUrl(), + '_blank' + ) } this.map.ui.openPanel({ data: { html: container }, className: 'dark' }) }, @@ -1343,16 +1336,14 @@ L.U.DataLayer = L.Evented.extend({ const date = new Date(parseInt(data.at, 10)) const content = `${date.toLocaleString(L.lang)} (${parseInt(data.size) / 1000}Kb)` const el = L.DomUtil.create('div', 'umap-datalayer-version', versionsContainer) - const a = L.DomUtil.create('a', '', el) - L.DomUtil.add('span', '', el, content) - a.href = '#' - a.title = L._('Restore this version') - L.DomEvent.on(a, 'click', L.DomEvent.stop).on( - a, - 'click', + const a = L.DomUtil.createButton( + '', + el, + L._('Restore this version'), () => this.restore(data.name), this ) + L.DomUtil.add('span', '', el, content) } const versionsContainer = L.DomUtil.createFieldset(container, L._('Versions'), { diff --git a/umap/static/umap/js/umap.permissions.js b/umap/static/umap/js/umap.permissions.js index 7e277503..1687e142 100644 --- a/umap/static/umap/js/umap.permissions.js +++ b/umap/static/umap/js/umap.permissions.js @@ -116,12 +116,10 @@ L.U.MapPermissions = L.Class.extend({ L._('Advanced actions') ) const advancedButtons = L.DomUtil.create('div', 'button-bar', advancedActions) - const download = L.DomUtil.create('a', 'button', advancedButtons) - download.href = '#' - download.textContent = L._('Attach the map to my account') - L.DomEvent.on(download, 'click', L.DomEvent.stop).on( - download, - 'click', + const download = L.DomUtil.createButton( + 'button', + advancedButtons, + L._('Attach the map to my account'), this.attach, this ) @@ -188,15 +186,17 @@ L.U.MapPermissions = L.Class.extend({ addOwnerLink: function (element, container) { if (this.options.owner && this.options.owner.name && this.options.owner.url) { const ownerContainer = L.DomUtil.add( - element, - 'umap-map-owner', - container, - ` ${L._('by')} ` - ), - owner = L.DomUtil.create('a') - owner.href = this.options.owner.url - owner.textContent = this.options.owner.name - ownerContainer.appendChild(owner) + element, + 'umap-map-owner', + container, + ` ${L._('by')} ` + ) + L.DomUtil.createLink( + '', + ownerContainer, + this.options.owner.name, + this.options.owner.url + ) } }, diff --git a/umap/static/umap/js/umap.popup.js b/umap/static/umap/js/umap.popup.js index 9fd96b3c..9487890c 100644 --- a/umap/static/umap/js/umap.popup.js +++ b/umap/static/umap/js/umap.popup.js @@ -107,7 +107,7 @@ L.U.PopupTemplate.Default = L.Class.extend({ properties ) content = L.Util.greedyTemplate(template, properties) - content = L.Util.toHTML(content, {target: target}) + content = L.Util.toHTML(content, { target: target }) container.innerHTML = content return container }, diff --git a/umap/static/umap/js/umap.ui.js b/umap/static/umap/js/umap.ui.js index 28bb6403..abe99542 100644 --- a/umap/static/umap/js/umap.ui.js +++ b/umap/static/umap/js/umap.ui.js @@ -93,17 +93,16 @@ L.U.UI = L.Evented.extend({ if (timeoutID) window.clearTimeout(timeoutID) this.popAlert() } - const closeLink = L.DomUtil.create('a', 'umap-close-link', this._alert) - closeLink.href = '#' - L.DomUtil.add('i', 'umap-close-icon', closeLink) - const label = L.DomUtil.create('span', '', closeLink) - label.title = label.textContent = L._('Close') - L.DomEvent.on(closeLink, 'click', L.DomEvent.stop).on( - closeLink, - 'click', + const closeButton = L.DomUtil.createButton( + 'umap-close-link', + this._alert, + '', close, this ) + L.DomUtil.add('i', 'umap-close-icon', closeButton) + const label = L.DomUtil.create('span', '', closeButton) + label.title = label.textContent = L._('Close') L.DomUtil.add('div', '', this._alert, e.content) if (e.actions) { let action, el, input @@ -116,18 +115,13 @@ L.U.UI = L.Evented.extend({ this._alert ) } - el = L.DomUtil.element('a', { className: 'umap-action' }, this._alert) - el.href = '#' - el.textContent = action.label - L.DomEvent.on(el, 'click', L.DomEvent.stop) - if (action.callback) { - L.DomEvent.on( - el, - 'click', - action.callback, - action.callbackContext || this.map - ) - } + el = L.DomUtil.createButton( + { className: 'umap-action' }, + this._alert, + action.label, + action.callback, + action.callbackContext || this.map + ) L.DomEvent.on(el, 'click', close, this) } } From a28d427ba1ddaeb5d534fe17fba848a31a201f2e Mon Sep 17 00:00:00 2001 From: David Larlet Date: Fri, 1 Sep 2023 14:35:38 -0400 Subject: [PATCH 02/16] Set a default `type="button"` for JS buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > The `button` [type] has no default behavior, and does nothing when pressed by default. It can have client-side scripts listen to the element's events, which are triggered when the events occur. — https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button --- umap/static/umap/js/umap.core.js | 1 + 1 file changed, 1 insertion(+) diff --git a/umap/static/umap/js/umap.core.js b/umap/static/umap/js/umap.core.js index 41bfd4bd..a5f79ec6 100644 --- a/umap/static/umap/js/umap.core.js +++ b/umap/static/umap/js/umap.core.js @@ -318,6 +318,7 @@ L.DomUtil.createButton = (className, container, content, callback, context) => { const el = L.DomUtil.add('a', className, container, content) el.href = '#' // const el = L.DomUtil.add('button', className, container, content) + // el.type = 'button' if (callback) { L.DomEvent.on(el, 'click', L.DomEvent.stop).on(el, 'click', callback, context) } From 7695906c38948364189e39ddb1d63f73b74dc674 Mon Sep 17 00:00:00 2001 From: David Larlet Date: Fri, 8 Sep 2023 09:46:19 -0400 Subject: [PATCH 03/16] Better styles for buttons (vs. links) --- umap/static/umap/base.css | 23 ++++++++++++++++------- umap/static/umap/js/umap.core.js | 8 ++++---- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/umap/static/umap/base.css b/umap/static/umap/base.css index 12cd7d39..7d2f1984 100644 --- a/umap/static/umap/base.css +++ b/umap/static/umap/base.css @@ -163,7 +163,9 @@ select { select[multiple="multiple"] { height: auto; } -.button, input[type="submit"] { +.button, +[type="button"], +input[type="submit"] { display: block; margin-bottom: 14px; text-align: center; @@ -177,12 +179,15 @@ select[multiple="multiple"] { border: none; text-decoration: none; } -.dark .button { +.dark .button, +.dark [type="button"] { background-color: #2a2e30; color: #eeeeec; border: 1px solid #1b1f20; } -.dark .button:hover, .dark input[type="submit"]:hover { +.dark .button:hover, +.dark [type="button"]:hover, +.dark input[type="submit"]:hover { background-color: #2e3436; } .dark a { @@ -361,7 +366,8 @@ input.switch:checked ~ label:after { .umap-multiplechoice.by4 { grid-template-columns: 1fr 1fr 1fr 1fr; } -.button-bar .button { +.button-bar .button, +.button-bar [type="button"] { display: inline-block; } .umap-multiplechoice input[type='radio'] { @@ -558,10 +564,12 @@ input.blur { border-top-right-radius: 0; border-bottom-right-radius: 0; } -.blur + .button:before { +.blur + .button:before, +.blur + [type="button"]:before { content: '✔'; } -.blur + .button { +.blur + .button, +.blur + [type="button"] { width: 40px; height: 18px; display: inline-block; @@ -571,7 +579,8 @@ input.blur { border-bottom-left-radius: 0; box-sizing: border-box; } -input[type=hidden].blur + .button { +input[type=hidden].blur + .button, +input[type=hidden].blur + [type="button"] { display: none; } diff --git a/umap/static/umap/js/umap.core.js b/umap/static/umap/js/umap.core.js index a5f79ec6..7582c9b5 100644 --- a/umap/static/umap/js/umap.core.js +++ b/umap/static/umap/js/umap.core.js @@ -315,10 +315,10 @@ L.DomUtil.createFieldset = (container, legend, options) => { L.DomUtil.createButton = (className, container, content, callback, context) => { // TODO: actually switch to buttons’ elements. - const el = L.DomUtil.add('a', className, container, content) - el.href = '#' - // const el = L.DomUtil.add('button', className, container, content) - // el.type = 'button' + // const el = L.DomUtil.add('a', className, container, content) + // el.href = '#' + const el = L.DomUtil.add('button', className, container, content) + el.type = 'button' if (callback) { L.DomEvent.on(el, 'click', L.DomEvent.stop).on(el, 'click', callback, context) } From 79117f2a66d7f56bff7901032f3c44ebc3bb761f Mon Sep 17 00:00:00 2001 From: David Larlet Date: Fri, 15 Sep 2023 21:27:44 -0400 Subject: [PATCH 04/16] Better styles for buttons (vs. links) --- umap/static/umap/js/umap.controls.js | 3 +- umap/static/umap/js/umap.core.js | 10 ++++- umap/static/umap/js/umap.forms.js | 7 ++-- umap/static/umap/map.css | 61 ++++++++++++++++++---------- 4 files changed, 54 insertions(+), 27 deletions(-) diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js index 2e740fd7..5f6e0637 100644 --- a/umap/static/umap/js/umap.controls.js +++ b/umap/static/umap/js/umap.controls.js @@ -864,7 +864,8 @@ L.U.Map.include({ 'umap-main-edit-toolbox with-transition dark', this._controlContainer ) - L.DomUtil.createLink('logo', container, '/') + const logo = L.DomUtil.create('div', 'logo', container) + L.DomUtil.createLink('', logo, 'uMap', '/', null, L._('Go to the homepage')) const nameButton = L.DomUtil.createButton( 'map-name', container, diff --git a/umap/static/umap/js/umap.core.js b/umap/static/umap/js/umap.core.js index 7582c9b5..7141f183 100644 --- a/umap/static/umap/js/umap.core.js +++ b/umap/static/umap/js/umap.core.js @@ -129,7 +129,10 @@ L.Util.toHTML = (r, options) => { // images r = r.replace(/{{([^\]|]*?)}}/g, '') - r = r.replace(/{{([^|]*?)\|(\d*?)(px)?}}/g, '') + r = r.replace( + /{{([^|]*?)\|(\d*?)(px)?}}/g, + '' + ) //Unescape http r = r.replace(/(h_t_t_p)/g, 'http') @@ -325,12 +328,15 @@ L.DomUtil.createButton = (className, container, content, callback, context) => { return el } -L.DomUtil.createLink = (className, container, content, url, target) => { +L.DomUtil.createLink = (className, container, content, url, target, title) => { const el = L.DomUtil.add('a', className, container, content) el.href = url if (target) { el.target = target } + if (title) { + el.title = title + } return el } diff --git a/umap/static/umap/js/umap.forms.js b/umap/static/umap/js/umap.forms.js index c81417c9..1f8598ac 100644 --- a/umap/static/umap/js/umap.forms.js +++ b/umap/static/umap/js/umap.forms.js @@ -614,7 +614,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({ for (const idx in data.pictogram_list) { this.addIconPreview(data.pictogram_list[idx]) } - const cancelButton = L.DomUtil.createButton( + const closeButton = L.DomUtil.createButton( 'button action-button', this.pictogramsContainer, L._('Close'), @@ -624,8 +624,9 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({ }, this ) - cancelButton.style.display = 'block' - cancelButton.style.clear = 'both' + closeButton.style.display = 'block' + closeButton.style.clear = 'both' + const customButton = L.DomUtil.createButton( '', this.pictogramsContainer, diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css index 483316e9..0b18ef2e 100644 --- a/umap/static/umap/map.css +++ b/umap/static/umap/map.css @@ -42,7 +42,8 @@ } .leaflet-measure-control a, .leaflet-control-locate a, -.umap-control a { +.umap-control a, +.umap-control [type="button"] { background-position: 50% 50%; background-repeat: no-repeat; display: block; @@ -50,15 +51,19 @@ width: 36px; line-height: 36px; background-image: url('./img/24.svg'); + text-indent: -9999px; + margin-bottom: 0; } .leaflet-control.display-on-more, -a.umap-control-less { +.umap-control-less { display: none; } .umap-control-more, .umap-control-less { background-image: url('./img/24-white.svg'); background-position: -72px -474px; + text-indent: -9999px; + margin-bottom: 0; } .umap-control-less { background-position: -108px -476px; @@ -70,13 +75,13 @@ a.umap-control-less { .umap-more-controls .umap-control-more { display: none; } -.leaflet-control-embed a { +.leaflet-control-embed [type="button"] { background-position: -72px -108px; } -.leaflet-control-tilelayers a { +.leaflet-control-tilelayers [type="button"] { background-position: -72px 0; } -.leaflet-control-home a { +.leaflet-control-home [type="button"] { background-position: -122px -82px; } .leaflet-control-locate a { @@ -88,17 +93,17 @@ a.umap-control-less { background-position: -72px -144px; box-shadow: 0 0 4px 0 black inset; } -.leaflet-control-star a { +.leaflet-control-star [type="button"] { background-position: -108px -144px; } -.leaflet-control-star.starred a { +.leaflet-control-star.starred [type="button"] { background-position: -144px -144px; } -.leaflet-control-search a { +.leaflet-control-search [type="button"] { background-position: -36px -108px; display: block; } -.leaflet-control-search a.loading { +.leaflet-control-search [type="button"].loading { background-image: url('./img/search.gif'); } .umap-control-text { @@ -109,14 +114,15 @@ a.umap-control-less { text-align: center; font-size: 0.8em; } -.umap-control-text a { +.umap-control-text [type="button"] { float: right; + background-color: #666; margin: 0; width: 36px; + min-height: 23px; height: 23px; - line-height: 23px; } -.leaflet-control-edit-enable a:before { +.leaflet-control-edit-enable [type="button"]:before { content: ' '; width: 24px; height: 24px; @@ -126,7 +132,7 @@ a.umap-control-less { background-position: -52px -49px; } -.leaflet-control-edit-enable a { +.leaflet-control-edit-enable [type="button"] { width: initial; padding: 0 20px; background-color: #353c3e; @@ -138,7 +144,7 @@ a.umap-control-less { display: block; } .leaflet-control-toolbar .leaflet-toolbar-icon.dark:hover, -.leaflet-control-edit-enable a:hover { +.leaflet-control-edit-enable [type="button"]:hover { background-color: #4d5759; } .umap-permanent-credits-container { @@ -353,6 +359,7 @@ ul.photon-autocomplete { } .umap-help-box .umap-close-link { float: right; + width: 100px; } .umap-help-button { display: inline-block; @@ -362,7 +369,12 @@ ul.photon-autocomplete { background-position: -4px -4px; background-repeat: no-repeat; background-image: url('./img/16.svg'); + background-color: #323737 !important; vertical-align: middle; + text-indent: -9999px; + min-height: inherit; + padding-top: 5px; + border: none !important; } .dark .umap-help-button { background-image: url('./img/16-white.svg'); @@ -485,7 +497,7 @@ ul.photon-autocomplete { right: 0; height: 46px; background-color: #323737; - padding: 5px 10px; + padding: 0 10px; text-align: left; line-height: 36px; cursor: auto; @@ -493,22 +505,29 @@ ul.photon-autocomplete { z-index: 1000; opacity: 0.98; color: #fff; + display: flex; } .umap-main-edit-toolbox .logo { + width: 39px; + height: 100%; +} +.umap-main-edit-toolbox .logo a { background-image: url('./img/logo_small.svg'); background-position: 0 center; background-repeat: no-repeat; - width: 60px; display: inline-block; + width: 39px; height: 100%; vertical-align: middle; + text-indent: -9999px; } -.umap-main-edit-toolbox a { +.umap-main-edit-toolbox [type="button"] { color: #fff; font-size: 1.2em; - vertical-align: middle; + border: none; + background-color: #323737; } -.umap-main-edit-toolbox a:hover { +.umap-main-edit-toolbox [type="button"]:hover { text-decoration: underline; } .umap-main-edit-toolbox .map-name { @@ -520,13 +539,13 @@ ul.photon-autocomplete { margin-right: 5px; font-weight: bold; } -a.map-name:after { +.map-name:after { content: '\00a0'; padding-left: 3px; width: 1ch; display: inline-block; } -.umap-is-dirty a.map-name:after { +.umap-is-dirty .map-name:after { content: '*'; } .umap-main-edit-toolbox .share-status { From 9c7269eabca6feb8d85eb7e13596df393c96e911 Mon Sep 17 00:00:00 2001 From: David Larlet Date: Mon, 16 Oct 2023 14:48:20 -0400 Subject: [PATCH 05/16] Better styles for buttons and tooltips --- umap/static/umap/base.css | 27 +++++ umap/static/umap/js/umap.controls.js | 141 ++++++++++++++++++++++----- umap/static/umap/js/umap.core.js | 46 ++++----- umap/static/umap/js/umap.ui.js | 48 ++++++--- umap/static/umap/map.css | 111 +++++++++++---------- umap/static/umap/test/_pre.js | 4 +- 6 files changed, 261 insertions(+), 116 deletions(-) diff --git a/umap/static/umap/base.css b/umap/static/umap/base.css index 7d2f1984..85fa2fbc 100644 --- a/umap/static/umap/base.css +++ b/umap/static/umap/base.css @@ -735,6 +735,7 @@ input:invalid { .umap-alert input { padding: 5px; border-radius: 4px; + width: 100%; } /* *********** */ @@ -771,6 +772,20 @@ input:invalid { border-width: 11px; margin-left: calc(-50% + 21px); } +#umap-tooltip-container.tooltip-bottom:before { + top: -22px; + left: calc(50% - 11px); + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-top-color: rgba(30, 30, 30, 0.7); + border-width: 11px; + margin-left: calc(-50% + 21px); + transform: rotate(180deg); +} #umap-tooltip-container.tooltip.tooltip-left:after { left: 100%; top: 50%; @@ -816,10 +831,22 @@ input:invalid { color: #fff; float: right; padding-right: 10px; + width: 100px; + line-height: 1; + margin: .5rem; + background-color: #666; + font-size: .7rem; } #umap-alert-container .umap-close-icon { background-position: -74px -55px; } +#umap-alert-container .umap-alert-actions { + display: flex; + margin: 1rem; +} +#umap-alert-container .umap-alert-actions .umap-action { + margin-bottom: 0; +} /* *********** */ diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js index 5f6e0637..e8c89ba5 100644 --- a/umap/static/umap/js/umap.controls.js +++ b/umap/static/umap/js/umap.controls.js @@ -320,13 +320,28 @@ L.U.EditControl = L.Control.extend({ onAdd: function (map) { const container = L.DomUtil.create('div', 'leaflet-control-edit-enable') - L.DomUtil.createButton( + const enableEditing = L.DomUtil.createButton( '', container, - `${L._('Enable editing')} (Ctrl+E)`, + L._('Enable editing'), map.enableEdit, map ) + L.DomEvent.on( + enableEditing, + 'mouseover', + function () { + map.ui.tooltip({ + content: `${L._('Edit map')} (Ctrl+E)`, + anchor: enableEditing, + position: 'bottom', + delay: 750, + duration: 5000, + }) + }, + this + ) + return container }, }) @@ -864,22 +879,52 @@ L.U.Map.include({ 'umap-main-edit-toolbox with-transition dark', this._controlContainer ) - const logo = L.DomUtil.create('div', 'logo', container) + const leftContainer = L.DomUtil.create('div', 'umap-left-edit-toolbox', container) + const rightContainer = L.DomUtil.create('div', 'umap-right-edit-toolbox', container) + const logo = L.DomUtil.create('div', 'logo', leftContainer) L.DomUtil.createLink('', logo, 'uMap', '/', null, L._('Go to the homepage')) const nameButton = L.DomUtil.createButton( 'map-name', - container, + leftContainer, '', this.edit, this ) + L.DomEvent.on( + nameButton, + 'mouseover', + function () { + this.ui.tooltip({ + content: L._('Edit the title of the map'), + anchor: nameButton, + position: 'bottom', + delay: 500, + duration: 5000, + }) + }, + this + ) const shareStatusButton = L.DomUtil.createButton( 'share-status', - container, + leftContainer, '', this.permissions.edit, this.permissions ) + L.DomEvent.on( + shareStatusButton, + 'mouseover', + function () { + this.ui.tooltip({ + content: L._('Update who can see and edit the map'), + anchor: shareStatusButton, + position: 'bottom', + delay: 500, + duration: 5000, + }) + }, + this + ) const update = () => { const status = this.permissions.getShareStatusDisplay() nameButton.textContent = this.getDisplayName() @@ -897,39 +942,81 @@ L.U.Map.include({ L.DomEvent.on(shareStatusButton, 'click', this.permissions.edit, this.permissions) } this.on('postsync', L.bind(update, this)) - L.DomUtil.createButton( - 'leaflet-control-edit-save button', - container, - `${L._('Save current edits')} (Ctrl+S)`, - this.save, - this - ) - L.DomUtil.createButton( + if (this.options.user) { + L.DomUtil.createLink( + 'umap-user', + rightContainer, + L._(`My Dashboard ({username})`, { username: this.options.user.name }), + this.options.user.url + ) + } + this.help.link(rightContainer, 'edit') + const controlEditCancel = L.DomUtil.createButton( 'leaflet-control-edit-cancel', - container, - `${L._('Cancel edits')} (Ctrl+Z)`, + rightContainer, + L._('Cancel edits'), this.askForReset, this ) - L.DomUtil.createButton( + L.DomEvent.on( + controlEditCancel, + 'mouseover', + function () { + this.ui.tooltip({ + content: `${L._('Cancel')} (Ctrl+Z)`, + anchor: controlEditCancel, + position: 'bottom', + delay: 500, + duration: 5000, + }) + }, + this + ) + const controlEditDisable = L.DomUtil.createButton( 'leaflet-control-edit-disable', - container, - `${L._('Disable editing')} (Ctrl+E)`, + rightContainer, + L.DomUtil.add('span', '', null, L._('Disable editing')), function (e) { this.disableEdit(e) this.ui.closePanel() }, this ) - this.help.link(container, 'edit') - if (this.options.user) { - L.DomUtil.createLink( - 'umap-user', - container, - L._(`My Dashboard ({username})`, { username: this.options.user.name }), - this.options.user.url - ) - } + L.DomEvent.on( + controlEditDisable, + 'mouseover', + function () { + this.ui.tooltip({ + content: `${L._('Disable editing')} (Ctrl+E)`, + anchor: controlEditDisable, + position: 'bottom', + delay: 500, + duration: 5000, + }) + }, + this + ) + const controlEditSave = L.DomUtil.createButton( + 'leaflet-control-edit-save button', + rightContainer, + L.DomUtil.add('span', '', null, L._('Save current edits')), + this.save, + this + ) + L.DomEvent.on( + controlEditSave, + 'mouseover', + function () { + this.ui.tooltip({ + content: `${L._('Save')} (Ctrl+S)`, + anchor: controlEditSave, + position: 'bottom', + delay: 500, + duration: 5000, + }) + }, + this + ) }, renderShareBox: function () { diff --git a/umap/static/umap/js/umap.core.js b/umap/static/umap/js/umap.core.js index 7141f183..5958598b 100644 --- a/umap/static/umap/js/umap.core.js +++ b/umap/static/umap/js/umap.core.js @@ -261,32 +261,31 @@ L.Util.hasVar = (value) => { } L.Util.copyToClipboard = function (textToCopy) { - // https://stackoverflow.com/a/65996386 - // Navigator clipboard api needs a secure context (https) - if (navigator.clipboard && window.isSecureContext) { - navigator.clipboard.writeText(textToCopy) - } else { - // Use the 'out of viewport hidden text area' trick - const textArea = document.createElement('textarea') - textArea.value = textToCopy + // https://stackoverflow.com/a/65996386 + // Navigator clipboard api needs a secure context (https) + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(textToCopy) + } else { + // Use the 'out of viewport hidden text area' trick + const textArea = document.createElement('textarea') + textArea.value = textToCopy - // Move textarea out of the viewport so it's not visible - textArea.style.position = 'absolute' - textArea.style.left = '-999999px' + // Move textarea out of the viewport so it's not visible + textArea.style.position = 'absolute' + textArea.style.left = '-999999px' - document.body.prepend(textArea) - textArea.select() + document.body.prepend(textArea) + textArea.select() - try { - document.execCommand('copy') - } catch (error) { - console.error(error) - } finally { - textArea.remove() - } - } + try { + document.execCommand('copy') + } catch (error) { + console.error(error) + } finally { + textArea.remove() } - + } +} L.DomUtil.add = (tagName, className, container, content) => { const el = L.DomUtil.create(tagName, className, container) @@ -317,9 +316,6 @@ L.DomUtil.createFieldset = (container, legend, options) => { } L.DomUtil.createButton = (className, container, content, callback, context) => { - // TODO: actually switch to buttons’ elements. - // const el = L.DomUtil.add('a', className, container, content) - // el.href = '#' const el = L.DomUtil.add('button', className, container, content) el.type = 'button' if (callback) { diff --git a/umap/static/umap/js/umap.ui.js b/umap/static/umap/js/umap.ui.js index abe99542..c8e9133c 100644 --- a/umap/static/umap/js/umap.ui.js +++ b/umap/static/umap/js/umap.ui.js @@ -106,18 +106,19 @@ L.U.UI = L.Evented.extend({ L.DomUtil.add('div', '', this._alert, e.content) if (e.actions) { let action, el, input + const form = L.DomUtil.add('div', 'umap-alert-actions', this._alert) for (let i = 0; i < e.actions.length; i++) { action = e.actions[i] if (action.input) { input = L.DomUtil.element( 'input', { className: 'umap-alert-input', placeholder: action.input }, - this._alert + form ) } el = L.DomUtil.createButton( - { className: 'umap-action' }, - this._alert, + 'umap-action', + form, action.label, action.callback, action.callbackContext || this.map @@ -133,20 +134,32 @@ L.U.UI = L.Evented.extend({ } }, - tooltip: function (e) { + tooltip: function (opts) { this.TOOLTIP_ID = Math.random() const id = this.TOOLTIP_ID - L.DomUtil.addClass(this.parent, 'umap-tooltip') - if (e.anchor && e.position === 'top') this.anchorTooltipTop(e.anchor) - else if (e.anchor && e.position === 'left') this.anchorTooltipLeft(e.anchor) - else this.anchorTooltipAbsolute() - this._tooltip.innerHTML = e.content + function showIt() { + if (opts.anchor && opts.position === 'top') { + this.anchorTooltipTop(opts.anchor) + } else if (opts.anchor && opts.position === 'left') { + this.anchorTooltipLeft(opts.anchor) + } else if (opts.anchor && opts.position === 'bottom') { + this.anchorTooltipBottom(opts.anchor) + } else { + this.anchorTooltipAbsolute() + } + L.DomUtil.addClass(this.parent, 'umap-tooltip') + this._tooltip.innerHTML = opts.content + } function closeIt() { this.closeTooltip(id) } - if (e.anchor) L.DomEvent.once(e.anchor, 'mouseout', closeIt, this) - if (e.duration !== Infinity) - window.setTimeout(L.bind(closeIt, this), e.duration || 3000) + window.setTimeout(L.bind(showIt, this), opts.delay || 0) + if (opts.anchor) { + L.DomEvent.once(opts.anchor, 'mouseout', closeIt, this) + } + if (opts.duration !== Infinity) { + window.setTimeout(L.bind(closeIt, this), opts.duration || 3000) + } }, anchorTooltipAbsolute: function () { @@ -168,6 +181,15 @@ L.U.UI = L.Evented.extend({ }) }, + anchorTooltipBottom: function (el) { + this._tooltip.className = 'tooltip-bottom' + const coords = this.getPosition(el) + this.setTooltipPosition({ + left: coords.left + 30, + bottom: this.getDocHeight() - coords.top - 76, + }) + }, + anchorTooltipLeft: function (el) { this._tooltip.className = 'tooltip-left' const coords = this.getPosition(el) @@ -179,7 +201,9 @@ L.U.UI = L.Evented.extend({ closeTooltip: function (id) { if (id && id !== this.TOOLTIP_ID) return + this._tooltip.className = '' this._tooltip.innerHTML = '' + this.setTooltipPosition({}) L.DomUtil.removeClass(this.parent, 'umap-tooltip') }, diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css index 0b18ef2e..71d8c584 100644 --- a/umap/static/umap/map.css +++ b/umap/static/umap/map.css @@ -379,10 +379,6 @@ ul.photon-autocomplete { .dark .umap-help-button { background-image: url('./img/16-white.svg'); } -.umap-help-link { - float: right; - margin-right: 20px; -} .umap-help-on .umap-help-box { visibility: visible; top: 100px; @@ -423,27 +419,37 @@ ul.photon-autocomplete { /* ********************************* */ /* Edit main toolbox */ /* ********************************* */ +.umap-main-edit-toolbox [type="button"] { + color: #fff; + font-size: 1.2em; + border: none; + background-color: #323737; + width: auto; + margin-bottom: 0; +} +.umap-main-edit-toolbox [type="button"]:hover { + text-decoration: underline; +} -.leaflet-container a.leaflet-control-edit-save, -.leaflet-container a.leaflet-control-edit-cancel, -.leaflet-container a.leaflet-control-edit-disable { +.leaflet-container .umap-help-link { + font-size: 12px; +} +.leaflet-container .leaflet-control-edit-save, +.leaflet-container .leaflet-control-edit-cancel, +.leaflet-container .leaflet-control-edit-disable { display: block; border: none; - font-size: 11px; - margin-left: 10px; - float: right; + font-size: 12px; border-radius: 20px; color: #f8f8f8; - width: auto; height: 36px; line-height: 36px; min-height: 36px; padding: 0 20px; - min-width: 100px; } -.leaflet-container a.leaflet-control-edit-disable:before, -.leaflet-container a.leaflet-control-edit-save:before, -.leaflet-container a.leaflet-control-edit-cancel:before { +.leaflet-container .leaflet-control-edit-disable:before, +.leaflet-container .leaflet-control-edit-save:before, +.leaflet-container .leaflet-control-edit-cancel:before { display: inline-block; width: 24px; height: 24px; @@ -453,37 +459,37 @@ ul.photon-autocomplete { vertical-align: middle; content: ' '; } -.leaflet-container a.leaflet-control-edit-save:before { +.leaflet-container .leaflet-control-edit-save:before { background-position: -2px -25px; } -.leaflet-container a.leaflet-control-edit-disable:before { +.leaflet-container .leaflet-control-edit-disable:before { background-position: -26px -1px; } -.leaflet-container a.leaflet-control-edit-cancel, -.leaflet-container a.leaflet-control-edit-disable { +.leaflet-container .leaflet-control-edit-cancel, +.leaflet-container .leaflet-control-edit-disable { border: 1px solid #555; } -.leaflet-container a.leaflet-control-edit-save { +.leaflet-container .leaflet-control-edit-save { opacity: 0.5; cursor: not-allowed; background-color: #215d9c; } -.umap-is-dirty a.leaflet-control-edit-save { +.umap-is-dirty .leaflet-control-edit-save { opacity: 1; cursor: pointer; } -.leaflet-container a.leaflet-control-edit-save, -.leaflet-container a.leaflet-control-edit-cancel, -.leaflet-container a.leaflet-control-edit-disable, +.leaflet-container .leaflet-control-edit-save, +.leaflet-container .leaflet-control-edit-cancel, +.leaflet-container .leaflet-control-edit-disable, .umap-edit-enabled .leaflet-control-edit-enable { display: none; } -.umap-edit-enabled a.leaflet-control-edit-save, -.umap-edit-enabled a.leaflet-control-edit-disable, -.umap-edit-enabled .umap-is-dirty a.leaflet-control-edit-cancel { +.umap-edit-enabled .leaflet-control-edit-save, +.umap-edit-enabled .leaflet-control-edit-disable, +.umap-edit-enabled .umap-is-dirty .leaflet-control-edit-cancel { display: inline-block; } -.umap-is-dirty a.leaflet-control-edit-disable { +.umap-is-dirty .leaflet-control-edit-disable { display: none; } .umap-caption-bar { @@ -506,7 +512,17 @@ ul.photon-autocomplete { opacity: 0.98; color: #fff; display: flex; + justify-content: space-between; } +.umap-left-edit-toolbox, +.umap-right-edit-toolbox { + display: flex; + column-gap: 10px; +} +.umap-right-edit-toolbox { + align-items: baseline; +} + .umap-main-edit-toolbox .logo { width: 39px; height: 100%; @@ -521,24 +537,21 @@ ul.photon-autocomplete { vertical-align: middle; text-indent: -9999px; } -.umap-main-edit-toolbox [type="button"] { - color: #fff; - font-size: 1.2em; - border: none; - background-color: #323737; -} -.umap-main-edit-toolbox [type="button"]:hover { - text-decoration: underline; -} .umap-main-edit-toolbox .map-name { display: inline-block; - max-width: 200px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; - margin-right: 5px; font-weight: bold; + text-align: left; } +.umap-main-edit-toolbox .share-status { + font-size: 1em; + font-style: italic; + overflow: hidden; + text-overflow: ellipsis; +} + .map-name:after { content: '\00a0'; padding-left: 3px; @@ -548,9 +561,6 @@ ul.photon-autocomplete { .umap-is-dirty .map-name:after { content: '*'; } -.umap-main-edit-toolbox .share-status { - margin: 0 20px; -} .umap-edit-enabled .umap-main-edit-toolbox { top: 0; } @@ -1532,14 +1542,11 @@ a.add-datalayer:hover, @media all and (max-width: 980px) { - .leaflet-container a.leaflet-control-edit-save, - .leaflet-container a.leaflet-control-edit-disable, - .leaflet-container a.leaflet-control-edit-cancel { - text-indent: calc(100% - 10px); - width: 35px; - min-width: initial; + .leaflet-container .leaflet-control-edit-save span, + .leaflet-container .leaflet-control-edit-disable span, + .leaflet-container .leaflet-control-edit-cancel span { + display: none; } - .umap-main-edit-toolbox .umap-help-button { display: none; } @@ -1549,7 +1556,11 @@ a.add-datalayer:hover, .umap-main-edit-toolbox .umap-user:after { display: none; } - +} +@media all and (max-width: 640px) { + .umap-main-edit-toolbox .map-name { + max-width: 150px; + } } @media all and (max-width: 480px) { diff --git a/umap/static/umap/test/_pre.js b/umap/static/umap/test/_pre.js index dda0c79e..836a3c91 100644 --- a/umap/static/umap/test/_pre.js +++ b/umap/static/umap/test/_pre.js @@ -47,10 +47,10 @@ var enableEdit = function () { happen.click(qs('div.leaflet-control-edit-enable a')) } var disableEdit = function () { - happen.click(qs('a.leaflet-control-edit-disable')) + happen.click(qs('.leaflet-control-edit-disable')) } var clickSave = function () { - happen.click(qs('a.leaflet-control-edit-save')) + happen.click(qs('.leaflet-control-edit-save')) } var clickCancel = function () { var _confirm = window.confirm From 222af1b52871f5a86b1c3c25fd3e30e4a1f6835a Mon Sep 17 00:00:00 2001 From: David Larlet Date: Mon, 16 Oct 2023 15:37:09 -0400 Subject: [PATCH 06/16] =?UTF-8?q?Header=E2=80=99s=20styles=20for=20logged?= =?UTF-8?q?=20in=20user?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- umap/static/umap/map.css | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css index 71d8c584..0dbf44a8 100644 --- a/umap/static/umap/map.css +++ b/umap/static/umap/map.css @@ -572,12 +572,15 @@ ul.photon-autocomplete { display: inline; } .umap-main-edit-toolbox .umap-user { - margin-right: 20px; - float: right; + color: #fff; +} +.umap-main-edit-toolbox .umap-user:hover { + text-decoration: underline; } .umap-main-edit-toolbox .umap-user:after { content: '|'; padding-left: 20px; + display: inline-block; /* Prevents underline on hover. */ } .umap-edit-enabled .leaflet-top { top: 48px; From 740ff90262fcc81a36ab621e425b52da29df3be9 Mon Sep 17 00:00:00 2001 From: David Larlet Date: Mon, 16 Oct 2023 15:37:25 -0400 Subject: [PATCH 07/16] Keep save button in blue on hover --- umap/static/umap/map.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css index 0dbf44a8..235fe2d6 100644 --- a/umap/static/umap/map.css +++ b/umap/static/umap/map.css @@ -478,6 +478,9 @@ ul.photon-autocomplete { opacity: 1; cursor: pointer; } +.umap-is-dirty .leaflet-control-edit-save:hover { + background-color: #215d9c; +} .leaflet-container .leaflet-control-edit-save, .leaflet-container .leaflet-control-edit-cancel, .leaflet-container .leaflet-control-edit-disable, From 8656ca305990e3bac6e1c97041b0d5a5ffb6d8c1 Mon Sep 17 00:00:00 2001 From: David Larlet Date: Mon, 16 Oct 2023 15:46:11 -0400 Subject: [PATCH 08/16] Deal with browsable layers from left sidebar --- umap/static/umap/map.css | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css index 235fe2d6..5a8532e2 100644 --- a/umap/static/umap/map.css +++ b/umap/static/umap/map.css @@ -848,13 +848,14 @@ ul.photon-autocomplete { .leaflet-control-browse .umap-browse-actions .off .layer-title { color: rgb(179, 179, 179); } -.leaflet-control-browse.expanded > a { +.leaflet-control-browse.expanded > a, +.leaflet-control-browse.expanded > button { display: none; } .leaflet-control-browse.expanded .umap-browse-actions { display: block; } -.leaflet-control-browse a.umap-browse-link { +.leaflet-control-browse .umap-browse-link { background-image: none; background-color: rgb(68, 68, 68); color: white; @@ -869,7 +870,7 @@ ul.photon-autocomplete { border-radius: 2px; } a.add-datalayer:before, -.leaflet-control-browse a.umap-browse-link:before { +.leaflet-control-browse .umap-browse-link:before { background-image: url('./img/16.svg'); background-repeat: no-repeat; background-position: -45px -96px; @@ -883,7 +884,7 @@ a.add-datalayer:before { background-position: -45px -45px; } a.add-datalayer:hover, -.leaflet-control-browse a.umap-browse-link:hover { +.leaflet-control-browse .umap-browse-link:hover { background-color: rgb(99, 99, 99); } .umap-browse-data .off .feature { From 85c1af2b5093af5a2a35c44e50550f5c712b01b9 Mon Sep 17 00:00:00 2001 From: David Larlet Date: Mon, 16 Oct 2023 16:19:03 -0400 Subject: [PATCH 09/16] Use the createLink function when pertinent --- umap/static/umap/js/umap.controls.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js index e8c89ba5..68e2030f 100644 --- a/umap/static/umap/js/umap.controls.js +++ b/umap/static/umap/js/umap.controls.js @@ -1347,15 +1347,14 @@ L.U.AttributionControl = L.Control.Attribution.extend({ L.DomUtil.createLink('', container, ` — ${L._('Home')}`, '/') } if (this._map.options.captionMenus) { - const poweredBy = L.DomUtil.add( - 'a', + L.DomUtil.createLink( '', container, - ` — ${L._('Powered by uMap')}` + ` — ${L._('Powered by uMap')}`, + 'https://github.com/umap-project/umap/' ) - poweredBy.href = 'https://github.com/umap-project/umap/' } - L.DomUtil.create('a', 'attribution-toggle', this._container) + L.DomUtil.createLink('attribution-toggle', this._container, '') }, }) From 1d94ebbaa3c46e141a06d8f850b72fa9057b5fba Mon Sep 17 00:00:00 2001 From: David Larlet Date: Mon, 16 Oct 2023 16:28:41 -0400 Subject: [PATCH 10/16] Responsiveness of the header when logged in --- umap/static/umap/js/umap.controls.js | 6 ++++-- umap/static/umap/map.css | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js index 68e2030f..9ba1e37d 100644 --- a/umap/static/umap/js/umap.controls.js +++ b/umap/static/umap/js/umap.controls.js @@ -946,7 +946,9 @@ L.U.Map.include({ L.DomUtil.createLink( 'umap-user', rightContainer, - L._(`My Dashboard ({username})`, { username: this.options.user.name }), + L._(`My Dashboard ({username})`, { + username: this.options.user.name, + }), this.options.user.url ) } @@ -954,7 +956,7 @@ L.U.Map.include({ const controlEditCancel = L.DomUtil.createButton( 'leaflet-control-edit-cancel', rightContainer, - L._('Cancel edits'), + L.DomUtil.add('span', '', null, L._('Cancel edits')), this.askForReset, this ) diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css index 5a8532e2..cf9bd419 100644 --- a/umap/static/umap/map.css +++ b/umap/static/umap/map.css @@ -1549,6 +1549,7 @@ a.add-datalayer:hover, @media all and (max-width: 980px) { + .umap-main-edit-toolbox .umap-user span, .leaflet-container .leaflet-control-edit-save span, .leaflet-container .leaflet-control-edit-disable span, .leaflet-container .leaflet-control-edit-cancel span { @@ -1565,6 +1566,10 @@ a.add-datalayer:hover, } } @media all and (max-width: 640px) { + .umap-main-edit-toolbox .umap-user { + display: none; + } + .umap-main-edit-toolbox .map-name { max-width: 150px; } From c5a28755a2b0554273c93ea99b80f3369a46b307 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Fri, 27 Oct 2023 12:55:24 +0200 Subject: [PATCH 11/16] Fix JS tests related to a11y a to button change --- umap/static/umap/test/Choropleth.js | 2 +- umap/static/umap/test/Controls.js | 2 +- umap/static/umap/test/DataLayer.js | 2 +- umap/static/umap/test/Map.js | 6 +++--- umap/static/umap/test/_pre.js | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/umap/static/umap/test/Choropleth.js b/umap/static/umap/test/Choropleth.js index f87e7cce..1cd8c2ec 100644 --- a/umap/static/umap/test/Choropleth.js +++ b/umap/static/umap/test/Choropleth.js @@ -205,7 +205,7 @@ describe('L.U.Choropleth', function () { }) after(function () { this.server.restore() - //resetMap() + resetMap() }) describe('#init()', function () { diff --git a/umap/static/umap/test/Controls.js b/umap/static/umap/test/Controls.js index 5541376b..00616316 100644 --- a/umap/static/umap/test/Controls.js +++ b/umap/static/umap/test/Controls.js @@ -78,7 +78,7 @@ describe('L.U.Controls', function () { describe('#exportPanel()', function () { it('should be opened at datalayer button click', function () { - let button = qs('.leaflet-control-embed a') + let button = qs('.leaflet-control-embed button') assert.ok(button) happen.click(button) assert.ok(qs('#umap-ui-container .umap-share')) diff --git a/umap/static/umap/test/DataLayer.js b/umap/static/umap/test/DataLayer.js index c2094f90..3c545ff8 100644 --- a/umap/static/umap/test/DataLayer.js +++ b/umap/static/umap/test/DataLayer.js @@ -341,7 +341,7 @@ describe('L.U.DataLayer', function () { happen.click( qs('#browse_data_toggle_' + L.stamp(this.datalayer) + ' .layer-edit') ) - deleteLink = qs('a.delete_datalayer_button') + deleteLink = qs('button.delete_datalayer_button') assert.ok(deleteLink) }) diff --git a/umap/static/umap/test/Map.js b/umap/static/umap/test/Map.js index 9bcc7cfb..12b64d73 100644 --- a/umap/static/umap/test/Map.js +++ b/umap/static/umap/test/Map.js @@ -50,7 +50,7 @@ describe('L.U.Map', function () { }) it('enable edit on click on toggle button', function () { - var el = qs('div.leaflet-control-edit-enable a') + var el = qs('div.leaflet-control-edit-enable button') happen.click(el) assert.isTrue(L.DomUtil.hasClass(document.body, 'umap-edit-enabled')) }) @@ -109,7 +109,7 @@ describe('L.U.Map', function () { var button = qs('a.update-map-settings') assert.ok(button, 'update map info button exists') happen.click(button) - var deleteLink = qs('a.umap-delete') + var deleteLink = qs('button.umap-delete') assert.ok(deleteLink, 'delete map button exists') sinon.spy(window, 'confirm') this.server.respondWith('POST', path, JSON.stringify({ redirect: '#' })) @@ -301,7 +301,7 @@ describe('L.U.Map', function () { }) it('should update title bar (umap format import)', function () { - var title = qs('#map div.umap-main-edit-toolbox a.map-name') + var title = qs('#map div.umap-main-edit-toolbox button.map-name') assert.equal(title.innerHTML, 'Imported map') }) diff --git a/umap/static/umap/test/_pre.js b/umap/static/umap/test/_pre.js index 836a3c91..5df2e152 100644 --- a/umap/static/umap/test/_pre.js +++ b/umap/static/umap/test/_pre.js @@ -44,7 +44,7 @@ var resetMap = function () { document.body.className = '' } var enableEdit = function () { - happen.click(qs('div.leaflet-control-edit-enable a')) + happen.click(qs('div.leaflet-control-edit-enable button')) } var disableEdit = function () { happen.click(qs('.leaflet-control-edit-disable')) @@ -57,7 +57,7 @@ var clickCancel = function () { window.confirm = function (text) { return true } - happen.click(qs('a.leaflet-control-edit-cancel')) + happen.click(qs('button.leaflet-control-edit-cancel')) happen.once(document.body, { type: 'keypress', keyCode: 13 }) window.confirm = _confirm } From 2f424b753962311bfc94538fb1006f4201cbecca Mon Sep 17 00:00:00 2001 From: David Larlet Date: Fri, 27 Oct 2023 10:35:26 -0400 Subject: [PATCH 12/16] Flat button for icon shapes direct input --- umap/static/umap/base.css | 9 +++++++++ umap/static/umap/js/umap.forms.js | 21 ++++++++++++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/umap/static/umap/base.css b/umap/static/umap/base.css index 85fa2fbc..29bcac1d 100644 --- a/umap/static/umap/base.css +++ b/umap/static/umap/base.css @@ -193,6 +193,15 @@ input[type="submit"] { .dark a { color: #eeeeec; } +[type="button"].flat, +.dark [type="button"].flat { + border: none; + background-color: inherit; + padding: 0; + text-align: left; + min-height: inherit; + text-decoration: underline; +} .help-text, .helptext { display: block; padding: 7px 7px; diff --git a/umap/static/umap/js/umap.forms.js b/umap/static/umap/js/umap.forms.js index 1f8598ac..23efdb60 100644 --- a/umap/static/umap/js/umap.forms.js +++ b/umap/static/umap/js/umap.forms.js @@ -397,7 +397,11 @@ L.FormBuilder.DataLayerSwitcher = L.FormBuilder.Select.extend({ getOptions: function () { const options = [] this.builder.map.eachDataLayerReverse((datalayer) => { - if (datalayer.isLoaded() && !datalayer.isDataReadOnly() && datalayer.canBrowse()) { + if ( + datalayer.isLoaded() && + !datalayer.isDataReadOnly() && + datalayer.canBrowse() + ) { options.push([L.stamp(datalayer), datalayer.getName()]) } }) @@ -628,7 +632,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({ closeButton.style.clear = 'both' const customButton = L.DomUtil.createButton( - '', + 'flat', this.pictogramsContainer, L._('Toggle direct input (advanced)'), function (e) { @@ -728,7 +732,10 @@ L.FormBuilder.MultiChoice = L.FormBuilder.Element.extend({ fetch: function () { let value = (this.backup = this.toHTML()) if (!this.container.querySelector(`input[type="radio"][value="${value}"]`)) - value = typeof(this.options.default) !== 'undefined' ? this.options.default : this.default + value = + typeof this.options.default !== 'undefined' + ? this.options.default + : this.default this.container.querySelector(`input[type="radio"][value="${value}"]`).checked = true }, @@ -838,7 +845,9 @@ L.FormBuilder.Range = L.FormBuilder.FloatInput.extend({ }, value: function () { - return L.DomUtil.hasClass(this.wrapper, 'undefined') ? undefined : L.FormBuilder.FloatInput.prototype.value.call(this) + return L.DomUtil.hasClass(this.wrapper, 'undefined') + ? undefined + : L.FormBuilder.FloatInput.prototype.value.call(this) }, buildHelpText: function () { @@ -853,7 +862,9 @@ L.FormBuilder.Range = L.FormBuilder.FloatInput.extend({ const step = this.options.step || 1, digits = step < 1 ? 2 : 0 for (let i = this.options.min; i <= this.options.max; i += this.options.step) { - options += `` + options += `` } datalist.innerHTML = options L.FormBuilder.Input.prototype.buildHelpText.call(this) From 1d65ce6b49f30edd8eebb23e7382b3c04c5456d3 Mon Sep 17 00:00:00 2001 From: David Larlet Date: Fri, 27 Oct 2023 11:31:34 -0400 Subject: [PATCH 13/16] Better labels and icons for edit/preview modes Fix #556 --- umap/static/umap/js/umap.controls.js | 12 ++++++------ umap/static/umap/map.css | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js index 9ba1e37d..6bb4c27a 100644 --- a/umap/static/umap/js/umap.controls.js +++ b/umap/static/umap/js/umap.controls.js @@ -323,7 +323,7 @@ L.U.EditControl = L.Control.extend({ const enableEditing = L.DomUtil.createButton( '', container, - L._('Enable editing'), + L._('Edit'), map.enableEdit, map ) @@ -332,7 +332,7 @@ L.U.EditControl = L.Control.extend({ 'mouseover', function () { map.ui.tooltip({ - content: `${L._('Edit map')} (Ctrl+E)`, + content: `${L._('Switch to edit mode')} (Ctrl+E)`, anchor: enableEditing, position: 'bottom', delay: 750, @@ -977,7 +977,7 @@ L.U.Map.include({ const controlEditDisable = L.DomUtil.createButton( 'leaflet-control-edit-disable', rightContainer, - L.DomUtil.add('span', '', null, L._('Disable editing')), + L.DomUtil.add('span', '', null, L._('View')), function (e) { this.disableEdit(e) this.ui.closePanel() @@ -989,7 +989,7 @@ L.U.Map.include({ 'mouseover', function () { this.ui.tooltip({ - content: `${L._('Disable editing')} (Ctrl+E)`, + content: `${L._('Back to preview')} (Ctrl+E)`, anchor: controlEditDisable, position: 'bottom', delay: 500, @@ -1001,7 +1001,7 @@ L.U.Map.include({ const controlEditSave = L.DomUtil.createButton( 'leaflet-control-edit-save button', rightContainer, - L.DomUtil.add('span', '', null, L._('Save current edits')), + L.DomUtil.add('span', '', null, L._('Save')), this.save, this ) @@ -1010,7 +1010,7 @@ L.U.Map.include({ 'mouseover', function () { this.ui.tooltip({ - content: `${L._('Save')} (Ctrl+S)`, + content: `${L._('Save current edits')} (Ctrl+S)`, anchor: controlEditSave, position: 'bottom', delay: 500, diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css index cf9bd419..42978a00 100644 --- a/umap/static/umap/map.css +++ b/umap/static/umap/map.css @@ -463,7 +463,7 @@ ul.photon-autocomplete { background-position: -2px -25px; } .leaflet-container .leaflet-control-edit-disable:before { - background-position: -26px -1px; + background-position: -52px -25px; } .leaflet-container .leaflet-control-edit-cancel, .leaflet-container .leaflet-control-edit-disable { From 860e4752194f831cff90f9202e3a341921664b67 Mon Sep 17 00:00:00 2001 From: David Larlet Date: Fri, 27 Oct 2023 11:32:03 -0400 Subject: [PATCH 14/16] Center the tooltip (arrow) for bottom position --- umap/static/umap/base.css | 1 - umap/static/umap/js/umap.ui.js | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/umap/static/umap/base.css b/umap/static/umap/base.css index 29bcac1d..6b928336 100644 --- a/umap/static/umap/base.css +++ b/umap/static/umap/base.css @@ -792,7 +792,6 @@ input:invalid { pointer-events: none; border-top-color: rgba(30, 30, 30, 0.7); border-width: 11px; - margin-left: calc(-50% + 21px); transform: rotate(180deg); } #umap-tooltip-container.tooltip.tooltip-left:after { diff --git a/umap/static/umap/js/umap.ui.js b/umap/static/umap/js/umap.ui.js index c8e9133c..30386915 100644 --- a/umap/static/umap/js/umap.ui.js +++ b/umap/static/umap/js/umap.ui.js @@ -185,8 +185,8 @@ L.U.UI = L.Evented.extend({ this._tooltip.className = 'tooltip-bottom' const coords = this.getPosition(el) this.setTooltipPosition({ - left: coords.left + 30, - bottom: this.getDocHeight() - coords.top - 76, + left: coords.left, + top: coords.bottom + 11, }) }, From ff9b462531fb6eb8812e37b0b81ede3c931fd580 Mon Sep 17 00:00:00 2001 From: David Larlet Date: Fri, 27 Oct 2023 11:40:28 -0400 Subject: [PATCH 15/16] All left sidebar icons with white background --- umap/static/umap/base.css | 1 + umap/static/umap/map.css | 1 + 2 files changed, 2 insertions(+) diff --git a/umap/static/umap/base.css b/umap/static/umap/base.css index 6b928336..cdf007e5 100644 --- a/umap/static/umap/base.css +++ b/umap/static/umap/base.css @@ -178,6 +178,7 @@ input[type="submit"] { line-height: 32px; border: none; text-decoration: none; + background-color: white; } .dark .button, .dark [type="button"] { diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css index 42978a00..77e9d665 100644 --- a/umap/static/umap/map.css +++ b/umap/static/umap/map.css @@ -326,6 +326,7 @@ ul.photon-autocomplete { border: 1px solid #bbb; border-radius: 4px; box-shadow: none; + background-color: white; } .leaflet-measure-control a { background-position: 0 -72px; From 312ebf5a0d2ded529cefffec0951e78ed3ef301e Mon Sep 17 00:00:00 2001 From: David Larlet Date: Fri, 27 Oct 2023 11:47:33 -0400 Subject: [PATCH 16/16] Remove JS inline style made useless --- umap/static/umap/js/umap.forms.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/umap/static/umap/js/umap.forms.js b/umap/static/umap/js/umap.forms.js index 23efdb60..c20e4f41 100644 --- a/umap/static/umap/js/umap.forms.js +++ b/umap/static/umap/js/umap.forms.js @@ -640,8 +640,6 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({ }, this ) - customButton.style.display = 'block' - customButton.style.clear = 'both' this.builder.map.help.button(customButton, 'formatIconSymbol') },