diff --git a/umap/static/umap/css/bar.css b/umap/static/umap/css/bar.css
index 7f90ad64..c95664dc 100644
--- a/umap/static/umap/css/bar.css
+++ b/umap/static/umap/css/bar.css
@@ -29,6 +29,11 @@
background-color: var(--color-lightCyan);
color: var(--color-dark);
}
+.dark [type=button].connected-peers:hover
+{
+ text-decoration: none;
+ border: 0.5px solid var(--color-brightCyan);
+}
.dark .off.connected-peers {
background-color: var(--color-lightGray);
color: var(--color-darkGray);
diff --git a/umap/static/umap/css/tooltip.css b/umap/static/umap/css/tooltip.css
index 6af26178..0a5e6d4d 100644
--- a/umap/static/umap/css/tooltip.css
+++ b/umap/static/umap/css/tooltip.css
@@ -1,59 +1,61 @@
-#umap-tooltip-container {
- line-height: 20px;
+.umap-tooltip-container {
padding: 5px 10px;
width: auto;
+ min-width: 100px;
+ max-width: 300px;
position: absolute;
box-shadow: var(--block-shadow);
display: none;
- background-color: rgba(40, 40, 40, 0.9);
color: #eeeeec;
- font-size: 0.8em;
- border-radius: 2px;
+ border-radius: var(--border-radius);
z-index: var(--zindex-tooltip);
font-weight: normal;
- max-width: 300px;
+ --tooltip-color: var(--color-darkGray);
+ background-color: var(--tooltip-color);
+ --arrow-size: 8px;
}
-.umap-tooltip #umap-tooltip-container {
+.tooltip-accent {
+ --tooltip-color: var(--color-lightCyan);
+}
+.umap-tooltip .umap-tooltip-container {
display: block;
}
-#umap-tooltip-container.tooltip-top:after {
+.umap-tooltip-container.tooltip-top:after {
top: 100%;
- left: calc(50% - 11px);
+ left: calc(50% - var(--arrow-size));
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);
+ border-top-color: var(--tooltip-color);
+ border-width: var(--arrow-size);
+ margin-left: calc(-50% + 2 * var(--arrow-size));
}
-#umap-tooltip-container.tooltip-bottom:before {
- top: -22px;
- left: calc(50% - 11px);
+.umap-tooltip-container.tooltip-bottom:before {
+ top: calc(var(--arrow-size) * -2);
+ left: calc(50% - var(--arrow-size));
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;
+ border-top-color: var(--tooltip-color);
+ border-width: var(--arrow-size);
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;
+.tooltip-accent ul {
+ padding-top: var(--small-box-padding);
+ padding-bottom: var(--small-box-padding);
+}
+.tooltip-accent li {
+ background-color: var(--color-light);
+ color: var(--color-dark);
+ padding: var(--small-box-padding);
+ margin-bottom: var(--small-box-padding);
+}
+.tooltip-accent li:last-of-type {
+ margin-bottom: 0;
}
-
diff --git a/umap/static/umap/js/modules/sync/engine.js b/umap/static/umap/js/modules/sync/engine.js
index 05994544..62106352 100644
--- a/umap/static/umap/js/modules/sync/engine.js
+++ b/umap/static/umap/js/modules/sync/engine.js
@@ -146,9 +146,8 @@ export class SyncEngine {
updater.applyMessage(operation)
}
- getNumberOfConnectedPeers() {
- if (this.peers) return Object.keys(this.peers).length
- return 0
+ getPeers() {
+ return this.peers || {}
}
/**
diff --git a/umap/static/umap/js/modules/ui/bar.js b/umap/static/umap/js/modules/ui/bar.js
index de446352..15e21b9c 100644
--- a/umap/static/umap/js/modules/ui/bar.js
+++ b/umap/static/umap/js/modules/ui/bar.js
@@ -2,6 +2,7 @@ import { DomEvent } from '../../../vendors/leaflet/leaflet-src.esm.js'
import { translate } from '../i18n.js'
import { WithTemplate } from '../utils.js'
import ContextMenu from './contextmenu.js'
+import * as Utils from '../utils.js'
const TOP_BAR_TEMPLATE = `
@@ -96,17 +97,22 @@ export class TopBar extends WithTemplate {
}
})
- const connectedPeers = this._umap.sync.getNumberOfConnectedPeers()
this.elements.peers.addEventListener('mouseover', () => {
- if (!connectedPeers) return
+ const connectedPeers = this._umap.sync.getPeers()
+ if (!Object.keys(connectedPeers).length) return
+ const ul = Utils.loadTemplate(
+ `
${Object.entries(connectedPeers)
+ .sort((el) => el !== this._umap.user?.name)
+ .map(([id, name]) => `- ${name || translate('Anonymous')}
`)
+ .join('')}
`
+ )
this._umap.tooltip.open({
- content: translate('{connectedPeers} peer(s) currently connected to this map', {
- connectedPeers: connectedPeers,
- }),
+ content: ul,
anchor: this.elements.peers,
position: 'bottom',
delay: 500,
duration: 5000,
+ accent: true,
})
})
diff --git a/umap/static/umap/js/modules/ui/base.js b/umap/static/umap/js/modules/ui/base.js
index 725e3c9b..db4aff9a 100644
--- a/umap/static/umap/js/modules/ui/base.js
+++ b/umap/static/umap/js/modules/ui/base.js
@@ -2,27 +2,18 @@ export class Positioned {
openAt({ anchor, position }) {
if (anchor && position === 'top') {
this.anchorTop(anchor)
- } else if (anchor && position === 'left') {
- this.anchorLeft(anchor)
} else if (anchor && position === 'bottom') {
this.anchorBottom(anchor)
- } else {
- this.anchorAbsolute()
}
}
- anchorAbsolute() {
- this.container.className = ''
- const left =
- this.parent.offsetLeft +
- this.parent.clientWidth / 2 -
- this.container.clientWidth / 2
- const top = this.parent.offsetTop + 75
- this.setPosition({ top: top, left: left })
+ toggleClassPosition(position) {
+ this.container.classList.toggle('tooltip-bottom', position === 'bottom')
+ this.container.classList.toggle('tooltip-top', position === 'top')
}
anchorTop(el) {
- this.container.className = 'tooltip-top'
+ this.toggleClassPosition('top')
const coords = this.getPosition(el)
this.setPosition({
left: coords.left - 10,
@@ -31,23 +22,15 @@ export class Positioned {
}
anchorBottom(el) {
- this.container.className = 'tooltip-bottom'
+ this.toggleClassPosition('bottom')
const coords = this.getPosition(el)
+ const selfCoords = this.getPosition(this.container)
this.setPosition({
- left: coords.left,
+ left: coords.left + coords.width / 2 - selfCoords.width / 2,
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,
- })
- }
-
getPosition(el) {
return el.getBoundingClientRect()
}
diff --git a/umap/static/umap/js/modules/ui/tooltip.js b/umap/static/umap/js/modules/ui/tooltip.js
index 88a859f9..2c4362e0 100644
--- a/umap/static/umap/js/modules/ui/tooltip.js
+++ b/umap/static/umap/js/modules/ui/tooltip.js
@@ -1,24 +1,32 @@
-import { DomEvent, DomUtil } from '../../../vendors/leaflet/leaflet-src.esm.js'
+import { DomEvent } from '../../../vendors/leaflet/leaflet-src.esm.js'
import { translate } from '../i18n.js'
import { Positioned } from './base.js'
+import * as Utils from '../utils.js'
export default class Tooltip extends Positioned {
constructor(parent) {
super()
this.parent = parent
- this.container = DomUtil.create('div', 'with-transition', this.parent)
- this.container.id = 'umap-tooltip-container'
+ this.container = Utils.loadTemplate('
')
+ this.parent.appendChild(this.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)
+ this.container.addEventListener('contextmenu', (event) => event.stopPropagation()) // Do not activate our custom context menu.
+ this.container.addEventListener('wheel', (event) => event.stopPropagation())
+ this.container.addEventListener('MozMousePixelScroll', (event) =>
+ event.stopPropagation()
+ )
}
open(opts) {
+ this.container.classList.toggle('tooltip-accent', Boolean(opts.accent))
const showIt = () => {
+ if (opts.content.nodeType === 1) {
+ this.container.appendChild(opts.content)
+ } else {
+ this.container.innerHTML = Utils.escapeHTML(opts.content)
+ }
+ this.parent.classList.add('umap-tooltip')
this.openAt(opts)
- 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
@@ -26,7 +34,7 @@ export default class Tooltip extends Positioned {
this.close(id)
}
if (opts.anchor) {
- L.DomEvent.once(opts.anchor, 'mouseout', closeIt)
+ opts.anchor.addEventListener('mouseout', closeIt, { once: true })
}
if (opts.duration !== Number.POSITIVE_INFINITY) {
window.setTimeout(closeIt, opts.duration || 3000)
@@ -38,9 +46,9 @@ export default class Tooltip extends Positioned {
// in the meantime. Eg. after a mouseout from the anchor.
window.clearTimeout(id)
if (id && id !== this.TOOLTIP_ID) return
- this.container.className = ''
+ this.toggleClassPosition()
this.container.innerHTML = ''
this.setPosition({})
- L.DomUtil.removeClass(this.parent, 'umap-tooltip')
+ this.parent.classList.remove('umap-tooltip')
}
}
diff --git a/umap/static/umap/js/modules/umap.js b/umap/static/umap/js/modules/umap.js
index abafa2f3..8594cd64 100644
--- a/umap/static/umap/js/modules/umap.js
+++ b/umap/static/umap/js/modules/umap.js
@@ -1357,7 +1357,7 @@ export default class Umap extends ServerStored {
numberOfConnectedPeers: () => {
Utils.eachElement('.connected-peers span', (el) => {
if (this.sync.websocketConnected) {
- el.textContent = this.sync.getNumberOfConnectedPeers()
+ el.textContent = Object.keys(this.sync.getPeers()).length
} else {
el.textContent = translate('Disconnected')
}
diff --git a/umap/static/umap/vars.css b/umap/static/umap/vars.css
index c294c0f5..effda252 100644
--- a/umap/static/umap/vars.css
+++ b/umap/static/umap/vars.css
@@ -39,6 +39,7 @@
--control-size: 36px;
--border-radius: 4px;
--box-padding: 20px;
+ --small-box-padding: 4px;
--box-margin: 14px;
--text-margin: 7px;
diff --git a/umap/tests/integration/test_draw_polygon.py b/umap/tests/integration/test_draw_polygon.py
index b58f91f7..0a5b05b1 100644
--- a/umap/tests/integration/test_draw_polygon.py
+++ b/umap/tests/integration/test_draw_polygon.py
@@ -484,12 +484,12 @@ def test_vertexmarker_not_shown_if_too_many(live_server, map, page, settings):
page.get_by_role("button", name="Import data", exact=True).click()
page.locator("path").click()
page.get_by_role("link", name="Toggle edit mode (⇧+Click)").click()
- expect(page.locator("#umap-tooltip-container")).to_contain_text(
+ expect(page.locator(".umap-tooltip-container")).to_contain_text(
"Please zoom in to edit the geometry"
)
expect(page.locator(".leaflet-vertex-icon")).to_be_hidden()
page.get_by_label("Zoom in").click()
- expect(page.locator("#umap-tooltip-container")).to_contain_text(
+ expect(page.locator(".umap-tooltip-container")).to_contain_text(
"Please zoom in to edit the geometry"
)
page.get_by_label("Zoom in").click()
@@ -497,6 +497,6 @@ def test_vertexmarker_not_shown_if_too_many(live_server, map, page, settings):
page.get_by_label("Zoom out").click()
page.wait_for_timeout(500)
expect(page.locator(".leaflet-vertex-icon")).to_be_hidden()
- expect(page.locator("#umap-tooltip-container")).to_contain_text(
+ expect(page.locator(".umap-tooltip-container")).to_contain_text(
"Please zoom in to edit the geometry"
)