refactor: Separate ui rendering from data updates
`U.SCHEMA` now contains an `impacts` key, which makes it possible to specify what part of the UI is impacted by data changes. A new `render` method has been added on `U.Map` and `U.DataLayer`, which is used to rerender the proper parts of the UI depending on the passed properties. `U.FormBuilder` calls this `render()` method (if present), during form changes.
This commit is contained in:
parent
99a0007ce0
commit
55cc7a098f
5 changed files with 416 additions and 330 deletions
|
@ -1,45 +1,52 @@
|
|||
import { translate } from './i18n.js'
|
||||
|
||||
// Possible impacts
|
||||
// ['ui', 'data', 'limit-bounds', 'datalayer-index', 'remote-data', 'background']
|
||||
|
||||
export const SCHEMA = {
|
||||
zoom: {
|
||||
type: Number,
|
||||
},
|
||||
scrollWheelZoom: {
|
||||
browsable: {
|
||||
type: Boolean,
|
||||
label: translate('Allow scroll wheel zoom?'),
|
||||
impacts: ['ui'],
|
||||
},
|
||||
scaleControl: {
|
||||
captionBar: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
label: translate('Do you want to display the scale control?'),
|
||||
label: translate('Do you want to display a caption bar?'),
|
||||
default: false,
|
||||
impacts: [],
|
||||
},
|
||||
captionMenus: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
label: translate('Do you want to display caption menus?'),
|
||||
default: true,
|
||||
},
|
||||
moreControl: {
|
||||
type: Boolean,
|
||||
label: translate('Do you want to display the «more» control?'),
|
||||
default: true,
|
||||
},
|
||||
miniMap: {
|
||||
type: Boolean,
|
||||
label: translate('Do you want to display a minimap?'),
|
||||
default: false,
|
||||
},
|
||||
displayPopupFooter: {
|
||||
type: Boolean,
|
||||
label: translate('Do you want to display popup footer?'),
|
||||
default: false,
|
||||
},
|
||||
onLoadPanel: {
|
||||
color: {
|
||||
impacts: ['data'],
|
||||
type: String,
|
||||
label: translate('Do you want to display a panel on load?'),
|
||||
choices: [
|
||||
['none', translate('None')],
|
||||
['caption', translate('Caption')],
|
||||
['databrowser', translate('Data browser')],
|
||||
['facet', translate('Facet search')],
|
||||
],
|
||||
default: 'none',
|
||||
handler: 'ColorPicker',
|
||||
label: translate('color'),
|
||||
helpEntries: 'colorValue',
|
||||
inheritable: true,
|
||||
default: 'DarkBlue',
|
||||
},
|
||||
dashArray: {
|
||||
impacts: ['data'],
|
||||
type: String,
|
||||
label: translate('dash array'),
|
||||
helpEntries: 'dashArray',
|
||||
inheritable: true,
|
||||
},
|
||||
datalayersControl: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
handler: 'DataLayersControl',
|
||||
label: translate('Display the data layers control'),
|
||||
default: true,
|
||||
},
|
||||
defaultView: {
|
||||
impacts: [], // no need to update the ui, only useful when loading the map
|
||||
type: String,
|
||||
label: translate('Default view'),
|
||||
choices: [
|
||||
|
@ -50,37 +57,80 @@ export const SCHEMA = {
|
|||
],
|
||||
default: 'center',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
label: translate('name'),
|
||||
},
|
||||
description: {
|
||||
label: translate('description'),
|
||||
impacts: ['ui'],
|
||||
type: 'Text',
|
||||
helpEntries: 'textFormatting',
|
||||
},
|
||||
licence: {
|
||||
type: String,
|
||||
label: translate('licence'),
|
||||
displayOnLoad: {
|
||||
type: Boolean,
|
||||
impacts: [],
|
||||
},
|
||||
tilelayer: {
|
||||
type: Object,
|
||||
displayPopupFooter: {
|
||||
type: Boolean,
|
||||
impacts: ['ui'],
|
||||
label: translate('Do you want to display popup footer?'),
|
||||
default: false,
|
||||
},
|
||||
overlay: {
|
||||
type: Object,
|
||||
easing: { impacts: [], type: Boolean, default: false },
|
||||
editinosmControl: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the control to open OpenStreetMap editor'),
|
||||
default: null,
|
||||
},
|
||||
limitBounds: {
|
||||
type: Object,
|
||||
embedControl: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the embed control'),
|
||||
default: true,
|
||||
},
|
||||
color: {
|
||||
facetKey: { impacts: ['ui'], type: String },
|
||||
fill: {
|
||||
impacts: ['data'],
|
||||
type: Boolean,
|
||||
label: translate('fill'),
|
||||
helpEntries: 'fill',
|
||||
inheritable: true,
|
||||
default: true,
|
||||
},
|
||||
fillColor: {
|
||||
impacts: ['data'],
|
||||
type: String,
|
||||
handler: 'ColorPicker',
|
||||
label: translate('color'),
|
||||
helpEntries: 'colorValue',
|
||||
label: translate('fill color'),
|
||||
helpEntries: 'fillColor',
|
||||
inheritable: true,
|
||||
default: 'DarkBlue',
|
||||
},
|
||||
fillOpacity: {
|
||||
impacts: ['data'],
|
||||
type: Number,
|
||||
min: 0.1,
|
||||
max: 1,
|
||||
step: 0.1,
|
||||
label: translate('fill opacity'),
|
||||
inheritable: true,
|
||||
default: 0.3,
|
||||
},
|
||||
filterKey: { impacts: [], type: String },
|
||||
fromZoom: {
|
||||
impacts: [], // not needed
|
||||
type: Number,
|
||||
label: translate('From zoom'),
|
||||
helpText: translate('Optional.'),
|
||||
},
|
||||
fullscreenControl: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the fullscreen control'),
|
||||
default: true,
|
||||
},
|
||||
iconClass: {
|
||||
impacts: ['data'],
|
||||
type: String,
|
||||
label: translate('Icon shape'),
|
||||
inheritable: true,
|
||||
|
@ -92,26 +142,8 @@ export const SCHEMA = {
|
|||
],
|
||||
default: 'Default',
|
||||
},
|
||||
iconUrl: {
|
||||
type: String,
|
||||
handler: 'IconUrl',
|
||||
label: translate('Icon symbol'),
|
||||
inheritable: true,
|
||||
// helpText: translate(
|
||||
// 'Symbol can be either a unicode character or an URL. You can use feature properties as variables: ex.: with "http://myserver.org/images/{name}.png", the {name} variable will be replaced by the "name" value of each marker.'
|
||||
// ),
|
||||
},
|
||||
smoothFactor: {
|
||||
type: Number,
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 0.5,
|
||||
label: translate('Simplify'),
|
||||
helpEntries: 'smoothFactor',
|
||||
inheritable: true,
|
||||
default: 1.0,
|
||||
},
|
||||
iconOpacity: {
|
||||
impacts: ['data'],
|
||||
type: Number,
|
||||
min: 0.1,
|
||||
max: 1,
|
||||
|
@ -120,132 +152,28 @@ export const SCHEMA = {
|
|||
inheritable: true,
|
||||
default: 1,
|
||||
},
|
||||
opacity: {
|
||||
type: Number,
|
||||
min: 0.1,
|
||||
max: 1,
|
||||
step: 0.1,
|
||||
label: translate('opacity'),
|
||||
iconUrl: {
|
||||
impacts: ['data'],
|
||||
type: String,
|
||||
handler: 'IconUrl',
|
||||
label: translate('Icon symbol'),
|
||||
inheritable: true,
|
||||
default: 0.5,
|
||||
},
|
||||
weight: {
|
||||
type: Number,
|
||||
min: 1,
|
||||
max: 20,
|
||||
step: 1,
|
||||
label: translate('weight'),
|
||||
inheritable: true,
|
||||
default: 3,
|
||||
},
|
||||
fill: {
|
||||
inCaption: {
|
||||
type: Boolean,
|
||||
label: translate('fill'),
|
||||
helpEntries: 'fill',
|
||||
impacts: ['ui'],
|
||||
},
|
||||
|
||||
interactive: {
|
||||
impacts: ['data'],
|
||||
type: Boolean,
|
||||
label: translate('Allow interactions'),
|
||||
helpEntries: 'interactive',
|
||||
inheritable: true,
|
||||
default: true,
|
||||
},
|
||||
fillColor: {
|
||||
type: String,
|
||||
handler: 'ColorPicker',
|
||||
label: translate('fill color'),
|
||||
helpEntries: 'fillColor',
|
||||
inheritable: true,
|
||||
},
|
||||
fillOpacity: {
|
||||
type: Number,
|
||||
min: 0.1,
|
||||
max: 1,
|
||||
step: 0.1,
|
||||
label: translate('fill opacity'),
|
||||
inheritable: true,
|
||||
default: 0.3,
|
||||
},
|
||||
dashArray: {
|
||||
type: String,
|
||||
label: translate('dash array'),
|
||||
helpEntries: 'dashArray',
|
||||
inheritable: true,
|
||||
},
|
||||
popupShape: {
|
||||
type: String,
|
||||
label: translate('Popup shape'),
|
||||
inheritable: true,
|
||||
choices: [
|
||||
['Default', translate('Popup')],
|
||||
['Large', translate('Popup (large)')],
|
||||
['Panel', translate('Side panel')],
|
||||
],
|
||||
default: 'Default',
|
||||
},
|
||||
popupTemplate: {
|
||||
type: String,
|
||||
label: translate('Popup content style'),
|
||||
inheritable: true,
|
||||
choices: [
|
||||
['Default', translate('Default')],
|
||||
['Table', translate('Table')],
|
||||
['GeoRSSImage', translate('GeoRSS (title + image)')],
|
||||
['GeoRSSLink', translate('GeoRSS (only link)')],
|
||||
['OSM', translate('OpenStreetMap')],
|
||||
],
|
||||
default: 'Default',
|
||||
},
|
||||
popupContentTemplate: {
|
||||
type: 'Text',
|
||||
label: translate('Popup content template'),
|
||||
helpEntries: ['dynamicProperties', 'textFormatting'],
|
||||
placeholder: '# {name}',
|
||||
inheritable: true,
|
||||
default: '# {name}\n{description}',
|
||||
},
|
||||
zoomTo: {
|
||||
type: Number,
|
||||
placeholder: translate('Inherit'),
|
||||
helpEntries: 'zoomTo',
|
||||
label: translate('Default zoom level'),
|
||||
inheritable: true,
|
||||
},
|
||||
captionBar: {
|
||||
type: Boolean,
|
||||
label: translate('Do you want to display a caption bar?'),
|
||||
default: false,
|
||||
},
|
||||
captionMenus: {
|
||||
type: Boolean,
|
||||
label: translate('Do you want to display caption menus?'),
|
||||
default: true,
|
||||
},
|
||||
slideshow: {
|
||||
type: Object,
|
||||
},
|
||||
sortKey: {
|
||||
type: String,
|
||||
},
|
||||
labelKey: {
|
||||
type: String,
|
||||
helpEntries: 'labelKey',
|
||||
placeholder: translate('Default: name'),
|
||||
label: translate('Label key'),
|
||||
inheritable: true,
|
||||
},
|
||||
filterKey: {
|
||||
type: String,
|
||||
},
|
||||
facetKey: {
|
||||
type: String,
|
||||
},
|
||||
slugKey: {
|
||||
type: String,
|
||||
},
|
||||
showLabel: {
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display label'),
|
||||
inheritable: true,
|
||||
default: false,
|
||||
},
|
||||
labelDirection: {
|
||||
impacts: ['data'],
|
||||
type: String,
|
||||
label: translate('Label direction'),
|
||||
inheritable: true,
|
||||
|
@ -259,11 +187,87 @@ export const SCHEMA = {
|
|||
default: 'auto',
|
||||
},
|
||||
labelInteractive: {
|
||||
impacts: ['data'],
|
||||
type: Boolean,
|
||||
label: translate('Labels are clickable'),
|
||||
inheritable: true,
|
||||
},
|
||||
labelKey: {
|
||||
impacts: ['data'],
|
||||
type: String,
|
||||
helpEntries: 'labelKey',
|
||||
placeholder: translate('Default: name'),
|
||||
label: translate('Label key'),
|
||||
inheritable: true,
|
||||
},
|
||||
licence: { impacts: ['ui'], type: String, label: translate('licence') },
|
||||
limitBounds: { impacts: ['limit-bounds'], type: Object },
|
||||
locateControl: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the locate control'),
|
||||
},
|
||||
longCredit: {
|
||||
impacts: ['ui'],
|
||||
type: 'Text',
|
||||
label: translate('Long credits'),
|
||||
helpEntries: ['longCredit', 'textFormatting'],
|
||||
},
|
||||
measureControl: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the measure control'),
|
||||
},
|
||||
miniMap: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
label: translate('Do you want to display a minimap?'),
|
||||
default: false,
|
||||
},
|
||||
moreControl: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
label: translate('Do you want to display the «more» control?'),
|
||||
default: true,
|
||||
},
|
||||
name: {
|
||||
impacts: ['ui', 'data'],
|
||||
type: String,
|
||||
label: translate('name'),
|
||||
},
|
||||
onLoadPanel: {
|
||||
impacts: [], // This is what happens during the map instantiation
|
||||
type: String,
|
||||
label: translate('Do you want to display a panel on load?'),
|
||||
choices: [
|
||||
['none', translate('None')],
|
||||
['caption', translate('Caption')],
|
||||
['databrowser', translate('Data browser')],
|
||||
['facet', translate('Facet search')],
|
||||
],
|
||||
default: 'none',
|
||||
},
|
||||
opacity: {
|
||||
impacts: ['data'],
|
||||
type: Number,
|
||||
min: 0.1,
|
||||
max: 1,
|
||||
step: 0.1,
|
||||
label: translate('opacity'),
|
||||
inheritable: true,
|
||||
default: 0.5,
|
||||
},
|
||||
outlink: {
|
||||
type: String,
|
||||
label: translate('Link to…'),
|
||||
helpEntries: 'outlink',
|
||||
placeholder: 'http://...',
|
||||
inheritable: true,
|
||||
},
|
||||
outlinkTarget: {
|
||||
impacts: [],
|
||||
type: String,
|
||||
label: translate('Open link in…'),
|
||||
inheritable: true,
|
||||
|
@ -274,115 +278,162 @@ export const SCHEMA = {
|
|||
['parent', translate('parent window')],
|
||||
],
|
||||
},
|
||||
shortCredit: {
|
||||
type: String,
|
||||
label: translate('Short credits'),
|
||||
helpEntries: ['shortCredit', 'textFormatting'],
|
||||
},
|
||||
longCredit: {
|
||||
type: 'Text',
|
||||
label: translate('Long credits'),
|
||||
helpEntries: ['longCredit', 'textFormatting'],
|
||||
},
|
||||
overlay: { impacts: ['background'], type: Object },
|
||||
permanentCredit: {
|
||||
impacts: ['ui'],
|
||||
type: 'Text',
|
||||
label: translate('Permanent credits'),
|
||||
helpEntries: ['permanentCredit', 'textFormatting'],
|
||||
},
|
||||
permanentCreditBackground: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
label: translate('Permanent credits background'),
|
||||
default: true,
|
||||
},
|
||||
zoomControl: {
|
||||
popupContentTemplate: {
|
||||
impacts: [], // not needed
|
||||
type: 'Text',
|
||||
label: translate('Popup content template'),
|
||||
helpEntries: ['dynamicProperties', 'textFormatting'],
|
||||
placeholder: '# {name}',
|
||||
inheritable: true,
|
||||
default: '# {name}\n{description}',
|
||||
},
|
||||
popupShape: {
|
||||
impacts: [], // not needed
|
||||
type: String,
|
||||
label: translate('Popup shape'),
|
||||
inheritable: true,
|
||||
choices: [
|
||||
['Default', translate('Popup')],
|
||||
['Large', translate('Popup (large)')],
|
||||
['Panel', translate('Side panel')],
|
||||
],
|
||||
default: 'Default',
|
||||
},
|
||||
popupTemplate: {
|
||||
impacts: [], // not needed
|
||||
type: String,
|
||||
label: translate('Popup content style'),
|
||||
inheritable: true,
|
||||
choices: [
|
||||
['Default', translate('Default')],
|
||||
['Table', translate('Table')],
|
||||
['GeoRSSImage', translate('GeoRSS (title + image)')],
|
||||
['GeoRSSLink', translate('GeoRSS (only link)')],
|
||||
['OSM', translate('OpenStreetMap')],
|
||||
],
|
||||
default: 'Default',
|
||||
},
|
||||
remoteData: {
|
||||
type: Object,
|
||||
impacts: ['remote-data'],
|
||||
},
|
||||
scaleControl: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the zoom control'),
|
||||
label: translate('Do you want to display the scale control?'),
|
||||
default: true,
|
||||
},
|
||||
datalayersControl: {
|
||||
scrollWheelZoom: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
handler: 'DataLayersControl',
|
||||
label: translate('Display the data layers control'),
|
||||
default: true,
|
||||
label: translate('Allow scroll wheel zoom?'),
|
||||
},
|
||||
searchControl: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the search control'),
|
||||
default: true,
|
||||
},
|
||||
locateControl: {
|
||||
shortCredit: {
|
||||
impacts: ['ui'],
|
||||
type: String,
|
||||
label: translate('Short credits'),
|
||||
helpEntries: ['shortCredit', 'textFormatting'],
|
||||
},
|
||||
showLabel: {
|
||||
impacts: ['data'],
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the locate control'),
|
||||
label: translate('Display label'),
|
||||
inheritable: true,
|
||||
default: false,
|
||||
},
|
||||
fullscreenControl: {
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the fullscreen control'),
|
||||
default: true,
|
||||
},
|
||||
editinosmControl: {
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the control to open OpenStreetMap editor'),
|
||||
default: null,
|
||||
},
|
||||
embedControl: {
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the embed control'),
|
||||
default: true,
|
||||
},
|
||||
measureControl: {
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the measure control'),
|
||||
},
|
||||
tilelayersControl: {
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the tile layers control'),
|
||||
slideshow: { impacts: ['ui'], type: Object },
|
||||
slugKey: { impacts: [], type: String },
|
||||
smoothFactor: {
|
||||
impacts: ['data'],
|
||||
type: Number,
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 0.5,
|
||||
label: translate('Simplify'),
|
||||
helpEntries: 'smoothFactor',
|
||||
inheritable: true,
|
||||
default: 1.0,
|
||||
},
|
||||
sortKey: { impacts: ['data', 'datalayer-index'], type: String },
|
||||
starControl: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the star map button'),
|
||||
},
|
||||
easing: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
interactive: {
|
||||
type: Boolean,
|
||||
label: translate('Allow interactions'),
|
||||
helpEntries: 'interactive',
|
||||
inheritable: true,
|
||||
default: true,
|
||||
},
|
||||
fromZoom: {
|
||||
type: Number,
|
||||
label: translate('From zoom'),
|
||||
helpText: translate('Optional.'),
|
||||
},
|
||||
toZoom: {
|
||||
type: Number,
|
||||
label: translate('To zoom'),
|
||||
helpText: translate('Optional.'),
|
||||
},
|
||||
stroke: {
|
||||
impacts: ['data'],
|
||||
type: Boolean,
|
||||
label: translate('stroke'),
|
||||
helpEntries: 'stroke',
|
||||
inheritable: true,
|
||||
default: true,
|
||||
},
|
||||
outlink: {
|
||||
label: translate('Link to…'),
|
||||
helpEntries: 'outlink',
|
||||
placeholder: 'http://...',
|
||||
tilelayer: { impacts: ['background'], type: Object },
|
||||
tilelayersControl: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the tile layers control'),
|
||||
},
|
||||
toZoom: {
|
||||
type: Number,
|
||||
impacts: [], // not needed
|
||||
label: translate('To zoom'),
|
||||
helpText: translate('Optional.'),
|
||||
},
|
||||
type: {
|
||||
type: 'String',
|
||||
impacts: ['data'],
|
||||
},
|
||||
weight: {
|
||||
impacts: ['data'],
|
||||
type: Number,
|
||||
min: 1,
|
||||
max: 20,
|
||||
step: 1,
|
||||
label: translate('weight'),
|
||||
inheritable: true,
|
||||
default: 3,
|
||||
},
|
||||
zoom: {
|
||||
impacts: [], // default zoom, doesn't need to be updated
|
||||
type: Number,
|
||||
},
|
||||
zoomControl: {
|
||||
impacts: ['ui'],
|
||||
type: Boolean,
|
||||
nullable: true,
|
||||
label: translate('Display the zoom control'),
|
||||
default: true,
|
||||
},
|
||||
zoomTo: {
|
||||
impacts: [], // not need to update the view
|
||||
type: Number,
|
||||
placeholder: translate('Inherit'),
|
||||
helpEntries: 'zoomTo',
|
||||
label: translate('Default zoom level'),
|
||||
inheritable: true,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -25,6 +25,31 @@ export function checkId(string) {
|
|||
return /^[A-Za-z0-9]{5}$/.test(string)
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the impacts for a given list of fields.
|
||||
*
|
||||
* Return a set containing the impacts.
|
||||
*
|
||||
* @param {fields} list[fields]
|
||||
* @returns Set[string]
|
||||
*/
|
||||
export function getImpactsFromSchema(fields) {
|
||||
return fields
|
||||
.map((field) => {
|
||||
// remove the option prefix for fields
|
||||
// And only keep the first part in case of a subfield
|
||||
// (e.g "options.limitBounds.foobar" will just return "limitBounds")
|
||||
return field.replace('options.', '').split('.')[0]
|
||||
})
|
||||
.reduce((acc, field) => {
|
||||
// retrieve the "impacts" field from the schema
|
||||
// and merge them together using sets
|
||||
const impacts = U.SCHEMA[field]?.impacts || []
|
||||
impacts.forEach((impact) => acc.add(impact))
|
||||
return acc
|
||||
}, new Set())
|
||||
}
|
||||
|
||||
/**
|
||||
* Import DOM purify, and initialize it.
|
||||
*
|
||||
|
@ -303,5 +328,5 @@ export function template(str, data) {
|
|||
value = value(data)
|
||||
}
|
||||
return value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1032,7 +1032,10 @@ U.FormBuilder = L.FormBuilder.extend({
|
|||
|
||||
setter: function (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
|
||||
if ('render' in this.obj) this.obj.render([field], this)
|
||||
}
|
||||
},
|
||||
|
||||
finish: function () {
|
||||
|
|
|
@ -69,6 +69,7 @@ U.Map = L.Map.extend({
|
|||
this.options.zoomControl = zoomControl !== undefined ? zoomControl : true
|
||||
this.options.fullscreenControl =
|
||||
fullscreenControl !== undefined ? fullscreenControl : true
|
||||
|
||||
this.datalayersFromQueryString = L.Util.queryString('datalayers')
|
||||
if (this.datalayersFromQueryString) {
|
||||
this.datalayersFromQueryString = this.datalayersFromQueryString
|
||||
|
@ -242,6 +243,42 @@ U.Map = L.Map.extend({
|
|||
this.on('click contextmenu.show', this.closeInplaceToolbar)
|
||||
},
|
||||
|
||||
render: function (fields) {
|
||||
let impacts = U.Utils.getImpactsFromSchema(fields)
|
||||
|
||||
for (let impact of impacts) {
|
||||
switch (impact) {
|
||||
case 'ui':
|
||||
this.initCaptionBar()
|
||||
this.renderEditToolbar()
|
||||
this.renderControls()
|
||||
break
|
||||
case 'data':
|
||||
this.redrawVisibleDataLayers()
|
||||
break
|
||||
case 'datalayer-index':
|
||||
this.reindexDataLayers()
|
||||
break
|
||||
case 'background':
|
||||
this.initTileLayers()
|
||||
break
|
||||
case 'bounds':
|
||||
this.handleLimitBounds()
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
reindexDataLayers: function () {
|
||||
this.eachDataLayer((datalayer) => datalayer.reindex())
|
||||
},
|
||||
|
||||
redrawVisibleDataLayers: function () {
|
||||
this.eachVisibleDataLayer((datalayer) => {
|
||||
datalayer.redraw()
|
||||
})
|
||||
},
|
||||
|
||||
setOptionsFromQueryString: function (options) {
|
||||
// This is not an editable option
|
||||
L.Util.setFromQueryString(options, 'editMode')
|
||||
|
@ -269,6 +306,8 @@ U.Map = L.Map.extend({
|
|||
}
|
||||
},
|
||||
|
||||
// Merge the given schema with the default one
|
||||
// Missing keys inside the schema are merged with the default ones.
|
||||
overrideSchema: function (schema) {
|
||||
for (const [key, extra] of Object.entries(schema)) {
|
||||
U.SCHEMA[key] = L.extend({}, U.SCHEMA[key], extra)
|
||||
|
@ -1128,13 +1167,7 @@ U.Map = L.Map.extend({
|
|||
'options.captionBar',
|
||||
'options.captionMenus',
|
||||
])
|
||||
builder = new U.FormBuilder(this, UIFields, {
|
||||
callback: function () {
|
||||
this.renderControls()
|
||||
this.initCaptionBar()
|
||||
},
|
||||
callbackContext: this,
|
||||
})
|
||||
builder = new U.FormBuilder(this, UIFields)
|
||||
const controlsOptions = L.DomUtil.createFieldset(
|
||||
container,
|
||||
L._('User interface options')
|
||||
|
@ -1157,14 +1190,7 @@ U.Map = L.Map.extend({
|
|||
'options.dashArray',
|
||||
]
|
||||
|
||||
builder = new U.FormBuilder(this, shapeOptions, {
|
||||
callback: function (e) {
|
||||
if (this._controls.miniMap) this.renderControls()
|
||||
this.eachVisibleDataLayer((datalayer) => {
|
||||
datalayer.redraw()
|
||||
})
|
||||
},
|
||||
})
|
||||
builder = new U.FormBuilder(this, shapeOptions)
|
||||
const defaultShapeProperties = L.DomUtil.createFieldset(
|
||||
container,
|
||||
L._('Default shape properties')
|
||||
|
@ -1217,14 +1243,7 @@ U.Map = L.Map.extend({
|
|||
],
|
||||
]
|
||||
|
||||
builder = new U.FormBuilder(this, optionsFields, {
|
||||
callback: function (e) {
|
||||
this.initCaptionBar()
|
||||
if (e.helper.field === 'options.sortKey') {
|
||||
this.eachDataLayer((datalayer) => datalayer.reindex())
|
||||
}
|
||||
},
|
||||
})
|
||||
builder = new U.FormBuilder(this, optionsFields)
|
||||
const defaultProperties = L.DomUtil.createFieldset(
|
||||
container,
|
||||
L._('Default properties')
|
||||
|
@ -1242,20 +1261,7 @@ U.Map = L.Map.extend({
|
|||
'options.labelInteractive',
|
||||
'options.outlinkTarget',
|
||||
]
|
||||
builder = new 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()
|
||||
})
|
||||
},
|
||||
})
|
||||
builder = new U.FormBuilder(this, popupFields)
|
||||
const popupFieldset = L.DomUtil.createFieldset(
|
||||
container,
|
||||
L._('Default interaction options')
|
||||
|
@ -1309,10 +1315,7 @@ U.Map = L.Map.extend({
|
|||
container,
|
||||
L._('Custom background')
|
||||
)
|
||||
builder = new U.FormBuilder(this, tilelayerFields, {
|
||||
callback: this.initTileLayers,
|
||||
callbackContext: this,
|
||||
})
|
||||
builder = new U.FormBuilder(this, tilelayerFields)
|
||||
customTilelayer.appendChild(builder.build())
|
||||
},
|
||||
|
||||
|
@ -1360,10 +1363,7 @@ U.Map = L.Map.extend({
|
|||
['options.overlay.tms', { handler: 'Switch', label: L._('TMS format') }],
|
||||
]
|
||||
const overlay = L.DomUtil.createFieldset(container, L._('Custom overlay'))
|
||||
builder = new U.FormBuilder(this, overlayFields, {
|
||||
callback: this.initTileLayers,
|
||||
callbackContext: this,
|
||||
})
|
||||
builder = new U.FormBuilder(this, overlayFields)
|
||||
overlay.appendChild(builder.build())
|
||||
},
|
||||
|
||||
|
@ -1390,10 +1390,7 @@ U.Map = L.Map.extend({
|
|||
{ handler: 'BlurFloatInput', placeholder: L._('max East') },
|
||||
],
|
||||
]
|
||||
const boundsBuilder = new U.FormBuilder(this, boundsFields, {
|
||||
callback: this.handleLimitBounds,
|
||||
callbackContext: this,
|
||||
})
|
||||
const boundsBuilder = new U.FormBuilder(this, boundsFields)
|
||||
limitBounds.appendChild(boundsBuilder.build())
|
||||
const boundsButtons = L.DomUtil.create('div', 'button-bar half', limitBounds)
|
||||
L.DomUtil.createButton(
|
||||
|
@ -1454,7 +1451,6 @@ U.Map = L.Map.extend({
|
|||
]
|
||||
const slideshowHandler = function () {
|
||||
this.slideshow.setOptions(this.options.slideshow)
|
||||
this.renderControls()
|
||||
}
|
||||
const slideshowBuilder = new U.FormBuilder(this, slideshowFields, {
|
||||
callback: slideshowHandler,
|
||||
|
@ -1472,10 +1468,7 @@ U.Map = L.Map.extend({
|
|||
'options.permanentCredit',
|
||||
'options.permanentCreditBackground',
|
||||
]
|
||||
const creditsBuilder = new U.FormBuilder(this, creditsFields, {
|
||||
callback: this.renderControls,
|
||||
callbackContext: this,
|
||||
})
|
||||
const creditsBuilder = new U.FormBuilder(this, creditsFields)
|
||||
credits.appendChild(creditsBuilder.build())
|
||||
},
|
||||
|
||||
|
|
|
@ -254,16 +254,18 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|||
},
|
||||
|
||||
onEdit: function (field, builder) {
|
||||
// Only compute the breaks if we're dealing with choropleth
|
||||
if (!field.startsWith('options.choropleth')) return
|
||||
// If user touches the breaks, then force manual mode
|
||||
if (field === 'options.choropleth.breaks') {
|
||||
this.datalayer.options.choropleth.mode = 'manual'
|
||||
builder.helpers['options.choropleth.mode'].fetch()
|
||||
if (builder) builder.helpers['options.choropleth.mode'].fetch()
|
||||
}
|
||||
this.computeBreaks()
|
||||
// If user changes the mode or the number of classes,
|
||||
// then update the breaks input value
|
||||
if (field === 'options.choropleth.mode' || field === 'options.choropleth.classes') {
|
||||
builder.helpers['options.choropleth.breaks'].fetch()
|
||||
if (builder) builder.helpers['options.choropleth.breaks'].fetch()
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -594,6 +596,31 @@ U.DataLayer = L.Evented.extend({
|
|||
if (this.autoLoaded()) this.map.on('zoomend', this.onZoomEnd, this)
|
||||
},
|
||||
|
||||
render: function (fields, builder) {
|
||||
let impacts = U.Utils.getImpactsFromSchema(fields)
|
||||
|
||||
for (let impact of impacts) {
|
||||
switch (impact) {
|
||||
case 'ui':
|
||||
this.map.updateDatalayersControl()
|
||||
break
|
||||
case 'data':
|
||||
if (fields.includes('options.type')) {
|
||||
this.resetLayer()
|
||||
}
|
||||
this.hide()
|
||||
fields.forEach((field) => {
|
||||
this.layer.onEdit(field, builder)
|
||||
})
|
||||
this.show()
|
||||
break
|
||||
case 'remote-data':
|
||||
this.fetchRemoteData()
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onMoveEnd: function (e) {
|
||||
if (this.isRemoteLayer() && this.showAtZoom()) this.fetchRemoteData()
|
||||
},
|
||||
|
@ -1189,29 +1216,18 @@ U.DataLayer = L.Evented.extend({
|
|||
const title = L.DomUtil.add('h3', '', container, L._('Layer properties'))
|
||||
let builder = new U.FormBuilder(this, metadataFields, {
|
||||
callback: function (e) {
|
||||
this.map.updateDatalayersControl()
|
||||
if (e.helper.field === 'options.type') {
|
||||
this.resetLayer()
|
||||
this.edit()
|
||||
}
|
||||
},
|
||||
})
|
||||
container.appendChild(builder.build())
|
||||
|
||||
const redrawCallback = function (e) {
|
||||
const field = e.helper.field,
|
||||
builder = e.helper.builder
|
||||
this.hide()
|
||||
this.layer.onEdit(field, builder)
|
||||
this.show()
|
||||
}
|
||||
|
||||
const layerOptions = this.layer.getEditableOptions()
|
||||
|
||||
if (layerOptions.length) {
|
||||
builder = new U.FormBuilder(this, layerOptions, {
|
||||
id: 'datalayer-layer-properties',
|
||||
callback: redrawCallback,
|
||||
})
|
||||
const layerProperties = L.DomUtil.createFieldset(
|
||||
container,
|
||||
|
@ -1235,7 +1251,6 @@ U.DataLayer = L.Evented.extend({
|
|||
|
||||
builder = new U.FormBuilder(this, shapeOptions, {
|
||||
id: 'datalayer-advanced-properties',
|
||||
callback: redrawCallback,
|
||||
})
|
||||
const shapeProperties = L.DomUtil.createFieldset(container, L._('Shape properties'))
|
||||
shapeProperties.appendChild(builder.build())
|
||||
|
@ -1251,7 +1266,6 @@ U.DataLayer = L.Evented.extend({
|
|||
|
||||
builder = new U.FormBuilder(this, optionsFields, {
|
||||
id: 'datalayer-advanced-properties',
|
||||
callback: redrawCallback,
|
||||
})
|
||||
const advancedProperties = L.DomUtil.createFieldset(
|
||||
container,
|
||||
|
@ -1269,7 +1283,7 @@ U.DataLayer = L.Evented.extend({
|
|||
'options.outlinkTarget',
|
||||
'options.interactive',
|
||||
]
|
||||
builder = new U.FormBuilder(this, popupFields, { callback: redrawCallback })
|
||||
builder = new U.FormBuilder(this, popupFields)
|
||||
const popupFieldset = L.DomUtil.createFieldset(
|
||||
container,
|
||||
L._('Interaction options')
|
||||
|
|
Loading…
Reference in a new issue