umap-project 2.1.3__py3-none-any.whl → 2.2.0b0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of umap-project might be problematic. Click here for more details.

Files changed (196) hide show
  1. umap/__init__.py +1 -1
  2. umap/context_processors.py +1 -0
  3. umap/locale/br/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/en/LC_MESSAGES/django.po +32 -32
  5. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/ms/LC_MESSAGES/django.mo +0 -0
  8. umap/migrations/0020_alter_tilelayer_url_template.py +19 -0
  9. umap/migrations/0021_remove_map_description.py +16 -0
  10. umap/models.py +8 -6
  11. umap/settings/base.py +1 -0
  12. umap/static/umap/base.css +29 -151
  13. umap/static/umap/content.css +7 -25
  14. umap/static/umap/css/icon.css +112 -0
  15. umap/static/umap/css/panel.css +140 -0
  16. umap/static/umap/img/16-white.svg +5 -1
  17. umap/static/umap/img/16.svg +7 -4
  18. umap/static/umap/img/24-white.svg +3 -1
  19. umap/static/umap/img/24.svg +3 -4
  20. umap/static/umap/img/source/16-white.svg +176 -940
  21. umap/static/umap/img/source/16.svg +8 -5
  22. umap/static/umap/img/source/24-white.svg +5 -3
  23. umap/static/umap/img/source/24.svg +6 -7
  24. umap/static/umap/js/modules/browser.js +82 -73
  25. umap/static/umap/js/modules/dompurify.js +12 -0
  26. umap/static/umap/js/modules/facets.js +148 -0
  27. umap/static/umap/js/modules/global.js +9 -1
  28. umap/static/umap/js/modules/i18n.js +7 -0
  29. umap/static/umap/js/modules/orderable.js +84 -0
  30. umap/static/umap/js/modules/panel.js +76 -0
  31. umap/static/umap/js/modules/request.js +0 -1
  32. umap/static/umap/js/modules/schema.js +324 -223
  33. umap/static/umap/js/modules/urls.js +1 -16
  34. umap/static/umap/js/modules/utils.js +340 -0
  35. umap/static/umap/js/umap.controls.js +183 -330
  36. umap/static/umap/js/umap.core.js +60 -364
  37. umap/static/umap/js/umap.datalayer.permissions.js +1 -1
  38. umap/static/umap/js/umap.features.js +60 -40
  39. umap/static/umap/js/umap.forms.js +111 -25
  40. umap/static/umap/js/umap.icon.js +11 -4
  41. umap/static/umap/js/umap.importer.js +24 -17
  42. umap/static/umap/js/umap.js +170 -145
  43. umap/static/umap/js/umap.layer.js +71 -40
  44. umap/static/umap/js/umap.permissions.js +9 -11
  45. umap/static/umap/js/umap.popup.js +10 -21
  46. umap/static/umap/js/umap.share.js +11 -8
  47. umap/static/umap/js/umap.tableeditor.js +4 -6
  48. umap/static/umap/js/umap.ui.js +0 -51
  49. umap/static/umap/locale/am_ET.js +242 -227
  50. umap/static/umap/locale/am_ET.json +18 -7
  51. umap/static/umap/locale/ar.js +242 -227
  52. umap/static/umap/locale/ar.json +18 -7
  53. umap/static/umap/locale/ast.js +242 -227
  54. umap/static/umap/locale/ast.json +18 -7
  55. umap/static/umap/locale/bg.js +242 -227
  56. umap/static/umap/locale/bg.json +18 -7
  57. umap/static/umap/locale/br.js +252 -237
  58. umap/static/umap/locale/br.json +22 -11
  59. umap/static/umap/locale/ca.js +242 -227
  60. umap/static/umap/locale/ca.json +18 -7
  61. umap/static/umap/locale/cs_CZ.js +242 -227
  62. umap/static/umap/locale/cs_CZ.json +18 -7
  63. umap/static/umap/locale/da.js +242 -227
  64. umap/static/umap/locale/da.json +18 -7
  65. umap/static/umap/locale/de.js +242 -227
  66. umap/static/umap/locale/de.json +18 -7
  67. umap/static/umap/locale/el.js +242 -227
  68. umap/static/umap/locale/el.json +18 -7
  69. umap/static/umap/locale/en.js +242 -234
  70. umap/static/umap/locale/en.json +19 -8
  71. umap/static/umap/locale/en_US.json +18 -7
  72. umap/static/umap/locale/es.js +242 -227
  73. umap/static/umap/locale/es.json +18 -7
  74. umap/static/umap/locale/et.js +242 -227
  75. umap/static/umap/locale/et.json +18 -7
  76. umap/static/umap/locale/eu.js +227 -199
  77. umap/static/umap/locale/eu.json +1 -1
  78. umap/static/umap/locale/fa_IR.js +242 -227
  79. umap/static/umap/locale/fa_IR.json +18 -7
  80. umap/static/umap/locale/fi.js +242 -227
  81. umap/static/umap/locale/fi.json +18 -7
  82. umap/static/umap/locale/fr.js +242 -234
  83. umap/static/umap/locale/fr.json +18 -7
  84. umap/static/umap/locale/gl.js +242 -227
  85. umap/static/umap/locale/gl.json +18 -7
  86. umap/static/umap/locale/he.js +242 -227
  87. umap/static/umap/locale/he.json +18 -7
  88. umap/static/umap/locale/hr.js +242 -227
  89. umap/static/umap/locale/hr.json +18 -7
  90. umap/static/umap/locale/hu.js +242 -234
  91. umap/static/umap/locale/hu.json +18 -7
  92. umap/static/umap/locale/id.js +242 -227
  93. umap/static/umap/locale/id.json +18 -7
  94. umap/static/umap/locale/is.js +242 -227
  95. umap/static/umap/locale/is.json +18 -7
  96. umap/static/umap/locale/it.js +242 -234
  97. umap/static/umap/locale/it.json +18 -7
  98. umap/static/umap/locale/ja.js +242 -227
  99. umap/static/umap/locale/ja.json +18 -7
  100. umap/static/umap/locale/ko.js +242 -227
  101. umap/static/umap/locale/ko.json +18 -7
  102. umap/static/umap/locale/lt.js +242 -227
  103. umap/static/umap/locale/lt.json +18 -7
  104. umap/static/umap/locale/ms.js +242 -234
  105. umap/static/umap/locale/ms.json +19 -8
  106. umap/static/umap/locale/nl.js +245 -230
  107. umap/static/umap/locale/nl.json +18 -7
  108. umap/static/umap/locale/no.js +242 -227
  109. umap/static/umap/locale/no.json +18 -7
  110. umap/static/umap/locale/pl.js +242 -227
  111. umap/static/umap/locale/pl.json +18 -7
  112. umap/static/umap/locale/pl_PL.json +18 -7
  113. umap/static/umap/locale/pt.js +242 -227
  114. umap/static/umap/locale/pt.json +18 -7
  115. umap/static/umap/locale/pt_BR.js +242 -227
  116. umap/static/umap/locale/pt_BR.json +18 -7
  117. umap/static/umap/locale/pt_PT.js +242 -227
  118. umap/static/umap/locale/pt_PT.json +18 -7
  119. umap/static/umap/locale/ro.js +242 -227
  120. umap/static/umap/locale/ro.json +18 -7
  121. umap/static/umap/locale/ru.js +242 -227
  122. umap/static/umap/locale/ru.json +18 -7
  123. umap/static/umap/locale/si.js +1 -1
  124. umap/static/umap/locale/si.json +1 -1
  125. umap/static/umap/locale/sk_SK.js +242 -227
  126. umap/static/umap/locale/sk_SK.json +18 -7
  127. umap/static/umap/locale/sl.js +242 -227
  128. umap/static/umap/locale/sl.json +18 -7
  129. umap/static/umap/locale/sr.js +242 -227
  130. umap/static/umap/locale/sr.json +18 -7
  131. umap/static/umap/locale/sv.js +242 -227
  132. umap/static/umap/locale/sv.json +18 -7
  133. umap/static/umap/locale/th_TH.js +242 -227
  134. umap/static/umap/locale/th_TH.json +18 -7
  135. umap/static/umap/locale/tr.js +242 -227
  136. umap/static/umap/locale/tr.json +18 -7
  137. umap/static/umap/locale/uk_UA.js +242 -227
  138. umap/static/umap/locale/uk_UA.json +18 -7
  139. umap/static/umap/locale/vi.js +242 -227
  140. umap/static/umap/locale/vi.json +18 -7
  141. umap/static/umap/locale/vi_VN.json +18 -7
  142. umap/static/umap/locale/zh.js +242 -227
  143. umap/static/umap/locale/zh.json +18 -7
  144. umap/static/umap/locale/zh_CN.json +18 -7
  145. umap/static/umap/locale/zh_TW.Big5.json +18 -7
  146. umap/static/umap/locale/zh_TW.js +242 -234
  147. umap/static/umap/locale/zh_TW.json +18 -7
  148. umap/static/umap/map.css +114 -265
  149. umap/static/umap/test/DataLayer.js +463 -0
  150. umap/static/umap/test/Feature.js +0 -226
  151. umap/static/umap/test/TableEditor.js +104 -0
  152. umap/static/umap/test/Util.js +0 -521
  153. umap/static/umap/test/index.html +0 -1
  154. umap/static/umap/unittests/URLs.js +1 -1
  155. umap/static/umap/unittests/utils.js +610 -0
  156. umap/static/umap/vars.css +9 -0
  157. umap/static/umap/vendors/dompurify/purify.es.mjs +1525 -0
  158. umap/static/umap/vendors/iconlayers/iconLayers.js +1 -1
  159. umap/templates/umap/css.html +2 -0
  160. umap/templates/umap/js.html +0 -1
  161. umap/templates/umap/map_detail.html +4 -0
  162. umap/templates/umap/map_table.html +12 -10
  163. umap/templatetags/umap_tags.py +5 -0
  164. umap/tests/integration/conftest.py +12 -1
  165. umap/tests/integration/test_anonymous_owned_map.py +6 -5
  166. umap/tests/integration/test_browser.py +12 -25
  167. umap/tests/integration/test_choropleth.py +1 -1
  168. umap/tests/integration/test_dashboard.py +10 -0
  169. umap/tests/integration/test_datalayer.py +8 -6
  170. umap/tests/integration/test_edit_datalayer.py +24 -19
  171. umap/tests/integration/test_edit_map.py +182 -2
  172. umap/tests/integration/test_edit_marker.py +120 -0
  173. umap/tests/integration/test_edit_polygon.py +122 -0
  174. umap/tests/integration/test_facets_browser.py +104 -14
  175. umap/tests/integration/test_import.py +70 -20
  176. umap/tests/integration/test_map.py +19 -17
  177. umap/tests/integration/test_map_list.py +28 -0
  178. umap/tests/integration/test_owned_map.py +10 -10
  179. umap/tests/integration/test_picto.py +5 -5
  180. umap/tests/integration/test_querystring.py +9 -15
  181. umap/tests/integration/test_slideshow.py +0 -5
  182. umap/tests/integration/test_statics.py +3 -2
  183. umap/tests/integration/test_tableeditor.py +1 -5
  184. umap/tests/integration/test_view_marker.py +64 -0
  185. umap/tests/integration/test_view_polygon.py +59 -0
  186. umap/tests/integration/test_view_polyline.py +51 -0
  187. umap/tests/test_map_views.py +13 -0
  188. {umap_project-2.1.3.dist-info → umap_project-2.2.0b0.dist-info}/METADATA +8 -8
  189. {umap_project-2.1.3.dist-info → umap_project-2.2.0b0.dist-info}/RECORD +194 -178
  190. umap/static/umap/vendors/dompurify/purify.min.js +0 -3
  191. umap/static/umap/vendors/dompurify/purify.min.js.map +0 -1
  192. /umap/tests/integration/{test_polygon.py → test_draw_polygon.py} +0 -0
  193. /umap/tests/integration/{test_polyline.py → test_draw_polyline.py} +0 -0
  194. {umap_project-2.1.3.dist-info → umap_project-2.2.0b0.dist-info}/WHEEL +0 -0
  195. {umap_project-2.1.3.dist-info → umap_project-2.2.0b0.dist-info}/entry_points.txt +0 -0
  196. {umap_project-2.1.3.dist-info → umap_project-2.2.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -59,15 +59,14 @@ U.FeatureMixin = {
59
59
  getPermalink: function () {
60
60
  const slug = this.getSlug()
61
61
  if (slug)
62
- return `${L.Util.getBaseUrl()}?${L.Util.buildQueryString({ feature: slug })}${
62
+ return `${U.Utils.getBaseUrl()}?${U.Utils.buildQueryString({ feature: slug })}${
63
63
  window.location.hash
64
64
  }`
65
65
  },
66
66
 
67
67
  view: function (e) {
68
- if (this.map.editEnabled) return
69
- const outlink = this.getOption('outlink'),
70
- target = this.getOption('outlinkTarget')
68
+ const outlink = this.getOption('outlink')
69
+ const target = this.getOption('outlinkTarget')
71
70
  if (outlink) {
72
71
  switch (target) {
73
72
  case 'self':
@@ -77,31 +76,38 @@ U.FeatureMixin = {
77
76
  window.top.location = outlink
78
77
  break
79
78
  default:
80
- const win = window.open(this.properties._umap_options.outlink)
79
+ window.open(this.properties._umap_options.outlink)
81
80
  }
82
81
  return
83
82
  }
84
83
  // TODO deal with an event instead?
85
- if (this.map.slideshow) this.map.slideshow.current = this
84
+ if (this.map.slideshow) {
85
+ this.map.slideshow.current = this
86
+ }
86
87
  this.map.currentFeature = this
87
88
  this.attachPopup()
88
- this.openPopup((e && e.latlng) || this.getCenter())
89
+ this.openPopup(e?.latlng || this.getCenter())
90
+ },
91
+
92
+ render: function (fields) {
93
+ const impactData = fields.some((field) => {
94
+ return field.startsWith('properties.')
95
+ })
96
+ if (impactData) {
97
+ if (this.map.currentFeature === this) {
98
+ this.view()
99
+ }
100
+ }
89
101
  },
90
102
 
91
103
  openPopup: function () {
92
- if (this.map.editEnabled) return
93
104
  this.parentClass.prototype.openPopup.apply(this, arguments)
94
105
  },
95
106
 
96
107
  edit: function (e) {
97
108
  if (!this.map.editEnabled || this.isReadOnly()) return
98
109
  const container = L.DomUtil.create('div', 'umap-feature-container')
99
- L.DomUtil.add(
100
- 'h3',
101
- `umap-feature-properties ${this.getClassName()}`,
102
- container,
103
- L._('Feature properties')
104
- )
110
+ L.DomUtil.createTitle(container, L._('Feature properties'), this.getClassName())
105
111
 
106
112
  let builder = new U.FormBuilder(
107
113
  this,
@@ -131,13 +137,13 @@ U.FeatureMixin = {
131
137
  callback: this._redraw, // In case we have dynamic options…
132
138
  })
133
139
  container.appendChild(builder.build())
134
- this.map.ui.once('panel:ready', () => {
135
- builder.helpers['properties.name'].input.focus()
136
- })
137
140
  this.appendEditFieldsets(container)
138
141
  const advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions'))
139
142
  this.getAdvancedEditActions(advancedActions)
140
- this.map.ui.openPanel({ data: { html: container }, className: 'dark' })
143
+ const onLoad = this.map.editPanel.open({ content: container })
144
+ onLoad.then(() => {
145
+ builder.helpers['properties.name'].input.focus()
146
+ })
141
147
  this.map.editedFeature = this
142
148
  if (!this.isOnScreen()) this.zoomTo(e)
143
149
  },
@@ -149,7 +155,7 @@ U.FeatureMixin = {
149
155
  L._('Delete'),
150
156
  function (e) {
151
157
  L.DomEvent.stop(e)
152
- if (this.confirmDelete()) this.map.ui.closePanel()
158
+ if (this.confirmDelete()) this.map.editPanel.close()
153
159
  },
154
160
  this
155
161
  )
@@ -204,7 +210,8 @@ U.FeatureMixin = {
204
210
  if (fallback === undefined) fallback = this.datalayer.options.name
205
211
  const key = this.getOption('labelKey') || 'name'
206
212
  // Variables mode.
207
- if (L.Util.hasVar(key)) return L.Util.greedyTemplate(key, this.extendedProperties())
213
+ if (U.Utils.hasVar(key))
214
+ return U.Utils.greedyTemplate(key, this.extendedProperties())
208
215
  // Simple mode.
209
216
  return this.properties[key] || this.properties.title || fallback
210
217
  },
@@ -291,7 +298,7 @@ U.FeatureMixin = {
291
298
  let value = fallback
292
299
  if (typeof this.staticOptions[option] !== 'undefined') {
293
300
  value = this.staticOptions[option]
294
- } else if (L.Util.usableOption(this.properties._umap_options, option)) {
301
+ } else if (U.Utils.usableOption(this.properties._umap_options, option)) {
295
302
  value = this.properties._umap_options[option]
296
303
  } else if (this.datalayer) {
297
304
  value = this.datalayer.getOption(option, this)
@@ -304,23 +311,22 @@ U.FeatureMixin = {
304
311
  getDynamicOption: function (option, fallback) {
305
312
  let value = this.getOption(option, fallback)
306
313
  // There is a variable inside.
307
- if (L.Util.hasVar(value)) {
308
- value = L.Util.greedyTemplate(value, this.properties, true)
309
- if (L.Util.hasVar(value)) value = this.map.getDefaultOption(option)
314
+ if (U.Utils.hasVar(value)) {
315
+ value = U.Utils.greedyTemplate(value, this.properties, true)
316
+ if (U.Utils.hasVar(value)) value = this.map.getDefaultOption(option)
310
317
  }
311
318
  return value
312
319
  },
313
320
 
314
- zoomTo: function (e) {
315
- e = e || {}
316
- const easing = e.easing !== undefined ? e.easing : this.map.getOption('easing')
321
+ zoomTo: function ({ easing, latlng, callback } = {}) {
322
+ if (easing === undefined) easing = this.map.getOption('easing')
323
+ if (callback) this.map.once('moveend', callback.call(this))
317
324
  if (easing) {
318
325
  this.map.flyTo(this.getCenter(), this.getBestZoom())
319
326
  } else {
320
- const latlng = e.latlng || this.getCenter()
327
+ latlng = latlng || this.getCenter()
321
328
  this.map.setView(latlng, this.getBestZoom() || this.map.getZoom())
322
329
  }
323
- if (e.callback) e.callback.call(this)
324
330
  },
325
331
 
326
332
  getBestZoom: function () {
@@ -466,7 +472,7 @@ U.FeatureMixin = {
466
472
  this.parentClass.prototype.onRemove.call(this, map)
467
473
  if (this.map.editedFeature === this) {
468
474
  this.endEdit()
469
- this.map.ui.closePanel()
475
+ this.map.editPanel.close()
470
476
  }
471
477
  },
472
478
 
@@ -485,7 +491,7 @@ U.FeatureMixin = {
485
491
  options.permanent = showLabel === true
486
492
  this.unbindTooltip()
487
493
  if ((showLabel === true || showLabel === null) && displayName)
488
- this.bindTooltip(L.Util.escapeHTML(displayName), options)
494
+ this.bindTooltip(U.Utils.escapeHTML(displayName), options)
489
495
  },
490
496
 
491
497
  matchFilter: function (filter, keys) {
@@ -498,11 +504,24 @@ U.FeatureMixin = {
498
504
  },
499
505
 
500
506
  matchFacets: function () {
501
- const facets = this.map.facets
502
- for (const [property, expected] of Object.entries(facets)) {
503
- if (expected.length) {
504
- let value = this.properties[property]
505
- if (!value || !expected.includes(value)) return false
507
+ const selected = this.map.facets.selected
508
+ for (let [name, { type, min, max, choices }] of Object.entries(selected)) {
509
+ let value = this.properties[name]
510
+ let parser = this.map.facets.getParser(type)
511
+ value = parser(value)
512
+ switch (type) {
513
+ case 'date':
514
+ case 'datetime':
515
+ case 'number':
516
+ min = parser(min)
517
+ max = parser(max)
518
+ if (!isNaN(min) && !isNaN(value) && min > value) return false
519
+ if (!isNaN(max) && !isNaN(value) && max < value) return false
520
+ break
521
+ default:
522
+ value = value || L._('<empty value>')
523
+ if (choices?.length && !choices.includes(value)) return false
524
+ break
506
525
  }
507
526
  }
508
527
  return true
@@ -533,7 +552,8 @@ U.FeatureMixin = {
533
552
  extendedProperties: function () {
534
553
  // Include context properties
535
554
  properties = this.map.getGeoContext()
536
- if (L.locale) properties.locale = L.locale
555
+ const locale = L.getLocale()
556
+ if (locale) properties.locale = locale
537
557
  if (L.lang) properties.lang = L.lang
538
558
  properties.rank = this.getRank() + 1
539
559
  if (this.hasGeom()) {
@@ -757,7 +777,7 @@ U.PathMixin = {
757
777
  if (this.map.editEnabled) {
758
778
  if (this.editEnabled()) {
759
779
  this.endEdit()
760
- this.map.ui.closePanel()
780
+ this.map.editPanel.close()
761
781
  } else {
762
782
  this.edit(e)
763
783
  }
@@ -1058,7 +1078,7 @@ U.Polyline = L.Polyline.extend({
1058
1078
  const geojson = this.toGeoJSON()
1059
1079
  geojson.geometry.type = 'Polygon'
1060
1080
  geojson.geometry.coordinates = [
1061
- L.Util.flattenCoordinates(geojson.geometry.coordinates),
1081
+ U.Utils.flattenCoordinates(geojson.geometry.coordinates),
1062
1082
  ]
1063
1083
  const polygon = this.datalayer.geojsonToFeatures(geojson)
1064
1084
  polygon.edit()
@@ -1198,7 +1218,7 @@ U.Polygon = L.Polygon.extend({
1198
1218
  toPolyline: function () {
1199
1219
  const geojson = this.toGeoJSON()
1200
1220
  geojson.geometry.type = 'LineString'
1201
- geojson.geometry.coordinates = L.Util.flattenCoordinates(
1221
+ geojson.geometry.coordinates = U.Utils.flattenCoordinates(
1202
1222
  geojson.geometry.coordinates
1203
1223
  )
1204
1224
  const polyline = this.datalayer.geojsonToFeatures(geojson)
@@ -492,8 +492,8 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
492
492
  this.buildTabs()
493
493
  const value = this.value()
494
494
  if (U.Icon.RECENT.length) this.showRecentTab()
495
- else if (!value || L.Util.isPath(value)) this.showSymbolsTab()
496
- else if (L.Util.isRemoteUrl(value) || L.Util.isDataImage(value)) this.showURLTab()
495
+ else if (!value || U.Utils.isPath(value)) this.showSymbolsTab()
496
+ else if (U.Utils.isRemoteUrl(value) || U.Utils.isDataImage(value)) this.showURLTab()
497
497
  else this.showCharsTab()
498
498
  const closeButton = L.DomUtil.createButton(
499
499
  'button action-button',
@@ -567,7 +567,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
567
567
  updatePreview: function () {
568
568
  this.buttons.innerHTML = ''
569
569
  if (this.isDefault()) return
570
- if (!L.Util.hasVar(this.value())) {
570
+ if (!U.Utils.hasVar(this.value())) {
571
571
  // Do not try to render URL with variables
572
572
  const box = L.DomUtil.create('div', 'umap-pictogram-choice', this.buttons)
573
573
  L.DomEvent.on(box, 'click', this.onDefine, this)
@@ -585,11 +585,11 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
585
585
  addIconPreview: function (pictogram, parent) {
586
586
  const baseClass = 'umap-pictogram-choice',
587
587
  value = pictogram.src,
588
- search = L.Util.normalize(this.searchInput.value),
588
+ search = U.Utils.normalize(this.searchInput.value),
589
589
  title = pictogram.attribution
590
590
  ? `${pictogram.name} — © ${pictogram.attribution}`
591
591
  : pictogram.name || pictogram.src
592
- if (search && L.Util.normalize(title).indexOf(search) === -1) return
592
+ if (search && U.Utils.normalize(title).indexOf(search) === -1) return
593
593
  const className = value === this.value() ? `${baseClass} selected` : baseClass,
594
594
  container = L.DomUtil.create('div', className, parent)
595
595
  U.Icon.makeIconElement(value, container)
@@ -637,7 +637,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
637
637
  categories[category].push(props)
638
638
  }
639
639
  const sorted = Object.entries(categories).toSorted(([a], [b]) =>
640
- L.Util.naturalSort(a, b)
640
+ U.Utils.naturalSort(a, b, L.lang)
641
641
  )
642
642
  for (let [name, items] of sorted) {
643
643
  this.addCategory(items, name)
@@ -688,7 +688,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
688
688
  showURLTab: function () {
689
689
  this.openTab('url')
690
690
  const value =
691
- L.Util.isRemoteUrl(this.value()) || L.Util.isDataImage(this.value())
691
+ U.Utils.isRemoteUrl(this.value()) || U.Utils.isDataImage(this.value())
692
692
  ? this.value()
693
693
  : null
694
694
  const input = this.buildInput(this.body, value)
@@ -744,34 +744,122 @@ L.FormBuilder.Switch = L.FormBuilder.CheckBox.extend({
744
744
  },
745
745
  })
746
746
 
747
- L.FormBuilder.FacetSearch = L.FormBuilder.Element.extend({
747
+ L.FormBuilder.FacetSearchChoices = L.FormBuilder.Element.extend({
748
748
  build: function () {
749
- this.container = L.DomUtil.create('div', 'umap-facet', this.parentNode)
749
+ this.container = L.DomUtil.create('fieldset', 'umap-facet', this.parentNode)
750
+ this.container.appendChild(this.label)
750
751
  this.ul = L.DomUtil.create('ul', '', this.container)
751
- const choices = this.options.choices
752
+ this.type = this.options.criteria['type']
753
+
754
+ const choices = this.options.criteria['choices']
752
755
  choices.sort()
753
756
  choices.forEach((value) => this.buildLi(value))
754
757
  },
755
758
 
756
759
  buildLabel: function () {
757
- this.label = L.DomUtil.add('h5', '', this.parentNode, this.options.label)
760
+ this.label = L.DomUtil.element('legend', {textContent: this.options.label})
758
761
  },
759
762
 
760
763
  buildLi: function (value) {
761
- const property_li = L.DomUtil.create('li', '', this.ul),
762
- input = L.DomUtil.create('input', '', property_li),
763
- label = L.DomUtil.create('label', '', property_li)
764
- input.type = 'checkbox'
765
- input.id = `checkbox_${this.name}_${value}`
766
- input.checked = this.get().includes(value)
764
+ const property_li = L.DomUtil.create('li', '', this.ul)
765
+ const label = L.DomUtil.add('label', '', property_li)
766
+ const input = L.DomUtil.create('input', '', label)
767
+ L.DomUtil.add('span', '', label, value)
768
+
769
+ input.type = this.type
770
+ input.name = `${this.type}_${this.name}`
771
+ input.checked = this.get()['choices'].includes(value)
767
772
  input.dataset.value = value
768
- label.htmlFor = `checkbox_${this.name}_${value}`
769
- label.innerHTML = value
773
+
770
774
  L.DomEvent.on(input, 'change', (e) => this.sync())
771
775
  },
772
776
 
773
777
  toJS: function () {
774
- return [...this.ul.querySelectorAll('input:checked')].map((i) => i.dataset.value)
778
+ return {
779
+ type: this.type,
780
+ choices: [...this.ul.querySelectorAll('input:checked')].map(
781
+ (i) => i.dataset.value
782
+ ),
783
+ }
784
+ },
785
+ })
786
+
787
+ L.FormBuilder.MinMaxBase = L.FormBuilder.Element.extend({
788
+ getInputType: function (type) {
789
+ return type
790
+ },
791
+
792
+ getLabels: function () {
793
+ return [L._('Min'), L._('Max')]
794
+ },
795
+
796
+ castValue: function (value) {
797
+ return value.valueOf()
798
+ },
799
+
800
+ build: function () {
801
+ this.container = L.DomUtil.create('fieldset', 'umap-facet', this.parentNode)
802
+ this.container.appendChild(this.label)
803
+ const {min, max, type} = this.options.criteria
804
+ this.type = type
805
+ this.inputType = this.getInputType(this.type)
806
+
807
+ const [minLabel, maxLabel] = this.getLabels()
808
+
809
+ this.minLabel = L.DomUtil.create('label', '', this.container)
810
+ this.minLabel.innerHTML = minLabel
811
+
812
+ this.minInput = L.DomUtil.create('input', '', this.minLabel)
813
+ this.minInput.type = this.inputType
814
+ this.minInput.step = 'any'
815
+ if (min != null) {
816
+ this.minInput.valueAsNumber = this.castValue(min)
817
+ this.minInput.dataset.value = min
818
+ }
819
+
820
+
821
+ this.maxLabel = L.DomUtil.create('label', '', this.container)
822
+ this.maxLabel.innerHTML = maxLabel
823
+
824
+ this.maxInput = L.DomUtil.create('input', '', this.maxLabel)
825
+ this.maxInput.type = this.inputType
826
+ this.maxInput.step = 'any'
827
+ if (max != null) {
828
+ this.maxInput.valueAsNumber = this.castValue(max)
829
+ this.maxInput.dataset.value = max
830
+ }
831
+
832
+ L.DomEvent.on(this.minInput, 'change', (e) => this.sync())
833
+ L.DomEvent.on(this.maxInput, 'change', (e) => this.sync())
834
+ },
835
+
836
+ buildLabel: function () {
837
+ this.label = L.DomUtil.element('legend', {textContent: this.options.label})
838
+ },
839
+
840
+ toJS: function () {
841
+ return {
842
+ type: this.type,
843
+ min: this.minInput.value,
844
+ max: this.maxInput.value,
845
+ }
846
+ },
847
+ })
848
+
849
+ L.FormBuilder.FacetSearchNumber = L.FormBuilder.MinMaxBase.extend({})
850
+
851
+ L.FormBuilder.FacetSearchDate = L.FormBuilder.MinMaxBase.extend({
852
+ castValue: function (value) {
853
+ return value.valueOf() - value.getTimezoneOffset() * 60000
854
+ },
855
+ getLabels: function () {
856
+ return [L._('From'), L._('Until')]
857
+ },
858
+ })
859
+
860
+ L.FormBuilder.FacetSearchDateTime = L.FormBuilder.FacetSearchDate.extend({
861
+ getInputType: function (type) {
862
+ return 'datetime-local'
775
863
  },
776
864
  })
777
865
 
@@ -1009,9 +1097,6 @@ U.FormBuilder = L.FormBuilder.extend({
1009
1097
  case 'iconUrl':
1010
1098
  schema.handler = 'IconUrl'
1011
1099
  break
1012
- case 'datalayersControl':
1013
- schema.handler = 'DataLayersControl'
1014
- break
1015
1100
  case 'licence':
1016
1101
  schema.handler = 'LicenceChooser'
1017
1102
  break
@@ -1032,10 +1117,11 @@ U.FormBuilder = L.FormBuilder.extend({
1032
1117
 
1033
1118
  setter: function (field, value) {
1034
1119
  L.FormBuilder.prototype.setter.call(this, field, value)
1035
- if (this.options.makeDirty !== false) this.obj.isDirty = true
1120
+ this.obj.isDirty = true
1121
+ if ('render' in this.obj) this.obj.render([field], this)
1036
1122
  },
1037
1123
 
1038
1124
  finish: function () {
1039
- this.map.ui.closePanel()
1125
+ this.map.editPanel.close()
1040
1126
  },
1041
1127
  })
@@ -18,7 +18,7 @@ U.Icon = L.DivIcon.extend({
18
18
  },
19
19
 
20
20
  _setRecent: function (url) {
21
- if (L.Util.hasVar(url)) return
21
+ if (U.Utils.hasVar(url)) return
22
22
  if (url === U.SCHEMA.iconUrl.default) return
23
23
  if (U.Icon.RECENT.indexOf(url) === -1) {
24
24
  U.Icon.RECENT.push(url)
@@ -50,7 +50,10 @@ U.Icon = L.DivIcon.extend({
50
50
  },
51
51
 
52
52
  formatUrl: function (url, feature) {
53
- return L.Util.greedyTemplate(url || '', feature ? feature.extendedProperties() : {})
53
+ return U.Utils.greedyTemplate(
54
+ url || '',
55
+ feature ? feature.extendedProperties() : {}
56
+ )
54
57
  },
55
58
 
56
59
  onAdd: function () {},
@@ -206,7 +209,7 @@ U.Icon.Cluster = L.DivIcon.extend({
206
209
  })
207
210
 
208
211
  U.Icon.isImg = function (src) {
209
- return L.Util.isPath(src) || L.Util.isRemoteUrl(src) || L.Util.isDataImage(src)
212
+ return U.Utils.isPath(src) || U.Utils.isRemoteUrl(src) || U.Utils.isDataImage(src)
210
213
  }
211
214
 
212
215
  U.Icon.makeIconElement = function (src, parent) {
@@ -236,7 +239,11 @@ U.Icon.setIconContrast = function (icon, parent, src, bgcolor) {
236
239
  if (L.DomUtil.contrastedColor(parent, bgcolor)) {
237
240
  // Decide whether to switch svg to white or not, but do it
238
241
  // only for internal SVG, as invert could do weird things
239
- if (L.Util.isPath(src) && src.endsWith('.svg') && src !== U.SCHEMA.iconUrl.default) {
242
+ if (
243
+ U.Utils.isPath(src) &&
244
+ src.endsWith('.svg') &&
245
+ src !== U.SCHEMA.iconUrl.default
246
+ ) {
240
247
  // Must be called after icon container is added to the DOM
241
248
  // An image
242
249
  icon.style.filter = 'invert(1)'
@@ -7,7 +7,11 @@ U.Importer = L.Class.extend({
7
7
 
8
8
  build: function () {
9
9
  this.container = L.DomUtil.create('div', 'umap-upload')
10
- this.title = L.DomUtil.add('h3', '', this.container, L._('Import data'))
10
+ this.title = L.DomUtil.createTitle(
11
+ this.container,
12
+ L._('Import data'),
13
+ 'icon-upload'
14
+ )
11
15
  this.presetBox = L.DomUtil.create('div', 'formbox', this.container)
12
16
  this.presetSelect = L.DomUtil.create('select', '', this.presetBox)
13
17
  this.fileBox = L.DomUtil.create('div', 'formbox', this.container)
@@ -16,7 +20,6 @@ U.Importer = L.Class.extend({
16
20
  { type: 'file', multiple: 'multiple', autofocus: true },
17
21
  this.fileBox
18
22
  )
19
- this.map.ui.once('panel:closed', () => (this.fileInput.value = null))
20
23
  this.urlInput = L.DomUtil.element(
21
24
  'input',
22
25
  { type: 'text', placeholder: L._('Provide an URL here') },
@@ -61,19 +64,6 @@ U.Importer = L.Class.extend({
61
64
  { type: 'checkbox', name: 'clear', id: 'datalayer-clear-check' },
62
65
  this.clearLabel
63
66
  )
64
- let option
65
- this.map.eachDataLayerReverse((datalayer) => {
66
- if (datalayer.isLoaded() && !datalayer.isRemoteLayer()) {
67
- const id = L.stamp(datalayer)
68
- option = L.DomUtil.add('option', '', this.layerInput, datalayer.options.name)
69
- option.value = id
70
- }
71
- })
72
- L.DomUtil.element(
73
- 'option',
74
- { value: '', textContent: L._('Import in a new layer') },
75
- this.layerInput
76
- )
77
67
  L.DomUtil.element(
78
68
  'option',
79
69
  { value: '', textContent: L._('Choose the data format') },
@@ -102,7 +92,7 @@ U.Importer = L.Class.extend({
102
92
  let type = '',
103
93
  newType
104
94
  for (let i = 0; i < e.target.files.length; i++) {
105
- newType = L.Util.detectFileType(e.target.files[i])
95
+ newType = U.Utils.detectFileType(e.target.files[i])
106
96
  if (!type && newType) type = newType
107
97
  if (type && newType !== type) {
108
98
  type = ''
@@ -117,7 +107,24 @@ U.Importer = L.Class.extend({
117
107
 
118
108
  open: function () {
119
109
  if (!this.container) this.build()
120
- this.map.ui.openPanel({ data: { html: this.container }, className: 'dark' })
110
+ const onLoad = this.map.editPanel.open({ content: this.container })
111
+ onLoad.then(() => {
112
+ this.fileInput.value = null
113
+ this.layerInput.innerHTML = ''
114
+ let option
115
+ this.map.eachDataLayerReverse((datalayer) => {
116
+ if (datalayer.isLoaded() && !datalayer.isRemoteLayer()) {
117
+ const id = L.stamp(datalayer)
118
+ option = L.DomUtil.add('option', '', this.layerInput, datalayer.options.name)
119
+ option.value = id
120
+ }
121
+ })
122
+ L.DomUtil.element(
123
+ 'option',
124
+ { value: '', textContent: L._('Import in a new layer') },
125
+ this.layerInput
126
+ )
127
+ })
121
128
  },
122
129
 
123
130
  openFiles: function () {