umap-project 3.3.2__py3-none-any.whl → 3.4.0__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.
Files changed (242) hide show
  1. umap/__init__.py +1 -1
  2. umap/context_processors.py +4 -1
  3. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/cs_CZ/LC_MESSAGES/django.po +43 -33
  5. umap/locale/da/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/da/LC_MESSAGES/django.po +43 -33
  7. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/de/LC_MESSAGES/django.po +64 -53
  9. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  10. umap/locale/el/LC_MESSAGES/django.po +35 -29
  11. umap/locale/en/LC_MESSAGES/django.po +47 -41
  12. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  13. umap/locale/es/LC_MESSAGES/django.po +43 -33
  14. umap/locale/et/LC_MESSAGES/django.mo +0 -0
  15. umap/locale/et/LC_MESSAGES/django.po +58 -54
  16. umap/locale/eu/LC_MESSAGES/django.mo +0 -0
  17. umap/locale/eu/LC_MESSAGES/django.po +43 -33
  18. umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
  19. umap/locale/fa_IR/LC_MESSAGES/django.po +43 -33
  20. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  21. umap/locale/fr/LC_MESSAGES/django.po +36 -30
  22. umap/locale/gl/LC_MESSAGES/django.mo +0 -0
  23. umap/locale/gl/LC_MESSAGES/django.po +43 -33
  24. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  25. umap/locale/hu/LC_MESSAGES/django.po +35 -29
  26. umap/locale/is/LC_MESSAGES/django.mo +0 -0
  27. umap/locale/is/LC_MESSAGES/django.po +43 -33
  28. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  29. umap/locale/it/LC_MESSAGES/django.po +43 -33
  30. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  31. umap/locale/nl/LC_MESSAGES/django.po +35 -29
  32. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  33. umap/locale/pl/LC_MESSAGES/django.po +114 -103
  34. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  35. umap/locale/pt/LC_MESSAGES/django.po +43 -33
  36. umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
  37. umap/locale/th_TH/LC_MESSAGES/django.po +310 -109
  38. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  39. umap/locale/zh_TW/LC_MESSAGES/django.po +80 -70
  40. umap/management/commands/switch_user.py +2 -2
  41. umap/migrations/0018_datalayer_uuid.py +1 -1
  42. umap/models.py +7 -3
  43. umap/settings/local.py.sample +1 -1
  44. umap/static/umap/base.css +89 -32
  45. umap/static/umap/content.css +129 -33
  46. umap/static/umap/css/bar.css +82 -20
  47. umap/static/umap/css/browser.css +163 -0
  48. umap/static/umap/css/contextmenu.css +15 -0
  49. umap/static/umap/css/dialog.css +36 -16
  50. umap/static/umap/css/form.css +123 -33
  51. umap/static/umap/css/icon.css +46 -3
  52. umap/static/umap/css/panel.css +7 -3
  53. umap/static/umap/css/popup.css +34 -8
  54. umap/static/umap/css/tooltip.css +8 -4
  55. umap/static/umap/img/16-white.svg +26 -8
  56. umap/static/umap/img/16.svg +1 -1
  57. umap/static/umap/img/source/16-white.svg +36 -18
  58. umap/static/umap/img/source/16.svg +1 -1
  59. umap/static/umap/js/components/alerts/alert.css +69 -31
  60. umap/static/umap/js/components/alerts/alert.js +20 -2
  61. umap/static/umap/js/components/base.js +1 -1
  62. umap/static/umap/js/modules/browser.js +69 -61
  63. umap/static/umap/js/modules/caption.js +10 -7
  64. umap/static/umap/js/modules/data/features.js +89 -63
  65. umap/static/umap/js/modules/data/fields.js +446 -0
  66. umap/static/umap/js/modules/data/layer.js +116 -196
  67. umap/static/umap/js/modules/domutils.js +109 -0
  68. umap/static/umap/js/modules/filters.js +780 -0
  69. umap/static/umap/js/modules/form/builder.js +8 -5
  70. umap/static/umap/js/modules/form/fields.js +111 -221
  71. umap/static/umap/js/modules/formatter.js +24 -1
  72. umap/static/umap/js/modules/help.js +4 -3
  73. umap/static/umap/js/modules/i18n.js +1 -1
  74. umap/static/umap/js/modules/importer.js +1 -1
  75. umap/static/umap/js/modules/importers/opendata.js +15 -0
  76. umap/static/umap/js/modules/importers/openrouteservice.js +6 -1
  77. umap/static/umap/js/modules/managers.js +2 -1
  78. umap/static/umap/js/modules/permissions.js +39 -31
  79. umap/static/umap/js/modules/rendering/controls.js +11 -9
  80. umap/static/umap/js/modules/rendering/icon.js +3 -8
  81. umap/static/umap/js/modules/rendering/layers/base.js +3 -3
  82. umap/static/umap/js/modules/rendering/layers/classified.js +18 -11
  83. umap/static/umap/js/modules/rendering/layers/cluster.js +23 -11
  84. umap/static/umap/js/modules/rendering/layers/heat.js +27 -21
  85. umap/static/umap/js/modules/rendering/map.js +1 -0
  86. umap/static/umap/js/modules/rendering/template.js +50 -23
  87. umap/static/umap/js/modules/rendering/ui.js +33 -25
  88. umap/static/umap/js/modules/rules.js +38 -44
  89. umap/static/umap/js/modules/schema.js +3 -6
  90. umap/static/umap/js/modules/share.js +5 -4
  91. umap/static/umap/js/modules/tableeditor.js +50 -38
  92. umap/static/umap/js/modules/templates.js +2 -3
  93. umap/static/umap/js/modules/ui/bar.js +55 -23
  94. umap/static/umap/js/modules/ui/dialog.js +38 -27
  95. umap/static/umap/js/modules/ui/panel.js +23 -8
  96. umap/static/umap/js/modules/ui/tooltip.js +6 -5
  97. umap/static/umap/js/modules/umap.js +158 -51
  98. umap/static/umap/js/modules/utils.js +24 -2
  99. umap/static/umap/js/umap.core.js +1 -110
  100. umap/static/umap/locale/am_ET.js +52 -17
  101. umap/static/umap/locale/am_ET.json +52 -17
  102. umap/static/umap/locale/ar.js +52 -17
  103. umap/static/umap/locale/ar.json +52 -17
  104. umap/static/umap/locale/ast.js +52 -17
  105. umap/static/umap/locale/ast.json +52 -17
  106. umap/static/umap/locale/bg.js +52 -17
  107. umap/static/umap/locale/bg.json +52 -17
  108. umap/static/umap/locale/br.js +48 -22
  109. umap/static/umap/locale/br.json +48 -22
  110. umap/static/umap/locale/ca.js +52 -17
  111. umap/static/umap/locale/ca.json +52 -17
  112. umap/static/umap/locale/cs_CZ.js +52 -17
  113. umap/static/umap/locale/cs_CZ.json +52 -17
  114. umap/static/umap/locale/da.js +54 -17
  115. umap/static/umap/locale/da.json +54 -17
  116. umap/static/umap/locale/de.js +102 -67
  117. umap/static/umap/locale/de.json +102 -67
  118. umap/static/umap/locale/el.js +52 -17
  119. umap/static/umap/locale/el.json +52 -17
  120. umap/static/umap/locale/en.js +54 -16
  121. umap/static/umap/locale/en.json +54 -16
  122. umap/static/umap/locale/en_US.json +52 -17
  123. umap/static/umap/locale/es.js +54 -17
  124. umap/static/umap/locale/es.json +54 -17
  125. umap/static/umap/locale/et.js +91 -56
  126. umap/static/umap/locale/et.json +91 -56
  127. umap/static/umap/locale/eu.js +84 -49
  128. umap/static/umap/locale/eu.json +84 -49
  129. umap/static/umap/locale/fa_IR.js +52 -17
  130. umap/static/umap/locale/fa_IR.json +52 -17
  131. umap/static/umap/locale/fi.js +52 -17
  132. umap/static/umap/locale/fi.json +52 -17
  133. umap/static/umap/locale/fr.js +54 -17
  134. umap/static/umap/locale/fr.json +54 -17
  135. umap/static/umap/locale/gl.js +52 -17
  136. umap/static/umap/locale/gl.json +52 -17
  137. umap/static/umap/locale/he.js +52 -17
  138. umap/static/umap/locale/he.json +52 -17
  139. umap/static/umap/locale/hr.js +52 -17
  140. umap/static/umap/locale/hr.json +52 -17
  141. umap/static/umap/locale/hu.js +59 -24
  142. umap/static/umap/locale/hu.json +59 -24
  143. umap/static/umap/locale/id.js +52 -17
  144. umap/static/umap/locale/id.json +52 -17
  145. umap/static/umap/locale/is.js +52 -17
  146. umap/static/umap/locale/is.json +52 -17
  147. umap/static/umap/locale/it.js +52 -17
  148. umap/static/umap/locale/it.json +52 -17
  149. umap/static/umap/locale/ja.js +52 -17
  150. umap/static/umap/locale/ja.json +52 -17
  151. umap/static/umap/locale/ko.js +52 -17
  152. umap/static/umap/locale/ko.json +52 -17
  153. umap/static/umap/locale/lt.js +52 -17
  154. umap/static/umap/locale/lt.json +52 -17
  155. umap/static/umap/locale/ms.js +52 -17
  156. umap/static/umap/locale/ms.json +52 -17
  157. umap/static/umap/locale/nl.js +52 -17
  158. umap/static/umap/locale/nl.json +52 -17
  159. umap/static/umap/locale/no.js +52 -17
  160. umap/static/umap/locale/no.json +52 -17
  161. umap/static/umap/locale/pl.js +53 -17
  162. umap/static/umap/locale/pl.json +53 -17
  163. umap/static/umap/locale/pl_PL.json +52 -17
  164. umap/static/umap/locale/pt.js +52 -17
  165. umap/static/umap/locale/pt.json +52 -17
  166. umap/static/umap/locale/pt_BR.js +52 -17
  167. umap/static/umap/locale/pt_BR.json +52 -17
  168. umap/static/umap/locale/pt_PT.js +52 -17
  169. umap/static/umap/locale/pt_PT.json +52 -17
  170. umap/static/umap/locale/ro.js +52 -17
  171. umap/static/umap/locale/ro.json +52 -17
  172. umap/static/umap/locale/ru.js +52 -17
  173. umap/static/umap/locale/ru.json +52 -17
  174. umap/static/umap/locale/si.js +1 -1
  175. umap/static/umap/locale/si.json +1 -1
  176. umap/static/umap/locale/sk_SK.js +52 -17
  177. umap/static/umap/locale/sk_SK.json +52 -17
  178. umap/static/umap/locale/sl.js +52 -17
  179. umap/static/umap/locale/sl.json +52 -17
  180. umap/static/umap/locale/sr.js +52 -17
  181. umap/static/umap/locale/sr.json +52 -17
  182. umap/static/umap/locale/sv.js +52 -17
  183. umap/static/umap/locale/sv.json +52 -17
  184. umap/static/umap/locale/th_TH.js +52 -17
  185. umap/static/umap/locale/th_TH.json +52 -17
  186. umap/static/umap/locale/tr.js +52 -17
  187. umap/static/umap/locale/tr.json +52 -17
  188. umap/static/umap/locale/uk_UA.js +52 -17
  189. umap/static/umap/locale/uk_UA.json +52 -17
  190. umap/static/umap/locale/vi.js +52 -17
  191. umap/static/umap/locale/vi.json +52 -17
  192. umap/static/umap/locale/vi_VN.json +52 -17
  193. umap/static/umap/locale/zh.js +52 -17
  194. umap/static/umap/locale/zh.json +52 -17
  195. umap/static/umap/locale/zh_CN.json +52 -17
  196. umap/static/umap/locale/zh_TW.Big5.json +52 -17
  197. umap/static/umap/locale/zh_TW.js +53 -17
  198. umap/static/umap/locale/zh_TW.json +53 -17
  199. umap/static/umap/map.css +63 -226
  200. umap/static/umap/unittests/utils.js +18 -0
  201. umap/static/umap/vars.css +23 -5
  202. umap/templates/umap/components/alerts/alert.html +32 -29
  203. umap/templates/umap/css.html +2 -1
  204. umap/templates/umap/login_popup_end.html +18 -9
  205. umap/templates/umap/user_map_table.html +7 -2
  206. umap/tests/integration/conftest.py +10 -6
  207. umap/tests/integration/test_anonymous_owned_map.py +90 -37
  208. umap/tests/integration/test_basics.py +25 -1
  209. umap/tests/integration/test_browser.py +37 -0
  210. umap/tests/integration/test_cluster.py +110 -0
  211. umap/tests/integration/test_conditional_rules.py +107 -52
  212. umap/tests/integration/test_datalayer.py +9 -16
  213. umap/tests/integration/test_draw_polygon.py +6 -0
  214. umap/tests/integration/test_draw_polyline.py +11 -0
  215. umap/tests/integration/test_edit_marker.py +12 -1
  216. umap/tests/integration/test_export_map.py +19 -0
  217. umap/tests/integration/test_fields.py +541 -0
  218. umap/tests/integration/test_filters.py +616 -0
  219. umap/tests/integration/test_iframe.py +1 -1
  220. umap/tests/integration/test_import.py +38 -42
  221. umap/tests/integration/test_map_preview.py +1 -1
  222. umap/tests/integration/test_picto.py +1 -1
  223. umap/tests/integration/test_popup.py +31 -0
  224. umap/tests/integration/test_remote_data.py +60 -4
  225. umap/tests/integration/test_save.py +1 -1
  226. umap/tests/integration/test_share.py +4 -4
  227. umap/tests/integration/test_tableeditor.py +31 -7
  228. umap/tests/integration/test_websocket_sync.py +71 -20
  229. umap/tests/test_dashboard.py +11 -1
  230. umap/tests/test_statics.py +2 -2
  231. umap/tests/test_utils.py +19 -2
  232. umap/tests/test_views.py +1 -1
  233. umap/urls.py +1 -0
  234. umap/utils.py +8 -1
  235. umap/views.py +5 -0
  236. {umap_project-3.3.2.dist-info → umap_project-3.4.0.dist-info}/METADATA +15 -15
  237. {umap_project-3.3.2.dist-info → umap_project-3.4.0.dist-info}/RECORD +240 -236
  238. umap/static/umap/js/modules/facets.js +0 -164
  239. umap/tests/integration/test_facets_browser.py +0 -279
  240. {umap_project-3.3.2.dist-info → umap_project-3.4.0.dist-info}/WHEEL +0 -0
  241. {umap_project-3.3.2.dist-info → umap_project-3.4.0.dist-info}/entry_points.txt +0 -0
  242. {umap_project-3.3.2.dist-info → umap_project-3.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -2,7 +2,6 @@ import { DomEvent, DomUtil, stamp } from '../../vendors/leaflet/leaflet-src.esm.
2
2
  import { Form } from './form/builder.js'
3
3
  import { EXPORT_FORMATS } from './formatter.js'
4
4
  import { translate } from './i18n.js'
5
- import * as Icon from './rendering/icon.js'
6
5
  import ContextMenu from './ui/contextmenu.js'
7
6
  import * as Utils from './utils.js'
8
7
  import { SCHEMA } from './schema.js'
@@ -23,28 +22,22 @@ export default class Browser {
23
22
  if (feature.isFiltered()) return
24
23
  if (this.options.inBbox && !feature.isOnScreen(this.bounds)) return
25
24
  const template = `
26
- <li class="feature ${feature.getClassName()}">
27
- <button class="icon icon-16 icon-zoom" title="${translate('Bring feature to center')}" data-ref=zoom></button>
28
- <button class="icon icon-16 show-on-edit icon-edit" title="${translate('Edit this feature')}" data-ref=edit></button>
29
- <button class="icon icon-16 show-on-edit icon-delete" title="${translate('Delete this feature')}" data-ref=remove></button>
30
- <i class="icon icon-16 icon-${feature.getClassName()} feature-color" data-ref=colorBox></i>
31
- <span class="feature-title" data-ref=label></span>
25
+ <li class="feature ${feature.getClassName()} ${feature.getUniqueClassName()} with-toolbox">
26
+ <span>
27
+ <i class="icon icon-16 icon-${feature.getClassName()} feature-color" data-ref=colorBox></i>
28
+ <span class="feature-title truncate" data-ref=label></span>
29
+ </span>
30
+ <span>
31
+ <button class="icon icon-16 icon-zoom" title="${translate('Bring feature to center')}" data-ref=zoom></button
32
+ ><button class="icon icon-16 show-on-edit icon-edit" title="${translate('Edit this feature')}" data-ref=edit></button
33
+ ><button class="icon icon-16 show-on-edit icon-delete" title="${translate('Delete this feature')}" data-ref=remove></button>
34
+ </span>
32
35
  </li>
33
36
  `
34
37
  const [row, { zoom, edit, remove, colorBox, label }] =
35
38
  Utils.loadTemplateWithRefs(template)
36
39
  label.textContent = label.title = feature.getDisplayName() || '—'
37
- const symbol = feature._getIconUrl
38
- ? Icon.formatUrl(feature._getIconUrl(), feature)
39
- : null
40
- const bgcolor = feature.getPreviewColor()
41
- colorBox.style.backgroundColor = bgcolor
42
- if (symbol && symbol !== SCHEMA.iconUrl.default) {
43
- const icon = Icon.makeElement(symbol, colorBox)
44
- Icon.setContrast(icon, colorBox, symbol, bgcolor)
45
- } else if (DomUtil.contrastedColor(colorBox, bgcolor)) {
46
- colorBox.classList.add('icon-white')
47
- }
40
+ feature.makePreview(colorBox)
48
41
  const viewFeature = (e) => {
49
42
  feature.zoomTo({ ...e, callback: () => feature.view() })
50
43
  }
@@ -65,21 +58,23 @@ export default class Browser {
65
58
  addDataLayer(datalayer, parent) {
66
59
  const open = this.mode !== 'layers' ? ' open' : ''
67
60
  const [container, { headline, toolbox, label }] = Utils.loadTemplateWithRefs(`
68
- <details class="datalayer ${datalayer.getHidableClass()}" id="${this.datalayerId(datalayer)}"${open}>
69
- <summary data-ref=headline>
61
+ <details class="datalayer ${datalayer.cssId}" id="${this.datalayerId(datalayer)}"${open}>
62
+ <summary data-ref=headline class="with-toolbox">
63
+ <span>
64
+ <span class="datalayer-name truncate" data-id="${datalayer.id}" data-ref=label></span>
65
+ <span class="datalayer-counter"></span>
66
+ </span>
70
67
  <span data-ref=toolbox></span>
71
- <span class="datalayer-name" data-id="${datalayer.id}" data-ref=label></span>
72
- <span class="datalayer-counter"></span>
73
68
  </summary>
74
69
  <ul></ul>
75
70
  </details>
76
71
  `)
77
72
  datalayer.renderToolbox(toolbox)
78
73
  parent.appendChild(container)
79
- this.updateDatalayer(datalayer)
74
+ this.updateFeaturesList(datalayer)
80
75
  }
81
76
 
82
- updateDatalayer(datalayer) {
77
+ updateFeaturesList(datalayer) {
83
78
  // Compute once, but use it for each feature later.
84
79
  this.bounds = this._leafletMap.getBounds()
85
80
  const id = this.datalayerId(datalayer)
@@ -102,14 +97,14 @@ export default class Browser {
102
97
  }
103
98
 
104
99
  toggleBadge() {
105
- Utils.toggleBadge(this.filtersTitle, this.hasFilters())
106
- Utils.toggleBadge('.umap-control-browse', this.hasFilters())
100
+ Utils.toggleBadge(this.filtersTitle, this.hasActiveFilters())
101
+ Utils.toggleBadge('.umap-control-browse', this.hasActiveFilters())
107
102
  }
108
103
 
109
104
  onFormChange() {
110
105
  this._umap.datalayers.browsable().map((datalayer) => {
111
106
  datalayer.resetLayer(true)
112
- this.updateDatalayer(datalayer)
107
+ this.updateFeaturesList(datalayer)
113
108
  if (this._umap.fullPanel?.isOpen()) datalayer.tableEdit()
114
109
  })
115
110
  this.toggleBadge()
@@ -123,25 +118,24 @@ export default class Browser {
123
118
  return !!document.querySelector('.on .umap-browser')
124
119
  }
125
120
 
126
- hasFilters() {
127
- return !!this.options.filter || this._umap.facets.isActive()
121
+ hasActiveFilters() {
122
+ return !!this.options.filter || this._umap.hasActiveFilters()
128
123
  }
129
124
 
130
125
  onMoveEnd() {
131
126
  if (!this.isOpen()) return
132
- const isListDynamic = this.options.inBbox
133
127
  this._umap.datalayers.browsable().map((datalayer) => {
134
- if (!isListDynamic && !datalayer.hasDynamicData()) return
135
- this.updateDatalayer(datalayer)
128
+ if (!this.options.inBbox && !datalayer.hasDynamicData()) return
129
+ this.updateFeaturesList(datalayer)
136
130
  })
137
131
  }
138
132
 
139
133
  update() {
140
134
  if (!this.isOpen()) return
141
135
  this.dataContainer.innerHTML = ''
142
- this._umap.datalayers.browsable().map((datalayer) => {
136
+ for (const datalayer of this._umap.datalayers.browsable()) {
143
137
  this.addDataLayer(datalayer, this.dataContainer)
144
- })
138
+ }
145
139
  }
146
140
 
147
141
  open(mode) {
@@ -151,11 +145,14 @@ export default class Browser {
151
145
  <div>
152
146
  <h3><i class="icon icon-16 icon-layers"></i>${translate('Data browser')}</h3>
153
147
  <details class="filters" data-ref="details">
154
- <summary data-ref=filtersTitle><i class="icon icon-16 icon-filters"></i>${translate('Filters')}</summary>
148
+ <summary data-ref=filtersTitle>
149
+ <i class="icon icon-16 icon-filters"></i>${translate('Filters')}
150
+ </summary>
151
+ <button type="button" class="show-on-edit flat" data-ref=manageFilters>${translate('Manage filters')}</button>
155
152
  <fieldset>
156
- <div data-ref=formContainer>
153
+ <div data-ref="formContainer" class="formbox">
157
154
  </div>
158
- <button class="flat" type="button" data-ref=reset><i class="icon icon-16 icon-restore" title=""></i>${translate('Reset all')}</button>
155
+ <button class="flat" type="button" data-ref=reset><i class="icon icon-16 icon-restore" title=""></i> ${translate('Reset all')}</button>
159
156
  </fieldset>
160
157
  </details>
161
158
  <div class="main-toolbox">
@@ -177,6 +174,7 @@ export default class Browser {
177
174
  dataContainer,
178
175
  formContainer,
179
176
  reset,
177
+ manageFilters,
180
178
  },
181
179
  ] = Utils.loadTemplateWithRefs(template)
182
180
  // HOTFIX. Remove when this is released:
@@ -187,43 +185,53 @@ export default class Browser {
187
185
  fitBounds.addEventListener('click', () => this._umap.fitDataBounds())
188
186
  download.addEventListener('click', () => this.downloadVisible(download))
189
187
  download.hidden = this._umap.getProperty('embedControl') === false
188
+ reset.addEventListener('click', () => this.resetFilters())
189
+ manageFilters.addEventListener('click', () => {
190
+ this._umap.edit().then((panel) => panel.scrollTo('details#fields-management'))
191
+ this._umap.filters.edit()
192
+ })
190
193
 
191
194
  this.filtersTitle = filtersTitle
192
195
  this.dataContainer = dataContainer
193
196
  this.formContainer = formContainer
194
197
  this.toggleBadge()
198
+ this.buildFilters()
199
+ this._umap.panel.open({
200
+ content: container,
201
+ className: 'umap-browser',
202
+ })
203
+
204
+ this.update()
205
+ }
195
206
 
196
- let fields = [
207
+ buildFilters() {
208
+ this.formContainer.innerHTML = ''
209
+ const fields = [
197
210
  [
198
211
  'options.filter',
199
212
  { handler: 'Input', placeholder: translate('Search map features…') },
200
213
  ],
201
214
  ['options.inBbox', { handler: 'Switch', label: translate('Current map view') }],
202
215
  ]
203
- const builder = new Form(this, fields)
204
- builder.on('set', () => this.onFormChange())
205
- let filtersBuilder
206
- this.formContainer.appendChild(builder.build())
207
- builder.form.addEventListener('reset', () => {
208
- window.setTimeout(builder.syncAll.bind(builder))
209
- })
210
- if (this._umap.properties.facetKey) {
211
- fields = this._umap.facets.build()
212
- filtersBuilder = new Form(this._umap.facets, fields)
213
- filtersBuilder.on('set', () => this.onFormChange())
214
- filtersBuilder.form.addEventListener('reset', () => {
215
- window.setTimeout(filtersBuilder.syncAll.bind(filtersBuilder))
216
+ const searchForm = new Form(this, fields, { className: 'formbox' })
217
+ const listenFormChanges = (form) => {
218
+ form.on('set', () => this.onFormChange())
219
+ form.form.addEventListener('reset', () => {
220
+ window.setTimeout(form.syncAll.bind(form))
216
221
  })
217
- this.formContainer.appendChild(filtersBuilder.build())
218
222
  }
219
- reset.addEventListener('click', () => this.resetFilters())
220
-
221
- this._umap.panel.open({
222
- content: container,
223
- className: 'umap-browser',
224
- })
225
-
226
- this.update()
223
+ this.formContainer.appendChild(searchForm.build())
224
+ listenFormChanges(searchForm)
225
+ if (this._umap.filters.size) {
226
+ const filtersForm = this._umap.filters.buildForm(this.formContainer)
227
+ listenFormChanges(filtersForm)
228
+ }
229
+ for (const datalayer of this._umap.datalayers.active()) {
230
+ if (datalayer.filters.size) {
231
+ const filtersForm = datalayer.filters.buildForm(this.formContainer)
232
+ listenFormChanges(filtersForm)
233
+ }
234
+ }
227
235
  }
228
236
 
229
237
  resetFilters() {
@@ -252,7 +260,7 @@ export default class Browser {
252
260
  if (datalayer.isVisible()) allHidden = false
253
261
  })
254
262
  this._umap.datalayers.browsable().map((datalayer) => {
255
- datalayer._forcedVisibility = true
263
+ datalayer.autoVisibility = false
256
264
  if (allHidden) {
257
265
  datalayer.show()
258
266
  } else {
@@ -5,7 +5,7 @@ import * as Utils from './utils.js'
5
5
  const TEMPLATE = `
6
6
  <div class="umap-caption">
7
7
  <div class="header">
8
- <i class="icon icon-16 icon-caption icon-block"></i>
8
+ <i class="icon icon-16 icon-info icon-block"></i>
9
9
  <hgroup>
10
10
  <h3><span class="map-name" data-ref="name"></span></h3>
11
11
  <p data-ref="author"></p>
@@ -39,7 +39,7 @@ export default class Caption extends Utils.WithTemplate {
39
39
  this._leafletMap = leafletMap
40
40
  this.loadTemplate(TEMPLATE)
41
41
  this.elements.star.addEventListener('click', async () => {
42
- if (this._umap.properties.user?.id) {
42
+ if (this._umap.permissions.userIsAuth()) {
43
43
  await this._umap.star()
44
44
  this.refresh()
45
45
  } else {
@@ -96,11 +96,14 @@ export default class Caption extends Utils.WithTemplate {
96
96
  addDataLayer(datalayer, parent) {
97
97
  if (!datalayer.properties.inCaption) return
98
98
  const template = `
99
- <p class="caption-item ${datalayer.cssId}">
100
- <span class="datalayer-legend"></span>
101
- <strong data-ref="toolbox"></strong>
102
- <span class="text" data-ref="description"></span>
103
- </p>`
99
+ <div class="caption-item ${datalayer.cssId}">
100
+ <p>
101
+ <span class="datalayer-legend"></span>
102
+ <strong data-ref="toolbox"></strong>
103
+ </p>
104
+ <p class="text" data-ref="description"></p>
105
+ </div>
106
+ `
104
107
  const [element, { toolbox, description }] = Utils.loadTemplateWithRefs(template)
105
108
  if (datalayer.properties.description) {
106
109
  description.innerHTML = Utils.toHTML(datalayer.properties.description)
@@ -22,6 +22,8 @@ import {
22
22
  } from '../rendering/ui.js'
23
23
  import { SCHEMA } from '../schema.js'
24
24
  import * as Utils from '../utils.js'
25
+ import * as DOMUtils from '../domutils.js'
26
+ import * as Icon from '../rendering/icon.js'
25
27
 
26
28
  class Feature {
27
29
  constructor(umap, datalayer, geojson = {}, id = null) {
@@ -91,7 +93,7 @@ class Feature {
91
93
 
92
94
  get fields() {
93
95
  // Fields are user defined properties
94
- return [...this.datalayer.fields, ...this._umap.fields]
96
+ return [...this._umap.fields.all(), ...this.datalayer.fields.all()]
95
97
  }
96
98
 
97
99
  setter(key, value) {
@@ -136,6 +138,10 @@ class Feature {
136
138
  return this.staticOptions.className
137
139
  }
138
140
 
141
+ getUniqueClassName() {
142
+ return `feature-${this.datalayer.id}-${this.id}`
143
+ }
144
+
139
145
  getPreviewColor() {
140
146
  return this.getDynamicOption(this.staticOptions.mainColor)
141
147
  }
@@ -213,10 +219,22 @@ class Feature {
213
219
  return field.startsWith('properties.')
214
220
  })
215
221
  if (impactData) {
222
+ Utils.eachElement(`.${this.getUniqueClassName()} .feature-title`, (el) => {
223
+ el.textContent = this.getDisplayName()
224
+ el.title = this.getDisplayName()
225
+ })
216
226
  if (this._umap.currentFeature === this) {
217
227
  this.view()
218
228
  }
219
229
  }
230
+ const impactPreview = fields.some((field) => {
231
+ return field.startsWith('properties._umap_options')
232
+ })
233
+ if (impactPreview) {
234
+ Utils.eachElement(`.${this.getUniqueClassName()} .feature-color`, (el) => {
235
+ this.makePreview(el)
236
+ })
237
+ }
220
238
  this.redraw()
221
239
  }
222
240
 
@@ -233,7 +251,7 @@ class Feature {
233
251
  )
234
252
 
235
253
  let builder = new MutatingForm(this, [
236
- ['datalayer', { handler: 'DataLayerSwitcher' }],
254
+ ['datalayer', { handler: 'EditableDataLayerSwitcher' }],
237
255
  ])
238
256
  // removeLayer step will close the edit panel, let's reopen it
239
257
  builder.on('set', () => this.edit(event))
@@ -241,11 +259,22 @@ class Feature {
241
259
 
242
260
  const properties = []
243
261
  for (const field of this.fields) {
244
- let handler = 'Input'
245
- if (field.key === 'description' || field.type === 'Text') {
246
- handler = 'Textarea'
262
+ const options = { handler: 'Input', label: field.key }
263
+ if (field.key === 'description' || field.TYPE === 'Text') {
264
+ options.handler = 'Textarea'
265
+ options.helpEntries = ['textFormatting']
266
+ } else if (field.TYPE === 'Number') {
267
+ options.handler = 'FloatInput'
268
+ } else if (field.TYPE === 'Date') {
269
+ options.handler = 'DateInput'
270
+ } else if (field.TYPE === 'Datetime') {
271
+ options.handler = 'DateTimeInput'
272
+ } else if (field.TYPE === 'Boolean') {
273
+ options.handler = 'Switch'
274
+ } else if (field.TYPE === 'Enum') {
275
+ options.helpText = translate('Comma separated list of values')
247
276
  }
248
- properties.push([`properties.${field.key}`, { label: field.key, handler }])
277
+ properties.push([`properties.${field.key}`, options])
249
278
  }
250
279
  builder = new MutatingForm(this, properties, {
251
280
  id: 'umap-feature-properties',
@@ -256,13 +285,13 @@ class Feature {
256
285
  `<button type="button"><i class="icon icon-16 icon-add"></i>${translate('Add a new field')}</button>`
257
286
  )
258
287
  button.addEventListener('click', () => {
259
- this.datalayer.addProperty().then(() => this.edit({ force: true }))
288
+ this.datalayer.fields.editField().then(() => this.edit({ force: true }))
260
289
  })
261
290
  form.appendChild(button)
262
291
  this.appendEditFieldsets(container)
263
292
  const [details, { fieldset }] = Utils.loadTemplateWithRefs(`
264
293
  <details>
265
- <summary>${translate('Advanced actions')}</summary>
294
+ <summary><h4>${translate('Advanced actions')}</h4></summary>
266
295
  <fieldset class="button-bar by2" data-ref=fieldset></fieldset>
267
296
  </details>
268
297
  `)
@@ -297,7 +326,7 @@ class Feature {
297
326
  container.appendChild(button)
298
327
  }
299
328
 
300
- addExtraEditFieldset() {}
329
+ addExtraEditFieldset(container) {}
301
330
 
302
331
  appendEditFieldsets(container) {
303
332
  const optionsFields = this.getShapeOptions()
@@ -497,16 +526,16 @@ class Feature {
497
526
  return properties
498
527
  }
499
528
 
500
- deleteProperty(property) {
501
- const oldValue = this.properties[property]
502
- delete this.properties[property]
503
- this.sync.update(`properties.${property}`, undefined, oldValue)
529
+ deleteField(name) {
530
+ const oldValue = this.properties[name]
531
+ delete this.properties[name]
532
+ this.sync.update(`properties.${name}`, undefined, oldValue)
504
533
  }
505
534
 
506
- renameProperty(from, to) {
535
+ renameField(from, to) {
507
536
  const oldValue = this.properties[from]
508
537
  this.properties[to] = this.properties[from]
509
- this.deleteProperty(from)
538
+ this.deleteField(from)
510
539
  this.sync.update(`properties.${to}`, oldValue, undefined)
511
540
  }
512
541
 
@@ -522,12 +551,13 @@ class Feature {
522
551
  isFiltered() {
523
552
  const filterKeys = this.datalayer.getFilterKeys()
524
553
  const filter = this._umap.browser.options.filter
525
- if (filter && !this.matchFilter(filter, filterKeys)) return true
526
- if (!this.matchFacets()) return true
554
+ if (filter && !this.matchFullTextFilter(filter, filterKeys)) return true
555
+ if (this._umap.filters.matchFeature(this)) return true
556
+ if (this.datalayer.filters.matchFeature(this)) return true
527
557
  return false
528
558
  }
529
559
 
530
- matchFilter(filter, keys) {
560
+ matchFullTextFilter(filter, keys) {
531
561
  filter = filter.toLowerCase()
532
562
  // When user hasn't touched settings, when a feature has no name
533
563
  // it will use the datalayer's name, so let's make the filtering
@@ -545,28 +575,6 @@ class Feature {
545
575
  return false
546
576
  }
547
577
 
548
- matchFacets() {
549
- const selected = this._umap.facets.selected
550
- for (const [name, { type, min, max, choices }] of Object.entries(selected)) {
551
- let value = this.properties[name]
552
- const parser = this._umap.facets.getParser(type)
553
- value = parser(value)
554
- switch (type) {
555
- case 'date':
556
- case 'datetime':
557
- case 'number':
558
- if (!Number.isNaN(min) && !Number.isNaN(value) && min > value) return false
559
- if (!Number.isNaN(max) && !Number.isNaN(value) && max < value) return false
560
- break
561
- default:
562
- value = value || translate('<empty value>')
563
- if (choices?.length && !choices.includes(value)) return false
564
- break
565
- }
566
- }
567
- return true
568
- }
569
-
570
578
  isMulti() {
571
579
  return false
572
580
  }
@@ -592,6 +600,7 @@ class Feature {
592
600
  if (U.lang) properties.lang = U.lang
593
601
  properties.rank = this.getRank() + 1
594
602
  properties.layer = this.datalayer.getName()
603
+ properties.id = this.id
595
604
  if (this.ui._map && this.hasGeom()) {
596
605
  const center = this.center
597
606
  properties.lat = center.lat
@@ -610,7 +619,7 @@ class Feature {
610
619
  }
611
620
 
612
621
  redraw() {
613
- if (this.datalayer?.isVisible()) {
622
+ if (this.datalayer?.isVisible() && this.ui?.isVisible()) {
614
623
  if (this.getUIClass() !== this.ui.getClass()) {
615
624
  this.datalayer.hideFeature(this)
616
625
  this.makeUI()
@@ -646,8 +655,7 @@ class Feature {
646
655
  items.push({
647
656
  label: translate('Copy as GeoJSON'),
648
657
  action: () => {
649
- L.Util.copyToClipboard(JSON.stringify(this.toGeoJSON()))
650
- this._umap.tooltip.open({ content: L._('✅ Copied!') })
658
+ DOMUtils.copyToClipboard(JSON.stringify(this.toGeoJSON()))
651
659
  },
652
660
  })
653
661
  return items
@@ -706,6 +714,19 @@ class Feature {
706
714
  this.ui.closePopup()
707
715
  }
708
716
  }
717
+
718
+ makePreview(element) {
719
+ element.innerHTML = ''
720
+ const symbol = this._getIconUrl ? Icon.formatUrl(this._getIconUrl(), this) : null
721
+ const bgcolor = this.getPreviewColor()
722
+ element.style.backgroundColor = bgcolor
723
+ if (symbol && symbol !== SCHEMA.iconUrl.default) {
724
+ const icon = Icon.makeElement(symbol, element)
725
+ Icon.setContrast(icon, element, symbol, bgcolor)
726
+ } else if (DOMUtils.contrastedColor(element, bgcolor)) {
727
+ element.classList.add('icon-white')
728
+ }
729
+ }
709
730
  }
710
731
 
711
732
  export class Point extends Feature {
@@ -931,6 +952,23 @@ class Path extends Feature {
931
952
  }
932
953
  return items
933
954
  }
955
+
956
+ addExtraEditFieldset(container) {
957
+ const options = [
958
+ 'properties._umap_options.textPath',
959
+ 'properties._umap_options.textPathColor',
960
+ 'properties._umap_options.textPathRepeat',
961
+ 'properties._umap_options.textPathRotate',
962
+ 'properties._umap_options.textPathSize',
963
+ 'properties._umap_options.textPathOffset',
964
+ 'properties._umap_options.textPathPosition',
965
+ ]
966
+ const builder = new MutatingForm(this, options, {
967
+ id: 'umap-feature-line-decoration',
968
+ })
969
+ const fieldset = DomUtil.createFieldset(container, translate('Line decoration'))
970
+ fieldset.appendChild(builder.build())
971
+ }
934
972
  }
935
973
 
936
974
  export class LineString extends Path {
@@ -1229,26 +1267,6 @@ export class LineString extends Path {
1229
1267
  button.addEventListener('click', async () => this.computeRoute())
1230
1268
  }
1231
1269
 
1232
- addExtraEditFieldset(container) {
1233
- const options = [
1234
- 'properties._umap_options.textPath',
1235
- 'properties._umap_options.textPathColor',
1236
- 'properties._umap_options.textPathRepeat',
1237
- 'properties._umap_options.textPathRotate',
1238
- 'properties._umap_options.textPathSize',
1239
- 'properties._umap_options.textPathOffset',
1240
- 'properties._umap_options.textPathPosition',
1241
- ]
1242
- const builder = new MutatingForm(this, options, {
1243
- id: 'umap-feature-line-decoration',
1244
- })
1245
- const fieldset = DomUtil.createFieldset(container, translate('Line decoration'))
1246
- fieldset.appendChild(builder.build())
1247
- if (this._umap.properties.ORSAPIKey && this.isRoute()) {
1248
- this._editRoute(container)
1249
- }
1250
- }
1251
-
1252
1270
  async computeElevation() {
1253
1271
  if (!this._umap.properties.ORSAPIKey) return
1254
1272
  const importer = new OpenRouteService(this._umap)
@@ -1258,6 +1276,7 @@ export class LineString extends Path {
1258
1276
  this.geometry = geometry
1259
1277
  this.ui.resetTooltip()
1260
1278
  this.sync.update('geometry', this.geometry, oldGeometry)
1279
+ Alert.success(translate('Elevation has been added!'))
1261
1280
  }
1262
1281
  }
1263
1282
 
@@ -1273,6 +1292,13 @@ export class LineString extends Path {
1273
1292
  }
1274
1293
  })
1275
1294
  }
1295
+
1296
+ addExtraEditFieldset(container) {
1297
+ super.addExtraEditFieldset(container)
1298
+ if (this._umap.properties.ORSAPIKey && this.isRoute()) {
1299
+ this._editRoute(container)
1300
+ }
1301
+ }
1276
1302
  }
1277
1303
 
1278
1304
  export class Polygon extends Path {