chore: move editToolBar and captionBar to modules

This also tries to rework a bit the "reflow" process, which how
to edit the UI when something changes in the data.

The idea is to build the whole HTML, then:
- the `propagate` method tries to change the UI with targetted CSS
selector, but anywhere
- while `render` hide/show some elements

This is not the destination, but just another step. We certainly
need to refactor the SCHEMA to make the next step (as we want to associate
some data change to UI change).
This commit is contained in:
Yohan Boniface 2024-11-18 17:36:43 +01:00
parent 2c64745c52
commit 5ceda7b2a3
11 changed files with 514 additions and 489 deletions

View file

@ -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);
}

View file

@ -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(','))

View file

@ -27,6 +27,10 @@ export class MapPermissions extends ServerStored {
)
}
render() {
this._umap.render(['properties.permissions'])
}
isOwner() {
return Boolean(this._umap.properties.user?.is_owner)
}

View file

@ -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(`
<button class="umap-user flat" type="button">
<i class="icon icon-16 icon-profile"></i>
<span>${this.options.user.name}</span>
</button>
`)
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()
},
})

View file

@ -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) {
@ -85,9 +81,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()

View file

@ -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 = `
<div class="umap-main-edit-toolbox with-transition dark">
<div class="umap-left-edit-toolbox" data-ref="left">
<div class="logo"><a class="" href="/" title="${translate('Go to the homepage')}">uMap</a></div>
<button class="map-name" type="button" data-ref="name"></button>
<button class="share-status" type="button" data-ref="share"></button>
</div>
<div class="umap-right-edit-toolbox" data-ref="right">
<button class="connected-peers" type="button" data-ref="peers"></button>
<button class="umap-user flat" type="button" data-ref="user">
<i class="icon icon-16 icon-profile"></i>
<span class="username" data-ref="username"></span>
</button>
<button class="umap-help-link" type="button" title="${translate('Help')}" data-ref="help">${translate('Help')}</button>
<button class="edit-cancel" type="button" data-ref="cancel">
<span class="">${translate('Cancel edits')}</span>
</button>
<button class="edit-disable" type="button" data-ref="view">
<span class="">${translate('View')}</span>
</button>
<button class="edit-save button" type="button" data-ref="save">
<span class="">${translate('Save')}</span>
</button>
</div>
</div>`
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 = `
<div class="umap-caption-bar">
<h3 class="map-name"></h3>
<span data-ref="author"></span>
<button class="umap-about-link flat" type="button" title="${translate('Open caption')}" data-ref="caption">${translate('Open caption')}</button>
<button class="umap-open-browser-link flat" type="button" title="${translate('Browse data')}" data-ref="browse">${translate('Browse data')}</button>
<button class="umap-open-browser-link flat" type="button" title="${translate('Filter data')}" data-ref="filter">${translate('Filter data')}</button>
</div>
`
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
}
}

View file

@ -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')
}
@ -1004,7 +995,13 @@ export default class Umap extends ServerStored {
],
]
const slideshowBuilder = new U.FormBuilder(this, slideshowFields, {
callback: () => this.slideshow.load(),
callback: () => {
this.slideshow.load()
// 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())
@ -1261,10 +1258,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) {
@ -1272,7 +1267,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) => {
@ -1294,6 +1290,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

View file

@ -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)
}
}

View file

@ -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,

View file

@ -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;

View file

@ -35,4 +35,5 @@
<link rel="stylesheet" href="{% static 'umap/css/dialog.css' %}" />
<link rel="stylesheet" href="{% static 'umap/css/importers.css' %}" />
<link rel="stylesheet" href="{% static 'umap/css/tableeditor.css' %}" />
<link rel="stylesheet" href="{% static 'umap/css/bar.css' %}" />
<link rel="stylesheet" href="{% static 'umap/theme.css' %}" />