diff --git a/umap/static/umap/base.css b/umap/static/umap/base.css index 6c48da914..2c11c5a1d 100644 --- a/umap/static/umap/base.css +++ b/umap/static/umap/base.css @@ -756,70 +756,6 @@ input[type=hidden].blur + [type="button"] { } -/* *********** */ -/* Tooltip */ -/* *********** */ -#umap-tooltip-container { - line-height: 20px; - padding: 5px 10px; - width: auto; - position: absolute; - box-shadow: 0 1px 7px #999999; - display: none; - background-color: rgba(40, 40, 40, 0.8); - color: #eeeeec; - font-size: 0.8em; - border-radius: 2px; - z-index: 1011; - font-weight: normal; - max-width: 300px; -} -.umap-tooltip #umap-tooltip-container { - display: block; -} -#umap-tooltip-container.tooltip-top:after { - top: 100%; - 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.8); - 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; - transform: rotate(180deg); -} -#umap-tooltip-container.tooltip.tooltip-left:after { - left: 100%; - top: 50%; - border: solid transparent; - content: " "; - height: 0; - width: 0; - position: absolute; - pointer-events: none; - border-color: rgba(136, 183, 213, 0); - border-left-color: #333; - border-width: 11px; - margin-top: -10px; -} - - - /* *********** */ /* Close link */ /* *********** */ diff --git a/umap/static/umap/css/tooltip.css b/umap/static/umap/css/tooltip.css new file mode 100644 index 000000000..caa475eb0 --- /dev/null +++ b/umap/static/umap/css/tooltip.css @@ -0,0 +1,59 @@ +#umap-tooltip-container { + line-height: 20px; + padding: 5px 10px; + width: auto; + position: absolute; + box-shadow: 0 1px 7px #999999; + display: none; + background-color: rgba(40, 40, 40, 0.8); + color: #eeeeec; + font-size: 0.8em; + border-radius: 2px; + z-index: 1011; + font-weight: normal; + max-width: 300px; +} +.umap-tooltip #umap-tooltip-container { + display: block; +} +#umap-tooltip-container.tooltip-top:after { + top: 100%; + 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.8); + 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; + transform: rotate(180deg); +} +#umap-tooltip-container.tooltip.tooltip-left:after { + left: 100%; + top: 50%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-color: rgba(136, 183, 213, 0); + border-left-color: #333; + border-width: 11px; + margin-top: -10px; +} + diff --git a/umap/static/umap/js/modules/global.js b/umap/static/umap/js/modules/global.js index 83bc9b1f3..924854f9f 100644 --- a/umap/static/umap/js/modules/global.js +++ b/umap/static/umap/js/modules/global.js @@ -4,6 +4,7 @@ import Facets from './facets.js' import Caption from './caption.js' import { Panel, EditPanel, FullPanel } from './panel.js' import Alert from './ui/alert.js' +import Tooltip from './ui/tooltip.js' import * as Utils from './utils.js' import { SCHEMA } from './schema.js' import { Request, ServerRequest, RequestError, HTTPError, NOKError } from './request.js' @@ -23,6 +24,7 @@ window.U = { Facets, Panel, Alert, + Tooltip, EditPanel, FullPanel, Utils, diff --git a/umap/static/umap/js/modules/ui/tooltip.js b/umap/static/umap/js/modules/ui/tooltip.js new file mode 100644 index 000000000..35f83ed77 --- /dev/null +++ b/umap/static/umap/js/modules/ui/tooltip.js @@ -0,0 +1,117 @@ +import { DomUtil, DomEvent } from '../../../vendors/leaflet/leaflet-src.esm.js' +import { translate } from '../i18n.js' + +export default class Tooltip { + constructor(map) { + this.parent = map._controlContainer + this.map = map + this.container = DomUtil.create('div', 'with-transition', this.parent) + this.container.id = 'umap-tooltip-container' + DomEvent.disableClickPropagation(this.container) + DomEvent.on(this.container, 'contextmenu', DomEvent.stopPropagation) // Do not activate our custom context menu. + DomEvent.on(this.container, 'wheel', DomEvent.stopPropagation) + DomEvent.on(this.container, 'MozMousePixelScroll', DomEvent.stopPropagation) + } + + open(opts) { + function showIt() { + if (opts.anchor && opts.position === 'top') { + this.anchorTop(opts.anchor) + } else if (opts.anchor && opts.position === 'left') { + this.anchorLeft(opts.anchor) + } else if (opts.anchor && opts.position === 'bottom') { + this.anchorBottom(opts.anchor) + } else { + this.anchorAbsolute() + } + L.DomUtil.addClass(this.parent, 'umap-tooltip') + this.container.innerHTML = U.Utils.escapeHTML(opts.content) + } + this.TOOLTIP_ID = window.setTimeout(L.bind(showIt, this), opts.delay || 0) + const id = this.TOOLTIP_ID + const closeIt = () => { + this.close(id) + } + if (opts.anchor) { + L.DomEvent.once(opts.anchor, 'mouseout', closeIt) + } + if (opts.duration !== Infinity) { + window.setTimeout(closeIt, opts.duration || 3000) + } + } + + anchorAbsolute() { + this.container.className = '' + const left = + this.parent.offsetLeft + + this.parent.clientWidth / 2 - + this.container.clientWidth / 2, + top = this.parent.offsetTop + 75 + this.setPosition({ top: top, left: left }) + } + + anchorTop(el) { + this.container.className = 'tooltip-top' + const coords = this.getPosition(el) + this.setPosition({ + left: coords.left - 10, + bottom: this.getDocHeight() - coords.top + 11, + }) + } + + anchorBottom(el) { + this.container.className = 'tooltip-bottom' + const coords = this.getPosition(el) + this.setPosition({ + left: coords.left, + top: coords.bottom + 11, + }) + } + + anchorLeft(el) { + this.container.className = 'tooltip-left' + const coords = this.getPosition(el) + this.setPosition({ + top: coords.top, + right: document.documentElement.offsetWidth - coords.left + 11, + }) + } + + close(id) { + // Clear timetout even if a new tooltip has been added + // in the meantime. Eg. after a mouseout from the anchor. + window.clearTimeout(id) + if (id && id !== this.TOOLTIP_ID) return + this.container.className = '' + this.container.innerHTML = '' + this.setPosition({}) + L.DomUtil.removeClass(this.parent, 'umap-tooltip') + } + + getPosition(el) { + return el.getBoundingClientRect() + } + + setPosition(coords) { + if (coords.left) this.container.style.left = `${coords.left}px` + else this.container.style.left = 'initial' + if (coords.right) this.container.style.right = `${coords.right}px` + else this.container.style.right = 'initial' + if (coords.top) this.container.style.top = `${coords.top}px` + else this.container.style.top = 'initial' + if (coords.bottom) this.container.style.bottom = `${coords.bottom}px` + else this.container.style.bottom = 'initial' + } + + getDocHeight() { + const D = document + return Math.max( + D.body.scrollHeight, + D.documentElement.scrollHeight, + D.body.offsetHeight, + D.documentElement.offsetHeight, + D.body.clientHeight, + D.documentElement.clientHeight + ) + } +} diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js index f7f09711d..4ae4ffbc7 100644 --- a/umap/static/umap/js/umap.controls.js +++ b/umap/static/umap/js/umap.controls.js @@ -394,7 +394,7 @@ U.EditControl = L.Control.extend({ enableEditing, 'mouseover', function () { - map.ui.tooltip({ + map.tooltip.open({ content: map.help.displayLabel('TOGGLE_EDIT'), anchor: enableEditing, position: 'bottom', @@ -693,7 +693,7 @@ const ControlsMixin = { nameButton, 'mouseover', function () { - this.ui.tooltip({ + this.tooltip.open({ content: L._('Edit the title of the map'), anchor: nameButton, position: 'bottom', @@ -714,7 +714,7 @@ const ControlsMixin = { shareStatusButton, 'mouseover', function () { - this.ui.tooltip({ + this.tooltip.open({ content: L._('Update who can see and edit the map'), anchor: shareStatusButton, position: 'bottom', @@ -763,7 +763,7 @@ const ControlsMixin = { controlEditCancel, 'mouseover', function () { - this.ui.tooltip({ + this.tooltip.open({ content: this.help.displayLabel('CANCEL'), anchor: controlEditCancel, position: 'bottom', @@ -784,7 +784,7 @@ const ControlsMixin = { controlEditDisable, 'mouseover', function () { - this.ui.tooltip({ + this.tooltip.open({ content: this.help.displayLabel('PREVIEW'), anchor: controlEditDisable, position: 'bottom', @@ -805,7 +805,7 @@ const ControlsMixin = { controlEditSave, 'mouseover', function () { - this.ui.tooltip({ + this.tooltip.open({ content: this.help.displayLabel('SAVE'), anchor: controlEditSave, position: 'bottom', @@ -1249,7 +1249,7 @@ U.Editable = L.Editable.extend({ L.Editable.prototype.initialize.call(this, map, options) this.on('editable:drawing:click editable:drawing:move', this.drawingTooltip) this.on('editable:drawing:end', (e) => { - this.closeTooltip() + this.map.tooltip.close() // Leaflet.Editable will delete the drawn shape if invalid // (eg. line has only one drawn point) // So let's check if the layer has no more shape @@ -1313,7 +1313,7 @@ U.Editable = L.Editable.extend({ drawingTooltip: function (e) { if (e.layer instanceof L.Marker && e.type == 'editable:drawing:start') { - this.map.ui.tooltip({ content: L._('Click to add a marker') }) + this.map.tooltip.open({ content: L._('Click to add a marker') }) } if (!(e.layer instanceof L.Polyline)) { // only continue with Polylines and Polygons @@ -1356,7 +1356,7 @@ U.Editable = L.Editable.extend({ } } if (content) { - this.map.ui.tooltip({ content: content }) + this.map.tooltip.open({ content: content }) } }, diff --git a/umap/static/umap/js/umap.features.js b/umap/static/umap/js/umap.features.js index 3ab590b82..27c835e7e 100644 --- a/umap/static/umap/js/umap.features.js +++ b/umap/static/umap/js/umap.features.js @@ -878,9 +878,9 @@ U.PathMixin = { _onMouseOver: function () { if (this.map.measureTools && this.map.measureTools.enabled()) { - this.map.ui.tooltip({ content: this.getMeasure(), anchor: this }) + this.map.tooltip.open({ content: this.getMeasure(), anchor: this }) } else if (this.map.editEnabled && !this.map.editedFeature) { - this.map.ui.tooltip({ content: L._('Click to edit'), anchor: this }) + this.map.tooltip.open({ content: L._('Click to edit'), anchor: this }) } }, diff --git a/umap/static/umap/js/umap.forms.js b/umap/static/umap/js/umap.forms.js index c52ad146b..4915dfb31 100644 --- a/umap/static/umap/js/umap.forms.js +++ b/umap/static/umap/js/umap.forms.js @@ -78,7 +78,7 @@ L.FormBuilder.Element.include({ info, 'mouseover', function () { - this.builder.map.ui.tooltip({ + this.builder.map.tooltip.open({ anchor: info, content: this.options.helpTooltip, position: 'top', diff --git a/umap/static/umap/js/umap.js b/umap/static/umap/js/umap.js index c6c01278e..4d7eb4229 100644 --- a/umap/static/umap/js/umap.js +++ b/umap/static/umap/js/umap.js @@ -58,11 +58,11 @@ U.Map = L.Map.extend({ this.panel = new U.Panel(this) this.alert = new U.Alert(this) + this.tooltip = new U.Tooltip(this) if (this.hasEditMode()) { this.editPanel = new U.EditPanel(this) this.fullPanel = new U.FullPanel(this) } - this.ui = new U.UI(this._container) L.DomEvent.on(document.body, 'dataloading', (e) => this.fire('dataloading', e)) L.DomEvent.on(document.body, 'dataload', (e) => this.fire('dataload', e)) this.server = new U.ServerRequest(this.alert) diff --git a/umap/static/umap/js/umap.ui.js b/umap/static/umap/js/umap.ui.js deleted file mode 100644 index 9e0a2f4e5..000000000 --- a/umap/static/umap/js/umap.ui.js +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Modals - */ -U.UI = L.Evented.extend({ - TOOLTIP_ID: null, - - initialize: function (parent) { - this.parent = parent - this.container = L.DomUtil.create('div', 'leaflet-ui-container', this.parent) - L.DomEvent.disableClickPropagation(this.container) - L.DomEvent.on(this.container, 'contextmenu', L.DomEvent.stopPropagation) // Do not activate our custom context menu. - L.DomEvent.on(this.container, 'wheel', L.DomEvent.stopPropagation) - L.DomEvent.on(this.container, 'MozMousePixelScroll', L.DomEvent.stopPropagation) - this._tooltip = L.DomUtil.create('div', '', this.container) - this._tooltip.id = 'umap-tooltip-container' - }, - - tooltip: function (opts) { - 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 = U.Utils.escapeHTML(opts.content) - } - this.TOOLTIP_ID = window.setTimeout(L.bind(showIt, this), opts.delay || 0) - const id = this.TOOLTIP_ID - function closeIt() { - this.closeTooltip(id) - } - 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 () { - this._tooltip.className = '' - const left = - this.parent.offsetLeft + - this.parent.clientWidth / 2 - - this._tooltip.clientWidth / 2, - top = this.parent.offsetTop + 75 - this.setTooltipPosition({ top: top, left: left }) - }, - - anchorTooltipTop: function (el) { - this._tooltip.className = 'tooltip-top' - const coords = this.getPosition(el) - this.setTooltipPosition({ - left: coords.left - 10, - bottom: this.getDocHeight() - coords.top + 11, - }) - }, - - anchorTooltipBottom: function (el) { - this._tooltip.className = 'tooltip-bottom' - const coords = this.getPosition(el) - this.setTooltipPosition({ - left: coords.left, - top: coords.bottom + 11, - }) - }, - - anchorTooltipLeft: function (el) { - this._tooltip.className = 'tooltip-left' - const coords = this.getPosition(el) - this.setTooltipPosition({ - top: coords.top, - right: document.documentElement.offsetWidth - coords.left + 11, - }) - }, - - closeTooltip: function (id) { - // Clear timetout even if a new tooltip has been added - // in the meantime. Eg. after a mouseout from the anchor. - window.clearTimeout(id) - if (id && id !== this.TOOLTIP_ID) return - this._tooltip.className = '' - this._tooltip.innerHTML = '' - this.setTooltipPosition({}) - L.DomUtil.removeClass(this.parent, 'umap-tooltip') - }, - - getPosition: function (el) { - return el.getBoundingClientRect() - }, - - setTooltipPosition: function (coords) { - if (coords.left) this._tooltip.style.left = `${coords.left}px` - else this._tooltip.style.left = 'initial' - if (coords.right) this._tooltip.style.right = `${coords.right}px` - else this._tooltip.style.right = 'initial' - if (coords.top) this._tooltip.style.top = `${coords.top}px` - else this._tooltip.style.top = 'initial' - if (coords.bottom) this._tooltip.style.bottom = `${coords.bottom}px` - else this._tooltip.style.bottom = 'initial' - }, - - getDocHeight: function () { - const D = document - return Math.max( - D.body.scrollHeight, - D.documentElement.scrollHeight, - D.body.offsetHeight, - D.documentElement.offsetHeight, - D.body.clientHeight, - D.documentElement.clientHeight - ) - }, -}) diff --git a/umap/templates/umap/css.html b/umap/templates/umap/css.html index f9faa5167..4e12a22d3 100644 --- a/umap/templates/umap/css.html +++ b/umap/templates/umap/css.html @@ -30,4 +30,5 @@ + diff --git a/umap/templates/umap/js.html b/umap/templates/umap/js.html index 8c8c206e6..966ed8d2a 100644 --- a/umap/templates/umap/js.html +++ b/umap/templates/umap/js.html @@ -60,5 +60,4 @@ -