This commit is contained in:
Alexis Métaireau 2024-01-12 22:15:47 +01:00
parent 4b34a7d300
commit d219ed331f
6 changed files with 195 additions and 64 deletions

View file

@ -1,8 +1,11 @@
import * as L from '../../vendors/leaflet/leaflet-src.esm.js' import * as L from '../../vendors/leaflet/leaflet-src.esm.js'
import * as Y from '../../vendors/yjs/yjs.js'
import URLs from './urls.js' import URLs from './urls.js'
// Import modules and export them to the global scope. // Import modules and export them to the global scope.
// For the not yet module-compatible JS out there. // For the not yet module-compatible JS out there.
// Copy the leaflet module, it's expected by leaflet plugins to be writeable. // Copy the leaflet module, it's expected by leaflet plugins to be writeable.
window.L = { ...L } window.L = { ...L }
window.Y = Y
window.umap = { URLs } window.umap = { URLs }

View file

@ -62,7 +62,12 @@ L.U.Browser = L.Class.extend({
container.id = `browse_data_datalayer_${datalayer.umap_id}` container.id = `browse_data_datalayer_${datalayer.umap_id}`
datalayer.renderToolbox(headline) datalayer.renderToolbox(headline)
L.DomUtil.add('span', '', headline, datalayer.options.name) L.DomUtil.add('span', '', headline, datalayer.options.name)
const counter = L.DomUtil.add('span', 'datalayer-counter', headline, datalayer.count()) const counter = L.DomUtil.add(
'span',
'datalayer-counter',
headline,
datalayer.count()
)
counter.title = L._('{count} features in this layer', { count: datalayer.count() }) counter.title = L._('{count} features in this layer', { count: datalayer.count() })
const ul = L.DomUtil.create('ul', '', container) const ul = L.DomUtil.create('ul', '', container)
L.DomUtil.classIf(container, 'off', !datalayer.isVisible()) L.DomUtil.classIf(container, 'off', !datalayer.isVisible())

View file

@ -0,0 +1,45 @@
/**
* A mixin to ease the rendering of the data, and updating of a local CRDT.
*
* The mixed class needs to expose:
*
* - `dataUpdaters`, an object matching each property with a list of renderers.
* - `getDataObject`, a method returning where the data is stored/retrieved.
*/
L.U.DataRendererMixin = {
populateCRDT: function () {
for (const [key, value] of Object.entries(this.options)) {
this.crdt.set(key, value)
}
},
/**
* For each passed property, find the functions to rerender the interface,
* and call them.
*
* @param list updatedProperties : properties that have been updated.
*/
renderProperties: function (updatedProperties) {
console.debug(updatedProperties)
let renderers = new Set()
for (const prop of updatedProperties) {
const propRenderers = this.dataUpdaters[prop]
if (propRenderers) {
for (const renderer of propRenderers) renderers.add(renderer)
}
}
console.debug('renderers', renderers)
for (const renderer of renderers) this[renderer]()
},
dataReceived: function () {
// Data has been received over the wire
this.updateInternalData()
this.onPropertiesUpdated(['name', 'color'])
},
}
L.U.FormBuilderDataRendererMixin = {
getDataObject: function () {
return this.options
},
}

View file

@ -1259,6 +1259,12 @@ L.U.FormBuilder = L.FormBuilder.extend({
setter: function (field, value) { setter: function (field, value) {
L.FormBuilder.prototype.setter.call(this, field, value) L.FormBuilder.prototype.setter.call(this, field, value)
if (this.options.makeDirty !== false) this.obj.isDirty = true if (this.options.makeDirty !== false) this.obj.isDirty = true
// FIXME: for now remove the options prefix
field = field.replace('options.', '')
if (this.obj.crdt) this.obj.crdt.set(field, value)
this.obj.onPropertiesUpdated([field])
}, },
finish: function () { finish: function () {

View file

@ -34,7 +34,7 @@ L.Map.mergeOptions({
// we cannot rely on this because of the y is overriden by Leaflet // we cannot rely on this because of the y is overriden by Leaflet
// See https://github.com/Leaflet/Leaflet/pull/9201 // See https://github.com/Leaflet/Leaflet/pull/9201
// And let's remove this -y when this PR is merged and released. // And let's remove this -y when this PR is merged and released.
demoTileInfos: { s: 'a', z: 9, x: 265, y: 181, '-y': 181, r: '' }, demoTileInfos: { 's': 'a', 'z': 9, 'x': 265, 'y': 181, '-y': 181, 'r': '' },
licences: [], licences: [],
licence: '', licence: '',
enableMarkerDraw: true, enableMarkerDraw: true,
@ -69,6 +69,122 @@ L.U.Map.include({
'tilelayers', 'tilelayers',
], ],
//Used by the L.U.DataRendererMixin
propertiesRenderers: {
// Controls
'name': ['renderEditToolbar', 'renderControls'],
'color': ['renderVisibleDataLayers'],
'moreControl': ['renderControls', 'initCaptionBar'],
'scrollWheelZoom': ['renderControls', 'initCaptionBar'],
'miniMap': ['renderControls', 'initCaptionBar'],
'scaleControl': ['renderControls', 'initCaptionBar'],
'onLoadPanel': ['renderControls', 'initCaptionBar'],
'defaultView': ['renderControls', 'initCaptionBar'],
'displayPopupFooter': ['renderControls', 'initCaptionBar'],
'captionBar': ['renderControls', 'initCaptionBar'],
'captionMenus': ['renderControls', 'initCaptionBar'],
'zoomControl': ['renderControls', 'initCaptionBar'],
'searchControl': ['renderControls', 'initCaptionBar'],
'fullscreenControl': ['renderControls', 'initCaptionBar'],
'embedControl': ['renderControls', 'initCaptionBar'],
'locateControl': ['renderControls', 'initCaptionBar'],
'measureControl': ['renderControls', 'initCaptionBar'],
'editinosmControl': ['renderControls', 'initCaptionBar'],
'datalayersControl': ['renderControls', 'initCaptionBar'],
'starControl': ['renderControls', 'initCaptionBar'],
'tilelayersControl': ['renderControls', 'initCaptionBar'],
// Shape properties
'color': ['renderVisibleDataLayers', 'renderControls'],
'iconClass': ['renderVisibleDataLayers', 'renderControls'],
'iconUrl': ['renderVisibleDataLayers', 'renderControls'],
'iconOpacity': ['renderVisibleDataLayers', 'renderControls'],
'opacity': ['renderVisibleDataLayers', 'renderControls'],
'weight': ['renderVisibleDataLayers', 'renderControls'],
'fill': ['renderVisibleDataLayers', 'renderControls'],
'fillColor': ['renderVisibleDataLayers', 'renderControls'],
'fillOpacity': ['renderVisibleDataLayers', 'renderControls'],
'smoothFactor': ['renderVisibleDataLayers', 'renderControls'],
'dashArray': ['renderVisibleDataLayers', 'renderControls'],
// Default properties
'zoomTo': ['initCaptionBar'],
'easing': ['initCaptionBar'],
'labelKey': ['initCaptionBar'],
'sortKey': ['initCaptionBar', 'reindexEachDataLayer'],
'filterKey': ['initCaptionBar'],
'facetKey': ['initCaptionBar'],
'slugKey': ['initCaptionBar'],
// Interaction properties
'popupShape': [],
'popupTemplate': [],
'popupContentTemplate': [],
'showLabel': ['renderVisibleDataLayers'],
'labelDirection': ['renderVisibleDataLayers'],
'labelInteractive': ['renderVisibleDataLayers'],
'outlinkTarget': [],
// Tile layer
'tilelayer.name': ['initTileLayer'],
'tilelayer.url_template': ['initTileLayer'],
'tilelayer.maxZoom': ['initTileLayer'],
'tilelayer.minZoom': ['initTileLayer'],
'tilelayer.attribution': ['initTileLayer'],
'tilelayer.tms': ['initTileLayer'],
// Overlay
'overlay.url_template': ['initTileLayer'],
'overlay.maxZoom': ['initTileLayer'],
'overlay.minZoom': ['initTileLayer'],
'overlay.attribution': ['initTileLayer'],
'overlay.opacity': ['initTileLayer'],
'overlay.tms': ['initTileLayer'],
// Bounds
'limitBounds.south': ['handleLimitBounds'],
'limitBounds.west': ['handleLimitBounds'],
'limitBounds.north': ['handleLimitBounds'],
'limitBounds.east': ['handleLimitBounds'],
// Slideshow
'slideshow.active': ['renderControls'],
'slideshow.delay': ['renderControls'],
'slideshow.easing': ['renderControls'],
'slideshow.autoplay': ['renderControls'],
// Credits
'licence': ['renderControls'],
'shortCredit': ['renderControls'],
'longCredit': ['renderControls'],
'permanentCredit': ['renderControls'],
'permanentCreditBackground': ['renderControls'],
},
reindexEachDataLayer: function () {
this.eachDataLayer((datalayer) => datalayer.reindex())
},
renderVisibleDataLayers: function () {
this.eachVisibleDataLayer((datalayer) => {
datalayer.redraw()
})
},
broadcastChanges: function (data) {
// Send changes over the wire
console.log(data)
},
updateInternalData: function () {
this.options.name = 'CRDTS, yeah'
this.options.color = 'Fushia'
},
getCRDT: function () {
return this._main_crdt.getMap('map')
},
initialize: function (el, geojson) { initialize: function (el, geojson) {
// Locale name (pt_PT, en_US…) // Locale name (pt_PT, en_US…)
// To be used for Django localization // To be used for Django localization
@ -292,6 +408,7 @@ L.U.Map.include({
this.backup() this.backup()
this.initContextMenu() this.initContextMenu()
this.on('click contextmenu.show', this.closeInplaceToolbar) this.on('click contextmenu.show', this.closeInplaceToolbar)
this._main_crdt = new YJS.Doc()
}, },
initControls: function () { initControls: function () {
@ -830,7 +947,10 @@ L.U.Map.include({
self.isDirty = true self.isDirty = true
} }
if (this._controls.tilelayersChooser) if (this._controls.tilelayersChooser)
this._controls.tilelayersChooser.openSwitcher({ callback: callback, className: 'dark' }) this._controls.tilelayersChooser.openSwitcher({
callback: callback,
className: 'dark',
})
}, },
manageDatalayers: function () { manageDatalayers: function () {
@ -1260,13 +1380,7 @@ L.U.Map.include({
'options.captionBar', 'options.captionBar',
'options.captionMenus', 'options.captionMenus',
]) ])
builder = new L.U.FormBuilder(this, UIFields, { builder = new L.U.FormBuilder(this, UIFields)
callback: function () {
this.renderControls()
this.initCaptionBar()
},
callbackContext: this,
})
const controlsOptions = L.DomUtil.createFieldset( const controlsOptions = L.DomUtil.createFieldset(
container, container,
L._('User interface options') L._('User interface options')
@ -1289,14 +1403,7 @@ L.U.Map.include({
'options.dashArray', 'options.dashArray',
] ]
builder = new L.U.FormBuilder(this, shapeOptions, { builder = new L.U.FormBuilder(this, shapeOptions)
callback: function (e) {
if (this._controls.miniMap) this.renderControls()
this.eachVisibleDataLayer((datalayer) => {
datalayer.redraw()
})
},
})
const defaultShapeProperties = L.DomUtil.createFieldset( const defaultShapeProperties = L.DomUtil.createFieldset(
container, container,
L._('Default shape properties') L._('Default shape properties')
@ -1349,14 +1456,7 @@ L.U.Map.include({
], ],
] ]
builder = new L.U.FormBuilder(this, optionsFields, { builder = new L.U.FormBuilder(this, optionsFields)
callback: function (e) {
this.initCaptionBar()
if (e.helper.field === 'options.sortKey') {
this.eachDataLayer((datalayer) => datalayer.reindex())
}
},
})
const defaultProperties = L.DomUtil.createFieldset( const defaultProperties = L.DomUtil.createFieldset(
container, container,
L._('Default properties') L._('Default properties')
@ -1374,20 +1474,7 @@ L.U.Map.include({
'options.labelInteractive', 'options.labelInteractive',
'options.outlinkTarget', 'options.outlinkTarget',
] ]
builder = new L.U.FormBuilder(this, popupFields, { builder = new L.U.FormBuilder(this, popupFields)
callback: function (e) {
if (
e.helper.field === 'options.popupTemplate' ||
e.helper.field === 'options.popupContentTemplate' ||
e.helper.field === 'options.popupShape' ||
e.helper.field === 'options.outlinkTarget'
)
return
this.eachVisibleDataLayer((datalayer) => {
datalayer.redraw()
})
},
})
const popupFieldset = L.DomUtil.createFieldset( const popupFieldset = L.DomUtil.createFieldset(
container, container,
L._('Default interaction options') L._('Default interaction options')
@ -1441,10 +1528,7 @@ L.U.Map.include({
container, container,
L._('Custom background') L._('Custom background')
) )
builder = new L.U.FormBuilder(this, tilelayerFields, { builder = new L.U.FormBuilder(this, tilelayerFields)
callback: this.initTileLayers,
callbackContext: this,
})
customTilelayer.appendChild(builder.build()) customTilelayer.appendChild(builder.build())
}, },
@ -1492,10 +1576,7 @@ L.U.Map.include({
['options.overlay.tms', { handler: 'Switch', label: L._('TMS format') }], ['options.overlay.tms', { handler: 'Switch', label: L._('TMS format') }],
] ]
const overlay = L.DomUtil.createFieldset(container, L._('Custom overlay')) const overlay = L.DomUtil.createFieldset(container, L._('Custom overlay'))
builder = new L.U.FormBuilder(this, overlayFields, { builder = new L.U.FormBuilder(this, overlayFields)
callback: this.initTileLayers,
callbackContext: this,
})
overlay.appendChild(builder.build()) overlay.appendChild(builder.build())
}, },
@ -1522,10 +1603,7 @@ L.U.Map.include({
{ handler: 'BlurFloatInput', placeholder: L._('max East') }, { handler: 'BlurFloatInput', placeholder: L._('max East') },
], ],
] ]
const boundsBuilder = new L.U.FormBuilder(this, boundsFields, { const boundsBuilder = new L.U.FormBuilder(this, boundsFields)
callback: this.handleLimitBounds,
callbackContext: this,
})
limitBounds.appendChild(boundsBuilder.build()) limitBounds.appendChild(boundsBuilder.build())
const boundsButtons = L.DomUtil.create('div', 'button-bar half', limitBounds) const boundsButtons = L.DomUtil.create('div', 'button-bar half', limitBounds)
L.DomUtil.createButton( L.DomUtil.createButton(
@ -1584,13 +1662,9 @@ L.U.Map.include({
{ handler: 'Switch', label: L._('Autostart when map is loaded') }, { handler: 'Switch', label: L._('Autostart when map is loaded') },
], ],
] ]
const slideshowHandler = function () {
const slideshowBuilder = new L.U.FormBuilder(this, slideshowFields, function () {
this.slideshow.setOptions(this.options.slideshow) this.slideshow.setOptions(this.options.slideshow)
this.renderControls()
}
const slideshowBuilder = new L.U.FormBuilder(this, slideshowFields, {
callback: slideshowHandler,
callbackContext: this,
}) })
slideshow.appendChild(slideshowBuilder.build()) slideshow.appendChild(slideshowBuilder.build())
}, },
@ -1628,10 +1702,7 @@ L.U.Map.include({
{ handler: 'Switch', label: L._('Permanent credits background') }, { handler: 'Switch', label: L._('Permanent credits background') },
], ],
] ]
const creditsBuilder = new L.U.FormBuilder(this, creditsFields, { const creditsBuilder = new L.U.FormBuilder(this, creditsFields)
callback: this.renderControls,
callbackContext: this,
})
credits.appendChild(creditsBuilder.build()) credits.appendChild(creditsBuilder.build())
}, },

View file

@ -2,8 +2,9 @@
<div id="map"></div> <div id="map"></div>
<!-- djlint:off --> <!-- djlint:off -->
<script defer type="text/javascript"> <script defer type="text/javascript">
let MAP
window.addEventListener('DOMContentLoaded', (event) => { window.addEventListener('DOMContentLoaded', (event) => {
let MAP = new L.U.Map("map", {{ map_settings|notag|safe }}); MAP = new L.U.Map("map", {{ map_settings|notag|safe }});
}); });
</script> </script>
<!-- djlint:on --> <!-- djlint:on -->