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