diff --git a/umap/static/umap/css/bar.css b/umap/static/umap/css/bar.css
new file mode 100644
index 000000000..bd2e8358b
--- /dev/null
+++ b/umap/static/umap/css/bar.css
@@ -0,0 +1,233 @@
+.umap-main-edit-toolbox [type="button"] {
+ color: #fff;
+ font-size: 1.2em;
+ border: none;
+ background-color: var(--color-darkGray);
+ width: auto;
+ margin-bottom: 0;
+}
+.umap-main-edit-toolbox [type="button"]:hover {
+ text-decoration: underline;
+}
+
+.leaflet-container [type="button"].umap-help-link {
+ font-size: 12px;
+ padding-bottom: 3px;
+ background-color: inherit;
+}
+.leaflet-container .edit-save,
+.leaflet-container .edit-cancel,
+.leaflet-container .edit-disable,
+.leaflet-container .connected-peers
+{
+ display: block;
+ border: none;
+ font-size: 12px;
+ border-radius: 20px;
+ color: #f8f8f8;
+ height: 32px;
+ line-height: 30px;
+ padding: 0 20px;
+}
+.leaflet-container .connected-peers,
+.dark [type="button"].connected-peers:hover
+{
+ background-color: var(--color-lightCyan);
+ color: var(--color-dark);
+}
+
+.leaflet-container .edit-disable:before,
+.leaflet-container .edit-save:before,
+.leaflet-container .edit-cancel:before,
+.leaflet-container .connected-peers:before {
+ display: inline-block;
+ width: 19px;
+ height: 24px;
+ background-position: -50px -122px;
+ background-repeat: no-repeat;
+ background-image: url('../img/16-white.svg');
+ vertical-align: middle;
+ content: ' ';
+ text-align: center;
+}
+
+.leaflet-container .connected-peers:before {
+ background-image: url('../img/16.svg');
+}
+
+.leaflet-container .edit-disable span,
+.leaflet-container .edit-save span,
+.leaflet-container .edit-cancel span,
+.leaflet-container .connected-peers span{
+ margin-inline-start: 10px;
+}
+.leaflet-container .edit-save:before {
+ background-position: -148px -2px;
+}
+.leaflet-container .edit-disable:before {
+ background-position: -50px -25px;
+}
+.leaflet-container .connected-peers:before {
+ background-position: -2px -95px;
+}
+.leaflet-container .edit-cancel,
+.leaflet-container .edit-disable,
+.leaflet-container .connected-peers{
+ border: 0.5px solid rgba(153, 153, 153, 0.40);
+}
+.leaflet-container .edit-cancel:hover,
+.leaflet-container .edit-disable:hover {
+ border: 0.5px solid rgba(153, 153, 153, 0.80);
+ text-decoration: none;
+}
+.leaflet-container .edit-save {
+ opacity: 0.5;
+ cursor: not-allowed;
+ border-radius: 16px;
+ border: 0.5px solid rgba(153, 153, 153, 0.40);
+ background: rgba(153, 153, 153, 0.10);
+}
+.dark [type="button"].edit-save:hover {
+ background: rgba(153, 153, 153, 0.10);
+ text-decoration: none;
+}
+.umap-is-dirty .edit-save {
+ opacity: 1;
+ cursor: pointer;
+ border: 0.5px solid rgba(66, 236, 230, 0.40);
+ background: rgba(66, 236, 230, 0.10);
+ color: #42ECE6;
+}
+.umap-is-dirty .edit-save:before {
+ background-position: -148px -26px;
+}
+.umap-is-dirty .dark [type="button"].edit-save:hover {
+ border-color: rgba(66, 236, 230, 0.80);
+ background: rgba(66, 236, 230, 0.10);
+}
+.leaflet-container .edit-save,
+.leaflet-container .edit-cancel,
+.leaflet-container .edit-disable,
+.umap-edit-enabled .edit-enable {
+ display: none;
+}
+.umap-edit-enabled .edit-save,
+.umap-edit-enabled .edit-disable,
+.umap-edit-enabled.umap-is-dirty .edit-cancel {
+ display: inline-block;
+}
+.umap-is-dirty .edit-disable {
+ display: none;
+}
+.umap-caption-bar {
+ display: none;
+}
+.umap-main-edit-toolbox {
+ top: -46px;
+ position: absolute;
+ width: 100%;
+ left: 0;
+ right: 0;
+ height: 46px;
+ padding: 0 10px;
+ text-align: start;
+ line-height: var(--control-size);
+ cursor: auto;
+ border-bottom: 1px solid #222;
+ z-index: var(--zindex-panels);
+ display: flex;
+ justify-content: space-between;
+ background-color: var(--background-color);
+ color: var(--text-color);
+}
+.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%;
+}
+.umap-main-edit-toolbox .logo a {
+ background-image: url('../img/logo_small.svg');
+ background-position: 0 center;
+ background-repeat: no-repeat;
+ display: inline-block;
+ width: 39px;
+ height: 100%;
+ vertical-align: middle;
+ text-indent: -9999px;
+}
+.umap-main-edit-toolbox .map-name {
+ display: inline-block;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ font-weight: bold;
+ text-align: start;
+}
+.umap-main-edit-toolbox .share-status {
+ font-size: 1em;
+ font-style: italic;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.map-name:after {
+ content: '\00a0';
+ padding-inline-start: 3px;
+ width: 1ch;
+ display: inline-block;
+}
+.umap-is-dirty .map-name:after {
+ content: '*';
+}
+.umap-edit-enabled .umap-main-edit-toolbox {
+ top: 0;
+}
+.umap-caption-bar h3,
+.umap-main-edit-toolbox h3 {
+ display: inline;
+}
+.umap-caption-bar button {
+ margin-inline-start: 10px;
+}
+.umap-caption-bar button + button:before {
+ content: '|';
+ padding-inline-end: 10px;
+}
+.umap-main-edit-toolbox .umap-user {
+ color: #fff;
+}
+.umap-main-edit-toolbox .umap-user:hover {
+ text-decoration: underline;
+}
+.umap-main-edit-toolbox .umap-user:after {
+ content: '|';
+ padding-inline-start: 20px;
+ display: inline-block; /* Prevents underline on hover. */
+}
+.umap-caption-bar-enabled .umap-caption-bar {
+ display: block;
+ height: var(--footer-height);
+ background-color: #fff;
+ width: 100%;
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ padding: var(--gutter);
+ text-align: start;
+ line-height: 100%;
+ cursor: auto;
+ border-top: 1px solid var(--color-lightGray);
+ opacity: 0.93;
+ z-index: var(--zindex-panels);
+}
+.umap-caption-bar-enabled {
+ --current-footer-height: var(--footer-height);
+}
diff --git a/umap/static/umap/js/modules/help.js b/umap/static/umap/js/modules/help.js
index 0530dd1df..b6c2a88e4 100644
--- a/umap/static/umap/js/modules/help.js
+++ b/umap/static/umap/js/modules/help.js
@@ -226,13 +226,6 @@ export default class Help {
return button
}
- getStartedLink(container) {
- const button = DomUtil.createButton('umap-help-link', container, translate('Help'))
- button.textContent = translate('Help')
- button.addEventListener('click', () => this.showGetStarted())
- return button
- }
-
parse(container) {
for (const element of container.querySelectorAll('[data-help]')) {
this.button(element, element.dataset.help.split(','))
diff --git a/umap/static/umap/js/modules/permissions.js b/umap/static/umap/js/modules/permissions.js
index 5d2ece729..17552afd2 100644
--- a/umap/static/umap/js/modules/permissions.js
+++ b/umap/static/umap/js/modules/permissions.js
@@ -27,6 +27,10 @@ export class MapPermissions extends ServerStored {
)
}
+ render() {
+ this._umap.render(['properties.permissions'])
+ }
+
isOwner() {
return Boolean(this._umap.properties.user?.is_owner)
}
diff --git a/umap/static/umap/js/modules/rendering/map.js b/umap/static/umap/js/modules/rendering/map.js
index 9c4c5bfa8..607ee22ef 100644
--- a/umap/static/umap/js/modules/rendering/map.js
+++ b/umap/static/umap/js/modules/rendering/map.js
@@ -12,7 +12,6 @@ import { translate } from '../i18n.js'
import { uMapAlert as Alert } from '../../components/alerts/alert.js'
import * as Utils from '../utils.js'
import * as Icon from './icon.js'
-import ContextMenu from '../ui/contextmenu.js'
// Those options are not saved on the server, so they can live here
// instead of in umap.properties
@@ -104,10 +103,6 @@ const ControlsMixin = {
},
renderControls: function () {
- const hasSlideshow = Boolean(this.options.slideshow?.active)
- const barEnabled = this.options.captionBar || hasSlideshow
- document.body.classList.toggle('umap-caption-bar-enabled', barEnabled)
- document.body.classList.toggle('umap-slideshow-enabled', hasSlideshow)
for (const control of Object.values(this._controls)) {
this.removeControl(control)
}
@@ -150,198 +145,6 @@ const ControlsMixin = {
if (this._umap.getProperty('scaleControl')) this._controls.scale.addTo(this)
this._controls.tilelayers.setLayers()
},
-
- renderEditToolbar: function () {
- const className = 'umap-main-edit-toolbox'
- const container =
- document.querySelector(`.${className}`) ||
- DomUtil.create('div', `${className} with-transition dark`, this._controlContainer)
- container.innerHTML = ''
- const leftContainer = DomUtil.create('div', 'umap-left-edit-toolbox', container)
- const rightContainer = DomUtil.create('div', 'umap-right-edit-toolbox', container)
- const logo = DomUtil.create('div', 'logo', leftContainer)
- DomUtil.createLink('', logo, 'uMap', '/', null, translate('Go to the homepage'))
- const nameButton = DomUtil.createButton('map-name', leftContainer, '')
- DomEvent.on(nameButton, 'mouseover', () => {
- this._umap.tooltip.open({
- content: translate('Edit the title of the map'),
- anchor: nameButton,
- position: 'bottom',
- delay: 500,
- duration: 5000,
- })
- })
- const shareStatusButton = DomUtil.createButton(
- 'share-status',
- leftContainer,
- '',
- this._umap.permissions.edit,
- this._umap.permissions
- )
- DomEvent.on(shareStatusButton, 'mouseover', () => {
- this._umap.tooltip.open({
- content: translate('Update who can see and edit the map'),
- anchor: shareStatusButton,
- position: 'bottom',
- delay: 500,
- duration: 5000,
- })
- })
- if (this.options.editMode === 'advanced') {
- DomEvent.on(nameButton, 'click', this._umap.editCaption, this._umap)
- DomEvent.on(
- shareStatusButton,
- 'click',
- this._umap.permissions.edit,
- this._umap.permissions
- )
- }
- if (this.options.user?.id) {
- const button = U.Utils.loadTemplate(`
-
- `)
- rightContainer.appendChild(button)
- const menu = new ContextMenu({ className: 'dark', fixed: true })
- const actions = [
- {
- label: translate('New map'),
- action: this._umap.urls.get('map_new'),
- },
- {
- label: translate('My maps'),
- action: this._umap.urls.get('user_dashboard'),
- },
- {
- label: translate('My teams'),
- action: this._umap.urls.get('user_teams'),
- },
- ]
- if (this._umap.urls.has('user_profile')) {
- actions.push({
- label: translate('My profile'),
- action: this._umap.urls.get('user_profile'),
- })
- }
- button.addEventListener('click', () => {
- menu.openBelow(button, actions)
- })
- }
-
- const connectedPeers = this._umap.sync.getNumberOfConnectedPeers()
- if (connectedPeers !== 0) {
- const connectedPeersCount = DomUtil.createButton(
- 'leaflet-control-connected-peers',
- rightContainer,
- ''
- )
- DomEvent.on(connectedPeersCount, 'mouseover', () => {
- this._umap.tooltip.open({
- content: translate(
- '{connectedPeers} peer(s) currently connected to this map',
- {
- connectedPeers: connectedPeers,
- }
- ),
- anchor: connectedPeersCount,
- position: 'bottom',
- delay: 500,
- duration: 5000,
- })
- })
-
- const updateConnectedPeersCount = () => {
- connectedPeersCount.innerHTML = this._umap.sync.getNumberOfConnectedPeers()
- }
- updateConnectedPeersCount()
- }
-
- this._umap.help.getStartedLink(rightContainer)
- const controlEditCancel = DomUtil.createButton(
- 'leaflet-control-edit-cancel',
- rightContainer,
- DomUtil.add('span', '', null, translate('Cancel edits')),
- () => this._umap.askForReset()
- )
- DomEvent.on(controlEditCancel, 'mouseover', () => {
- this._umap.tooltip.open({
- content: this._umap.help.displayLabel('CANCEL'),
- anchor: controlEditCancel,
- position: 'bottom',
- delay: 500,
- duration: 5000,
- })
- })
- const controlEditDisable = DomUtil.createButton(
- 'leaflet-control-edit-disable',
- rightContainer,
- DomUtil.add('span', '', null, translate('View')),
- this._umap.disableEdit,
- this._umap
- )
- DomEvent.on(controlEditDisable, 'mouseover', () => {
- this._umap.tooltip.open({
- content: this._umap.help.displayLabel('PREVIEW'),
- anchor: controlEditDisable,
- position: 'bottom',
- delay: 500,
- duration: 5000,
- })
- })
- const controlEditSave = DomUtil.createButton(
- 'leaflet-control-edit-save button',
- rightContainer,
- DomUtil.add('span', '', null, translate('Save')),
- () => this._umap.saveAll()
- )
- DomEvent.on(controlEditSave, 'mouseover', () => {
- this._umap.tooltip.open({
- content: this._umap.help.displayLabel('SAVE'),
- anchor: controlEditSave,
- position: 'bottom',
- delay: 500,
- duration: 5000,
- })
- })
- },
-
- renderCaptionBar: function () {
- if (this.options.noControl) return
- const container =
- this._controlContainer.querySelector('.umap-caption-bar') ||
- DomUtil.create('div', 'umap-caption-bar', this._controlContainer)
- container.innerHTML = ''
- const name = DomUtil.create('h3', 'map-name', container)
- DomEvent.disableClickPropagation(container)
- this._umap.addAuthorLink(container)
- if (this._umap.getProperty('captionMenus')) {
- DomUtil.createButton(
- 'umap-about-link flat',
- container,
- translate('Open caption'),
- () => this._umap.openCaption()
- )
- DomUtil.createButton(
- 'umap-open-browser-link flat',
- container,
- translate('Browse data'),
- () => this.openBrowser('data')
- )
- if (this.options.facetKey) {
- DomUtil.createButton(
- 'umap-open-filter-link flat',
- container,
- translate('Filter data'),
- () => this.openBrowser('filters')
- )
- }
- }
- this._umap.onceDatalayersLoaded(() => {
- this._umap.slideshow.renderToolbox(container)
- })
- },
}
const ManageTilelayerMixin = {
@@ -482,8 +285,6 @@ export const LeafletMap = BaseMap.extend({
renderUI: function () {
setOptions(this, this._umap.properties)
this.initTileLayers()
- this.renderCaptionBar()
- this.renderEditToolbar()
// Needs tilelayer to exist for minimap
this.renderControls()
this.handleLimitBounds()
@@ -576,6 +377,5 @@ export const LeafletMap = BaseMap.extend({
initEditTools: function () {
this.editTools = new U.Editable(this._umap)
- this.renderEditToolbar()
},
})
diff --git a/umap/static/umap/js/modules/slideshow.js b/umap/static/umap/js/modules/slideshow.js
index 0d9a7766a..64a7b33bc 100644
--- a/umap/static/umap/js/modules/slideshow.js
+++ b/umap/static/umap/js/modules/slideshow.js
@@ -26,13 +26,9 @@ export default class Slideshow extends WithTemplate {
this.play()
}, this)
}
- leafletMap.on(
- 'edit:enabled',
- function () {
- this.stop()
- },
- this
- )
+ leafletMap.on('edit:enabled', () => {
+ this.stop()
+ })
}
set current(feature) {
@@ -81,9 +77,13 @@ export default class Slideshow extends WithTemplate {
spinner.style.animation = 'none'
}
+ isEnabled() {
+ return Boolean(this.properties.active)
+ }
+
play() {
if (this._id) return
- if (this._umap.editEnabled || !this._umap.properties.slideshow.active) return
+ if (this._umap.editEnabled || !this.isEnabled()) return
L.DomUtil.addClass(document.body, this.CLASSNAME)
this._id = window.setInterval(L.bind(this.loop, this), this.properties.delay)
this.startSpinner()
diff --git a/umap/static/umap/js/modules/ui/bar.js b/umap/static/umap/js/modules/ui/bar.js
new file mode 100644
index 000000000..479f1a379
--- /dev/null
+++ b/umap/static/umap/js/modules/ui/bar.js
@@ -0,0 +1,187 @@
+import { DomEvent } from '../../../vendors/leaflet/leaflet-src.esm.js'
+import { translate } from '../i18n.js'
+import { WithTemplate } from '../utils.js'
+import ContextMenu from './contextmenu.js'
+
+const TOP_BAR_TEMPLATE = `
+
+
+
+
+
+
+
+
+
+
+
`
+
+export class TopBar extends WithTemplate {
+ constructor(umap, parent) {
+ super()
+ this._umap = umap
+ this._menu = new ContextMenu({ className: 'dark', fixed: true })
+ this.loadTemplate(TOP_BAR_TEMPLATE)
+ this.parent = parent
+ }
+
+ setup() {
+ this.parent.appendChild(this.element)
+ this.elements.name.addEventListener('mouseover', () => {
+ this._umap.tooltip.open({
+ content: translate('Edit the title of the map'),
+ anchor: this.elements.name,
+ position: 'bottom',
+ delay: 500,
+ duration: 5000,
+ })
+ })
+ this.elements.share.addEventListener('mouseover', () => {
+ this._umap.tooltip.open({
+ content: translate('Update who can see and edit the map'),
+ anchor: this.elements.share,
+ position: 'bottom',
+ delay: 500,
+ duration: 5000,
+ })
+ })
+ if (this._umap.properties.editMode === 'advanced') {
+ this.elements.name.addEventListener('click', () => this._umap.editCaption())
+ this.elements.share.addEventListener('click', () => this._umap.permissions.edit())
+ }
+ this.elements.user.addEventListener('click', () => {
+ if (this._umap.properties.user?.id) {
+ const actions = [
+ {
+ label: translate('New map'),
+ action: this._umap.urls.get('map_new'),
+ },
+ {
+ label: translate('My maps'),
+ action: this._umap.urls.get('user_dashboard'),
+ },
+ {
+ label: translate('My teams'),
+ action: this._umap.urls.get('user_teams'),
+ },
+ ]
+ if (this._umap.urls.has('user_profile')) {
+ actions.push({
+ label: translate('My profile'),
+ action: this._umap.urls.get('user_profile'),
+ })
+ }
+ this._menu.openBelow(this.elements.user, actions)
+ }
+ })
+
+ const connectedPeers = this._umap.sync.getNumberOfConnectedPeers()
+ this.elements.peers.dataset.connected = connectedPeers
+ this.elements.peers.addEventListener('mouseover', () => {
+ if (!connectedPeers) return
+ this._umap.tooltip.open({
+ content: translate('{connectedPeers} peer(s) currently connected to this map', {
+ connectedPeers: connectedPeers,
+ }),
+ anchor: this.elements.peers,
+ position: 'bottom',
+ delay: 500,
+ duration: 5000,
+ })
+ })
+
+ this.elements.help.addEventListener('click', () => this._umap.showGetStarted())
+ this.elements.cancel.addEventListener('click', () => this._umap.askForReset())
+ this.elements.cancel.addEventListener('mouseover', () => {
+ this._umap.tooltip.open({
+ content: this._umap.help.displayLabel('CANCEL'),
+ anchor: this.elements.cancel,
+ position: 'bottom',
+ delay: 500,
+ duration: 5000,
+ })
+ })
+ this.elements.view.addEventListener('click', () => this._umap.disableEdit())
+ this.elements.view.addEventListener('mouseover', () => {
+ this._umap.tooltip.open({
+ content: this._umap.help.displayLabel('PREVIEW'),
+ anchor: this.elements.view,
+ position: 'bottom',
+ delay: 500,
+ duration: 5000,
+ })
+ })
+ this.elements.save.addEventListener('click', () => this._umap.saveAll())
+ this.elements.save.addEventListener('mouseover', () => {
+ this._umap.tooltip.open({
+ content: this._umap.help.displayLabel('SAVE'),
+ anchor: this.elements.save,
+ position: 'bottom',
+ delay: 500,
+ duration: 5000,
+ })
+ })
+ this.redraw()
+ }
+
+ redraw() {
+ this.elements.peers.hidden = !this._umap.getProperty('syncEnabled')
+ }
+}
+
+const BOTTOM_BAR_TEMPLATE = `
+
+
+
+
+
+
+
+`
+
+export class BottomBar extends WithTemplate {
+ constructor(umap, slideshow, parent) {
+ super()
+ this._umap = umap
+ this._slideshow = slideshow
+ this.loadTemplate(BOTTOM_BAR_TEMPLATE)
+ this.parent = parent
+ }
+
+ setup() {
+ this.parent.appendChild(this.element)
+ DomEvent.disableClickPropagation(this.element)
+ this._umap.addAuthorLink(this.elements.author)
+ this.elements.caption.addEventListener('click', () => this._umap.openCaption())
+ this.elements.browse.addEventListener('click', () => this._umap.openBrowser('data'))
+ this.elements.filter.addEventListener('click', () =>
+ this._umap.openBrowser('filters')
+ )
+ this._slideshow.renderToolbox(this.element)
+ this.redraw()
+ }
+
+ redraw() {
+ const hasSlideshow = this._slideshow.isEnabled()
+ const barEnabled = this._umap.properties.captionBar || hasSlideshow
+ document.body.classList.toggle('umap-caption-bar-enabled', barEnabled)
+ document.body.classList.toggle('umap-slideshow-enabled', hasSlideshow)
+ const showMenus = this._umap.getProperty('captionMenus')
+ this.elements.caption.hidden = !showMenus
+ this.elements.browse.hidden = !showMenus
+ this.elements.filter.hidden = !showMenus || !this._umap.properties.facetKey
+ }
+}
diff --git a/umap/static/umap/js/modules/umap.js b/umap/static/umap/js/modules/umap.js
index b8a03f2e0..8143df1b9 100644
--- a/umap/static/umap/js/modules/umap.js
+++ b/umap/static/umap/js/modules/umap.js
@@ -13,6 +13,7 @@ import { LeafletMap } from './rendering/map.js'
import URLs from './urls.js'
import { Panel, EditPanel, FullPanel } from './ui/panel.js'
import Dialog from './ui/dialog.js'
+import { BottomBar, TopBar } from './ui/bar.js'
import Tooltip from './ui/tooltip.js'
import ContextMenu from './ui/contextmenu.js'
import { Request, ServerRequest } from './request.js'
@@ -102,12 +103,14 @@ export default class Umap extends ServerStored {
this.panel = new Panel(this, this._leafletMap)
this.dialog = new Dialog({ className: 'dark' })
+ this.topBar = new TopBar(this, this._leafletMap._controlContainer)
+ this.bottomBar = new BottomBar(
+ this,
+ this.slideshow,
+ this._leafletMap._controlContainer
+ )
this.tooltip = new Tooltip(this._leafletMap._controlContainer)
this.contextmenu = new ContextMenu()
- if (this.hasEditMode()) {
- this.editPanel = new EditPanel(this, this._leafletMap)
- this.fullPanel = new FullPanel(this, this._leafletMap)
- }
this.server = new ServerRequest()
this.request = new Request()
this.facets = new Facets(this)
@@ -117,6 +120,13 @@ export default class Umap extends ServerStored {
this.share = new Share(this)
this.rules = new Rules(this)
+ if (this.hasEditMode()) {
+ this.editPanel = new EditPanel(this, this._leafletMap)
+ this.fullPanel = new FullPanel(this, this._leafletMap)
+ this._leafletMap.initEditTools()
+ this.topBar.setup()
+ }
+
this.datalayersFromQueryString = this.searchParams.get('datalayers')
if (this.datalayersFromQueryString) {
this.datalayersFromQueryString = this.datalayersFromQueryString
@@ -180,14 +190,11 @@ export default class Umap extends ServerStored {
await this.loadDataFromQueryString()
}
- if (this.hasEditMode()) {
- this._leafletMap.initEditTools()
- }
-
if (!this.properties.noControl) {
this.initShortcuts()
this._leafletMap.on('contextmenu', (e) => this.onContextMenu(e))
this.onceDataLoaded(this.setViewFromQueryString)
+ this.bottomBar.setup()
this.propagate()
}
@@ -633,22 +640,6 @@ export default class Umap extends ServerStored {
this.fire('saved')
}
- propagate() {
- let els = document.querySelectorAll('.map-name')
- for (const el of els) {
- el.textContent = this.getDisplayName()
- }
- const status = this.permissions.getShareStatusDisplay()
- els = document.querySelectorAll('.share-status')
- for (const el of els) {
- if (status) {
- el.textContent = translate('Visibility: {status}', {
- status: status,
- })
- }
- }
- }
-
getDisplayName() {
return this.properties.name || translate('Untitled map')
}
@@ -993,7 +984,13 @@ export default class Umap extends ServerStored {
],
]
const slideshowBuilder = new U.FormBuilder(this, slideshowFields, {
- callback: () => this.slideshow.setProperties(this.properties.slideshow),
+ callback: () => {
+ this.slideshow.setProperties(this.properties.slideshow)
+ // FIXME when we refactor formbuilder: this callback is called in a 'postsync'
+ // event, which comes after the call of `setter` method, which will call the
+ // map.render method, which should do this redraw.
+ this.bottomBar.redraw()
+ },
umap: this,
})
slideshow.appendChild(slideshowBuilder.build())
@@ -1250,10 +1247,8 @@ export default class Umap extends ServerStored {
}
render(fields) {
- if (fields.includes('numberOfConnectedPeers')) {
- this._leafletMap.renderEditToolbar()
- this.propagate()
- }
+ const impacted = this.propagate(fields)
+ if (impacted) return // No need to run a wider reflow
const impacts = Utils.getImpactsFromSchema(fields)
for (const impact of impacts) {
@@ -1261,7 +1256,8 @@ export default class Umap extends ServerStored {
case 'ui':
this._leafletMap.renderUI()
this.browser.redraw()
- this.propagate()
+ this.topBar.redraw()
+ this.bottomBar.redraw()
break
case 'data':
this.eachVisibleDataLayer((datalayer) => {
@@ -1283,6 +1279,49 @@ export default class Umap extends ServerStored {
}
}
+ // This method does a targeted update of the UI,
+ // it whould be merged with `render`` method and the
+ // SCHEMA at some point
+ propagate(fields = []) {
+ const impacts = {
+ 'properties.name': () => {
+ Utils.eachElement('.map-name', (el) => {
+ el.textContent = this.getDisplayName()
+ })
+ },
+ user: () => {
+ Utils.eachElement('.umap-user .username', (el) => {
+ if (this.properties.user?.id) {
+ el.textContent = this.properties.user.name
+ }
+ })
+ },
+ 'properties.permissions': () => {
+ const status = this.permissions.getShareStatusDisplay()
+ if (status) {
+ Utils.eachElement('.share-status', (el) => {
+ el.textContent = translate('Visibility: {status}', {
+ status: status,
+ })
+ })
+ }
+ },
+ numberOfConnectedPeers: () => {
+ Utils.eachElement('.connected-peers', (el) => {
+ el.textContent = this.sync.getNumberOfConnectedPeers()
+ })
+ },
+ }
+ let impacted = false
+ for (const [field, impact] of Object.entries(impacts)) {
+ if (!fields.length || fields.includes(field)) {
+ impact()
+ impacted = true
+ }
+ }
+ return impacted
+ }
+
// TODO: allow to control the default datalayer
// (edit and viewing)
// cf https://github.com/umap-project/umap/issues/585
diff --git a/umap/static/umap/js/modules/utils.js b/umap/static/umap/js/modules/utils.js
index b9fe128e4..2e12083e4 100644
--- a/umap/static/umap/js/modules/utils.js
+++ b/umap/static/umap/js/modules/utils.js
@@ -25,8 +25,6 @@ export function checkId(string) {
return /^[A-Za-z0-9]{5}$/.test(string)
}
-
-
function _getPropertyName(field) {
const filtered_field = ['options.', 'properties.'].reduce(
(acc, prefix) => acc.replace(prefix, ''),
@@ -440,3 +438,9 @@ export function deepEqual(object1, object2) {
export function slugify(str) {
return (str || 'data').replace(/[^a-z0-9]/gi, '_').toLowerCase()
}
+
+export function eachElement(selector, callback) {
+ for (const el of document.querySelectorAll(selector)) {
+ callback(el)
+ }
+}
diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js
index 777c866cc..540614928 100644
--- a/umap/static/umap/js/umap.controls.js
+++ b/umap/static/umap/js/umap.controls.js
@@ -389,7 +389,7 @@ U.EditControl = L.Control.extend({
},
onAdd: function (map) {
- const container = L.DomUtil.create('div', 'leaflet-control-edit-enable')
+ const container = L.DomUtil.create('div', 'edit-enable')
const enableEditing = L.DomUtil.createButton(
'',
container,
diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css
index 480127cd5..7faf62105 100644
--- a/umap/static/umap/map.css
+++ b/umap/static/umap/map.css
@@ -45,9 +45,6 @@ html[dir="rtl"] .leaflet-tooltip-pane > * {
.umap-edit-enabled {
--current-header-height: var(--header-height);
}
-.umap-caption-bar-enabled {
- --current-footer-height: var(--footer-height);
-}
.leaflet-top {
top: var(--current-header-height);
}
@@ -166,7 +163,7 @@ html[dir="rtl"] .leaflet-tooltip-pane > * {
min-height: 23px;
height: 23px;
}
-.leaflet-control-edit-enable [type="button"]:before {
+.edit-enable [type="button"]:before {
content: ' ';
width: 24px;
height: 24px;
@@ -175,7 +172,7 @@ html[dir="rtl"] .leaflet-tooltip-pane > * {
background-image: url('./img/16-white.svg');
background-position: -48px -48px;
}
-.leaflet-control-edit-enable [type="button"] {
+.edit-enable [type="button"] {
width: initial;
padding: 0 20px;
background-color: #353c3e;
@@ -187,7 +184,7 @@ html[dir="rtl"] .leaflet-tooltip-pane > * {
display: block;
}
.leaflet-control-toolbar .leaflet-toolbar-icon.dark:hover,
-.leaflet-control-edit-enable [type="button"]:hover {
+.edit-enable [type="button"]:hover {
background-color: #4d5759;
}
.umap-permanent-credits-container {
@@ -476,245 +473,12 @@ ul.photon-autocomplete {
.umap-edit-actions li:hover {
background-color: #353c3e;
}
-
-
-/* ********************************* */
-/* Edit main toolbox */
-/* ********************************* */
-.umap-main-edit-toolbox [type="button"] {
- color: #fff;
- font-size: 1.2em;
- border: none;
- background-color: var(--color-darkGray);
- width: auto;
- margin-bottom: 0;
-}
-.umap-main-edit-toolbox [type="button"]:hover {
- text-decoration: underline;
-}
-
-.leaflet-container [type="button"].umap-help-link {
- font-size: 12px;
- padding-bottom: 3px;
- background-color: inherit;
-}
-.leaflet-container .leaflet-control-edit-save,
-.leaflet-container .leaflet-control-edit-cancel,
-.leaflet-container .leaflet-control-edit-disable,
-.leaflet-container .leaflet-control-connected-peers
-{
- display: block;
- border: none;
- font-size: 12px;
- border-radius: 20px;
- color: #f8f8f8;
- height: 32px;
- line-height: 30px;
- padding: 0 20px;
-}
-.leaflet-container .leaflet-control-connected-peers,
-.dark [type="button"].leaflet-control-connected-peers:hover
-{
- background-color: var(--color-lightCyan);
- color: var(--color-dark);
-}
-
-.leaflet-container .leaflet-control-edit-disable:before,
-.leaflet-container .leaflet-control-edit-save:before,
-.leaflet-container .leaflet-control-edit-cancel:before,
-.leaflet-container .leaflet-control-connected-peers:before {
- display: inline-block;
- width: 19px;
- height: 24px;
- background-position: -50px -122px;
- background-repeat: no-repeat;
- background-image: url('./img/16-white.svg');
- vertical-align: middle;
- content: ' ';
- text-align: center;
-}
-
-.leaflet-container .leaflet-control-connected-peers:before {
- background-image: url('./img/16.svg');
-}
-
-.leaflet-container .leaflet-control-edit-disable span,
-.leaflet-container .leaflet-control-edit-save span,
-.leaflet-container .leaflet-control-edit-cancel span,
-.leaflet-container .leaflet-control-connected-peers span{
- margin-inline-start: 10px;
-}
-.leaflet-container .leaflet-control-edit-save:before {
- background-position: -148px -2px;
-}
-.leaflet-container .leaflet-control-edit-disable:before {
- background-position: -50px -25px;
-}
-.leaflet-container .leaflet-control-connected-peers:before {
- background-position: -2px -95px;
-}
-.leaflet-container .leaflet-control-edit-cancel,
-.leaflet-container .leaflet-control-edit-disable,
-.leaflet-container .leaflet-control-connected-peers{
- border: 0.5px solid rgba(153, 153, 153, 0.40);
-}
-.leaflet-container .leaflet-control-edit-cancel:hover,
-.leaflet-container .leaflet-control-edit-disable:hover {
- border: 0.5px solid rgba(153, 153, 153, 0.80);
- text-decoration: none;
-}
-.leaflet-container .leaflet-control-edit-save {
- opacity: 0.5;
- cursor: not-allowed;
- border-radius: 16px;
- border: 0.5px solid rgba(153, 153, 153, 0.40);
- background: rgba(153, 153, 153, 0.10);
-}
-.dark [type="button"].leaflet-control-edit-save:hover {
- background: rgba(153, 153, 153, 0.10);
- text-decoration: none;
-}
-.umap-is-dirty .leaflet-control-edit-save {
- opacity: 1;
- cursor: pointer;
- border: 0.5px solid rgba(66, 236, 230, 0.40);
- background: rgba(66, 236, 230, 0.10);
- color: #42ECE6;
-}
-.umap-is-dirty .leaflet-control-edit-save:before {
- background-position: -148px -26px;
-}
-.umap-is-dirty .dark [type="button"].leaflet-control-edit-save:hover {
- border-color: rgba(66, 236, 230, 0.80);
- background: rgba(66, 236, 230, 0.10);
-}
-.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 .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 .leaflet-control-edit-disable {
- display: none;
-}
-.umap-caption-bar {
- display: none;
-}
-.umap-main-edit-toolbox {
- top: -46px;
- position: absolute;
- width: 100%;
- left: 0;
- right: 0;
- height: 46px;
- padding: 0 10px;
- text-align: start;
- line-height: var(--control-size);
- cursor: auto;
- border-bottom: 1px solid #222;
- z-index: var(--zindex-panels);
- display: flex;
- justify-content: space-between;
- background-color: var(--background-color);
- color: var(--text-color);
-}
-.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%;
-}
-.umap-main-edit-toolbox .logo a {
- background-image: url('./img/logo_small.svg');
- background-position: 0 center;
- background-repeat: no-repeat;
- display: inline-block;
- width: 39px;
- height: 100%;
- vertical-align: middle;
- text-indent: -9999px;
-}
-.umap-main-edit-toolbox .map-name {
- display: inline-block;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- font-weight: bold;
- text-align: start;
-}
-.umap-main-edit-toolbox .share-status {
- font-size: 1em;
- font-style: italic;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-.map-name:after {
- content: '\00a0';
- padding-inline-start: 3px;
- width: 1ch;
- display: inline-block;
-}
-.umap-is-dirty .map-name:after {
- content: '*';
-}
-.umap-edit-enabled .umap-main-edit-toolbox {
- top: 0;
-}
-.umap-caption-bar h3,
-.umap-main-edit-toolbox h3 {
- display: inline;
-}
-.umap-caption-bar button {
- margin-inline-start: 10px;
-}
-.umap-caption-bar button + button:before {
- content: '|';
- padding-inline-end: 10px;
-}
-.umap-main-edit-toolbox .umap-user {
- color: #fff;
-}
-.umap-main-edit-toolbox .umap-user:hover {
- text-decoration: underline;
-}
-.umap-main-edit-toolbox .umap-user:after {
- content: '|';
- padding-inline-start: 20px;
- display: inline-block; /* Prevents underline on hover. */
-}
-.umap-caption-bar-enabled .umap-caption-bar {
- display: block;
- height: var(--footer-height);
- background-color: #fff;
- width: 100%;
- position: absolute;
- left: 0;
- bottom: 0;
- right: 0;
- padding: var(--gutter);
- text-align: start;
- line-height: 100%;
- cursor: auto;
- border-top: 1px solid var(--color-lightGray);
- opacity: 0.93;
- z-index: var(--zindex-panels);
-}
.umap-help {
font-style: italic;
}
+
+
.umap-datalayer-version {
padding: 5px 0;
border-bottom: 1px solid #202425;
diff --git a/umap/templates/umap/css.html b/umap/templates/umap/css.html
index ba4b0cf8b..74b949935 100644
--- a/umap/templates/umap/css.html
+++ b/umap/templates/umap/css.html
@@ -35,4 +35,5 @@
+