umap-project 3.3.6__py3-none-any.whl → 3.4.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 (216) hide show
  1. umap/__init__.py +1 -1
  2. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  3. umap/locale/cs_CZ/LC_MESSAGES/django.po +43 -33
  4. umap/locale/da/LC_MESSAGES/django.mo +0 -0
  5. umap/locale/da/LC_MESSAGES/django.po +43 -33
  6. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/de/LC_MESSAGES/django.po +35 -29
  8. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  9. umap/locale/el/LC_MESSAGES/django.po +35 -29
  10. umap/locale/en/LC_MESSAGES/django.po +34 -28
  11. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  12. umap/locale/es/LC_MESSAGES/django.po +43 -33
  13. umap/locale/et/LC_MESSAGES/django.mo +0 -0
  14. umap/locale/et/LC_MESSAGES/django.po +58 -54
  15. umap/locale/eu/LC_MESSAGES/django.mo +0 -0
  16. umap/locale/eu/LC_MESSAGES/django.po +43 -33
  17. umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
  18. umap/locale/fa_IR/LC_MESSAGES/django.po +43 -33
  19. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  20. umap/locale/fr/LC_MESSAGES/django.po +36 -30
  21. umap/locale/gl/LC_MESSAGES/django.mo +0 -0
  22. umap/locale/gl/LC_MESSAGES/django.po +43 -33
  23. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  24. umap/locale/hu/LC_MESSAGES/django.po +35 -29
  25. umap/locale/is/LC_MESSAGES/django.mo +0 -0
  26. umap/locale/is/LC_MESSAGES/django.po +43 -33
  27. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  28. umap/locale/it/LC_MESSAGES/django.po +43 -33
  29. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  30. umap/locale/nl/LC_MESSAGES/django.po +35 -29
  31. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  32. umap/locale/pl/LC_MESSAGES/django.po +43 -33
  33. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  34. umap/locale/pt/LC_MESSAGES/django.po +43 -33
  35. umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
  36. umap/locale/th_TH/LC_MESSAGES/django.po +310 -109
  37. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  38. umap/locale/zh_TW/LC_MESSAGES/django.po +80 -70
  39. umap/management/commands/switch_user.py +2 -2
  40. umap/static/umap/base.css +89 -32
  41. umap/static/umap/content.css +129 -33
  42. umap/static/umap/css/bar.css +82 -20
  43. umap/static/umap/css/browser.css +163 -0
  44. umap/static/umap/css/contextmenu.css +15 -0
  45. umap/static/umap/css/dialog.css +36 -16
  46. umap/static/umap/css/form.css +122 -32
  47. umap/static/umap/css/icon.css +46 -3
  48. umap/static/umap/css/panel.css +7 -3
  49. umap/static/umap/css/popup.css +34 -8
  50. umap/static/umap/css/tooltip.css +8 -4
  51. umap/static/umap/img/16-white.svg +26 -8
  52. umap/static/umap/img/16.svg +1 -1
  53. umap/static/umap/img/source/16-white.svg +36 -18
  54. umap/static/umap/img/source/16.svg +1 -1
  55. umap/static/umap/js/components/alerts/alert.css +69 -31
  56. umap/static/umap/js/components/alerts/alert.js +20 -2
  57. umap/static/umap/js/modules/browser.js +63 -55
  58. umap/static/umap/js/modules/caption.js +10 -7
  59. umap/static/umap/js/modules/data/features.js +82 -59
  60. umap/static/umap/js/modules/data/layer.js +56 -157
  61. umap/static/umap/js/modules/domutils.js +109 -0
  62. umap/static/umap/js/modules/filters.js +807 -0
  63. umap/static/umap/js/modules/form/builder.js +8 -5
  64. umap/static/umap/js/modules/form/fields.js +110 -220
  65. umap/static/umap/js/modules/formatter.js +24 -1
  66. umap/static/umap/js/modules/help.js +3 -2
  67. umap/static/umap/js/modules/importers/opendata.js +5 -0
  68. umap/static/umap/js/modules/importers/openrouteservice.js +6 -1
  69. umap/static/umap/js/modules/managers.js +265 -1
  70. umap/static/umap/js/modules/permissions.js +35 -31
  71. umap/static/umap/js/modules/rendering/controls.js +7 -7
  72. umap/static/umap/js/modules/rendering/icon.js +3 -8
  73. umap/static/umap/js/modules/rendering/layers/classified.js +17 -10
  74. umap/static/umap/js/modules/rendering/layers/cluster.js +2 -2
  75. umap/static/umap/js/modules/rendering/template.js +44 -8
  76. umap/static/umap/js/modules/rendering/ui.js +29 -23
  77. umap/static/umap/js/modules/rules.js +4 -3
  78. umap/static/umap/js/modules/schema.js +3 -6
  79. umap/static/umap/js/modules/share.js +4 -3
  80. umap/static/umap/js/modules/tableeditor.js +50 -38
  81. umap/static/umap/js/modules/templates.js +2 -3
  82. umap/static/umap/js/modules/ui/bar.js +42 -18
  83. umap/static/umap/js/modules/ui/dialog.js +33 -31
  84. umap/static/umap/js/modules/ui/panel.js +21 -7
  85. umap/static/umap/js/modules/ui/tooltip.js +6 -5
  86. umap/static/umap/js/modules/umap.js +148 -51
  87. umap/static/umap/js/modules/utils.js +23 -1
  88. umap/static/umap/js/umap.core.js +1 -110
  89. umap/static/umap/locale/am_ET.js +40 -14
  90. umap/static/umap/locale/am_ET.json +40 -14
  91. umap/static/umap/locale/ar.js +40 -14
  92. umap/static/umap/locale/ar.json +40 -14
  93. umap/static/umap/locale/ast.js +40 -14
  94. umap/static/umap/locale/ast.json +40 -14
  95. umap/static/umap/locale/bg.js +40 -14
  96. umap/static/umap/locale/bg.json +40 -14
  97. umap/static/umap/locale/br.js +47 -21
  98. umap/static/umap/locale/br.json +47 -21
  99. umap/static/umap/locale/ca.js +40 -14
  100. umap/static/umap/locale/ca.json +40 -14
  101. umap/static/umap/locale/cs_CZ.js +40 -14
  102. umap/static/umap/locale/cs_CZ.json +40 -14
  103. umap/static/umap/locale/da.js +40 -14
  104. umap/static/umap/locale/da.json +40 -14
  105. umap/static/umap/locale/de.js +39 -13
  106. umap/static/umap/locale/de.json +39 -13
  107. umap/static/umap/locale/el.js +40 -14
  108. umap/static/umap/locale/el.json +40 -14
  109. umap/static/umap/locale/en.js +39 -13
  110. umap/static/umap/locale/en.json +39 -13
  111. umap/static/umap/locale/en_US.json +40 -14
  112. umap/static/umap/locale/es.js +40 -14
  113. umap/static/umap/locale/es.json +40 -14
  114. umap/static/umap/locale/et.js +79 -53
  115. umap/static/umap/locale/et.json +79 -53
  116. umap/static/umap/locale/eu.js +72 -46
  117. umap/static/umap/locale/eu.json +72 -46
  118. umap/static/umap/locale/fa_IR.js +40 -14
  119. umap/static/umap/locale/fa_IR.json +40 -14
  120. umap/static/umap/locale/fi.js +40 -14
  121. umap/static/umap/locale/fi.json +40 -14
  122. umap/static/umap/locale/fr.js +39 -13
  123. umap/static/umap/locale/fr.json +39 -13
  124. umap/static/umap/locale/gl.js +40 -14
  125. umap/static/umap/locale/gl.json +40 -14
  126. umap/static/umap/locale/he.js +40 -14
  127. umap/static/umap/locale/he.json +40 -14
  128. umap/static/umap/locale/hr.js +40 -14
  129. umap/static/umap/locale/hr.json +40 -14
  130. umap/static/umap/locale/hu.js +40 -14
  131. umap/static/umap/locale/hu.json +40 -14
  132. umap/static/umap/locale/id.js +40 -14
  133. umap/static/umap/locale/id.json +40 -14
  134. umap/static/umap/locale/is.js +40 -14
  135. umap/static/umap/locale/is.json +40 -14
  136. umap/static/umap/locale/it.js +40 -14
  137. umap/static/umap/locale/it.json +40 -14
  138. umap/static/umap/locale/ja.js +40 -14
  139. umap/static/umap/locale/ja.json +40 -14
  140. umap/static/umap/locale/ko.js +40 -14
  141. umap/static/umap/locale/ko.json +40 -14
  142. umap/static/umap/locale/lt.js +40 -14
  143. umap/static/umap/locale/lt.json +40 -14
  144. umap/static/umap/locale/ms.js +40 -14
  145. umap/static/umap/locale/ms.json +40 -14
  146. umap/static/umap/locale/nl.js +40 -14
  147. umap/static/umap/locale/nl.json +40 -14
  148. umap/static/umap/locale/no.js +40 -14
  149. umap/static/umap/locale/no.json +40 -14
  150. umap/static/umap/locale/pl.js +40 -14
  151. umap/static/umap/locale/pl.json +40 -14
  152. umap/static/umap/locale/pl_PL.json +40 -14
  153. umap/static/umap/locale/pt.js +40 -14
  154. umap/static/umap/locale/pt.json +40 -14
  155. umap/static/umap/locale/pt_BR.js +40 -14
  156. umap/static/umap/locale/pt_BR.json +40 -14
  157. umap/static/umap/locale/pt_PT.js +40 -14
  158. umap/static/umap/locale/pt_PT.json +40 -14
  159. umap/static/umap/locale/ro.js +40 -14
  160. umap/static/umap/locale/ro.json +40 -14
  161. umap/static/umap/locale/ru.js +40 -14
  162. umap/static/umap/locale/ru.json +40 -14
  163. umap/static/umap/locale/sk_SK.js +40 -14
  164. umap/static/umap/locale/sk_SK.json +40 -14
  165. umap/static/umap/locale/sl.js +40 -14
  166. umap/static/umap/locale/sl.json +40 -14
  167. umap/static/umap/locale/sr.js +40 -14
  168. umap/static/umap/locale/sr.json +40 -14
  169. umap/static/umap/locale/sv.js +40 -14
  170. umap/static/umap/locale/sv.json +40 -14
  171. umap/static/umap/locale/th_TH.js +40 -14
  172. umap/static/umap/locale/th_TH.json +40 -14
  173. umap/static/umap/locale/tr.js +40 -14
  174. umap/static/umap/locale/tr.json +40 -14
  175. umap/static/umap/locale/uk_UA.js +40 -14
  176. umap/static/umap/locale/uk_UA.json +40 -14
  177. umap/static/umap/locale/vi.js +40 -14
  178. umap/static/umap/locale/vi.json +40 -14
  179. umap/static/umap/locale/vi_VN.json +40 -14
  180. umap/static/umap/locale/zh.js +40 -14
  181. umap/static/umap/locale/zh.json +40 -14
  182. umap/static/umap/locale/zh_CN.json +40 -14
  183. umap/static/umap/locale/zh_TW.Big5.json +40 -14
  184. umap/static/umap/locale/zh_TW.js +39 -13
  185. umap/static/umap/locale/zh_TW.json +39 -13
  186. umap/static/umap/map.css +60 -223
  187. umap/static/umap/unittests/utils.js +18 -0
  188. umap/static/umap/vars.css +23 -5
  189. umap/templates/umap/components/alerts/alert.html +32 -29
  190. umap/templates/umap/css.html +2 -1
  191. umap/templates/umap/login_popup_end.html +18 -9
  192. umap/templates/umap/user_map_table.html +7 -2
  193. umap/tests/integration/conftest.py +2 -6
  194. umap/tests/integration/test_anonymous_owned_map.py +89 -36
  195. umap/tests/integration/test_basics.py +25 -1
  196. umap/tests/integration/test_browser.py +37 -0
  197. umap/tests/integration/test_draw_polygon.py +2 -0
  198. umap/tests/integration/test_edit_marker.py +1 -1
  199. umap/tests/integration/test_export_map.py +19 -0
  200. umap/tests/integration/test_fields.py +522 -0
  201. umap/tests/integration/test_filters.py +617 -0
  202. umap/tests/integration/test_import.py +15 -42
  203. umap/tests/integration/test_remote_data.py +60 -4
  204. umap/tests/integration/test_share.py +4 -4
  205. umap/tests/integration/test_tableeditor.py +31 -7
  206. umap/tests/integration/test_websocket_sync.py +3 -1
  207. umap/tests/test_dashboard.py +10 -0
  208. umap/urls.py +1 -0
  209. umap/views.py +5 -0
  210. {umap_project-3.3.6.dist-info → umap_project-3.4.0b0.dist-info}/METADATA +12 -12
  211. {umap_project-3.3.6.dist-info → umap_project-3.4.0b0.dist-info}/RECORD +214 -211
  212. umap/static/umap/js/modules/facets.js +0 -164
  213. umap/tests/integration/test_facets_browser.py +0 -279
  214. {umap_project-3.3.6.dist-info → umap_project-3.4.0b0.dist-info}/WHEEL +0 -0
  215. {umap_project-3.3.6.dist-info → umap_project-3.4.0b0.dist-info}/entry_points.txt +0 -0
  216. {umap_project-3.3.6.dist-info → umap_project-3.4.0b0.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)
@@ -109,7 +104,7 @@ export default class Browser {
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()
@@ -124,15 +119,14 @@ export default class Browser {
124
119
  }
125
120
 
126
121
  hasFilters() {
127
- return !!this.options.filter || this._umap.facets.isActive()
122
+ return !!this.options.filter || this._umap.hasFilters()
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
 
@@ -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
+ })
195
203
 
196
- let fields = [
204
+ this.update()
205
+ }
206
+
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() {
@@ -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.datalayer.fields.all(), ...this._umap.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))
@@ -245,6 +263,14 @@ class Feature {
245
263
  if (field.key === 'description' || field.type === 'Text') {
246
264
  options.handler = 'Textarea'
247
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'
248
274
  }
249
275
  properties.push([`properties.${field.key}`, options])
250
276
  }
@@ -257,13 +283,13 @@ class Feature {
257
283
  `<button type="button"><i class="icon icon-16 icon-add"></i>${translate('Add a new field')}</button>`
258
284
  )
259
285
  button.addEventListener('click', () => {
260
- this.datalayer.addProperty().then(() => this.edit({ force: true }))
286
+ this.datalayer.fields.editField().then(() => this.edit({ force: true }))
261
287
  })
262
288
  form.appendChild(button)
263
289
  this.appendEditFieldsets(container)
264
290
  const [details, { fieldset }] = Utils.loadTemplateWithRefs(`
265
291
  <details>
266
- <summary>${translate('Advanced actions')}</summary>
292
+ <summary><h4>${translate('Advanced actions')}</h4></summary>
267
293
  <fieldset class="button-bar by2" data-ref=fieldset></fieldset>
268
294
  </details>
269
295
  `)
@@ -298,7 +324,7 @@ class Feature {
298
324
  container.appendChild(button)
299
325
  }
300
326
 
301
- addExtraEditFieldset() {}
327
+ addExtraEditFieldset(container) {}
302
328
 
303
329
  appendEditFieldsets(container) {
304
330
  const optionsFields = this.getShapeOptions()
@@ -498,16 +524,16 @@ class Feature {
498
524
  return properties
499
525
  }
500
526
 
501
- deleteProperty(property) {
502
- const oldValue = this.properties[property]
503
- delete this.properties[property]
504
- this.sync.update(`properties.${property}`, undefined, oldValue)
527
+ deleteField(name) {
528
+ const oldValue = this.properties[name]
529
+ delete this.properties[name]
530
+ this.sync.update(`properties.${name}`, undefined, oldValue)
505
531
  }
506
532
 
507
- renameProperty(from, to) {
533
+ renameField(from, to) {
508
534
  const oldValue = this.properties[from]
509
535
  this.properties[to] = this.properties[from]
510
- this.deleteProperty(from)
536
+ this.deleteField(from)
511
537
  this.sync.update(`properties.${to}`, oldValue, undefined)
512
538
  }
513
539
 
@@ -523,12 +549,13 @@ class Feature {
523
549
  isFiltered() {
524
550
  const filterKeys = this.datalayer.getFilterKeys()
525
551
  const filter = this._umap.browser.options.filter
526
- if (filter && !this.matchFilter(filter, filterKeys)) return true
527
- if (!this.matchFacets()) return true
552
+ if (filter && !this.matchFullTextFilter(filter, filterKeys)) return true
553
+ if (this._umap.filters.matchFeature(this)) return true
554
+ if (this.datalayer.filters.matchFeature(this)) return true
528
555
  return false
529
556
  }
530
557
 
531
- matchFilter(filter, keys) {
558
+ matchFullTextFilter(filter, keys) {
532
559
  filter = filter.toLowerCase()
533
560
  // When user hasn't touched settings, when a feature has no name
534
561
  // it will use the datalayer's name, so let's make the filtering
@@ -546,28 +573,6 @@ class Feature {
546
573
  return false
547
574
  }
548
575
 
549
- matchFacets() {
550
- const selected = this._umap.facets.selected
551
- for (const [name, { type, min, max, choices }] of Object.entries(selected)) {
552
- let value = this.properties[name]
553
- const parser = this._umap.facets.getParser(type)
554
- value = parser(value)
555
- switch (type) {
556
- case 'date':
557
- case 'datetime':
558
- case 'number':
559
- if (!Number.isNaN(min) && !Number.isNaN(value) && min > value) return false
560
- if (!Number.isNaN(max) && !Number.isNaN(value) && max < value) return false
561
- break
562
- default:
563
- value = value || translate('<empty value>')
564
- if (choices?.length && !choices.includes(value)) return false
565
- break
566
- }
567
- }
568
- return true
569
- }
570
-
571
576
  isMulti() {
572
577
  return false
573
578
  }
@@ -593,6 +598,7 @@ class Feature {
593
598
  if (U.lang) properties.lang = U.lang
594
599
  properties.rank = this.getRank() + 1
595
600
  properties.layer = this.datalayer.getName()
601
+ properties.id = this.id
596
602
  if (this.ui._map && this.hasGeom()) {
597
603
  const center = this.center
598
604
  properties.lat = center.lat
@@ -611,7 +617,7 @@ class Feature {
611
617
  }
612
618
 
613
619
  redraw() {
614
- if (this.datalayer?.isVisible()) {
620
+ if (this.datalayer?.isVisible() && this.ui?.isVisible()) {
615
621
  if (this.getUIClass() !== this.ui.getClass()) {
616
622
  this.datalayer.hideFeature(this)
617
623
  this.makeUI()
@@ -647,8 +653,7 @@ class Feature {
647
653
  items.push({
648
654
  label: translate('Copy as GeoJSON'),
649
655
  action: () => {
650
- L.Util.copyToClipboard(JSON.stringify(this.toGeoJSON()))
651
- this._umap.tooltip.open({ content: L._('✅ Copied!') })
656
+ DOMUtils.copyToClipboard(JSON.stringify(this.toGeoJSON()))
652
657
  },
653
658
  })
654
659
  return items
@@ -707,6 +712,19 @@ class Feature {
707
712
  this.ui.closePopup()
708
713
  }
709
714
  }
715
+
716
+ makePreview(element) {
717
+ element.innerHTML = ''
718
+ const symbol = this._getIconUrl ? Icon.formatUrl(this._getIconUrl(), this) : null
719
+ const bgcolor = this.getPreviewColor()
720
+ element.style.backgroundColor = bgcolor
721
+ if (symbol && symbol !== SCHEMA.iconUrl.default) {
722
+ const icon = Icon.makeElement(symbol, element)
723
+ Icon.setContrast(icon, element, symbol, bgcolor)
724
+ } else if (DOMUtils.contrastedColor(element, bgcolor)) {
725
+ element.classList.add('icon-white')
726
+ }
727
+ }
710
728
  }
711
729
 
712
730
  export class Point extends Feature {
@@ -932,6 +950,23 @@ class Path extends Feature {
932
950
  }
933
951
  return items
934
952
  }
953
+
954
+ addExtraEditFieldset(container) {
955
+ const options = [
956
+ 'properties._umap_options.textPath',
957
+ 'properties._umap_options.textPathColor',
958
+ 'properties._umap_options.textPathRepeat',
959
+ 'properties._umap_options.textPathRotate',
960
+ 'properties._umap_options.textPathSize',
961
+ 'properties._umap_options.textPathOffset',
962
+ 'properties._umap_options.textPathPosition',
963
+ ]
964
+ const builder = new MutatingForm(this, options, {
965
+ id: 'umap-feature-line-decoration',
966
+ })
967
+ const fieldset = DomUtil.createFieldset(container, translate('Line decoration'))
968
+ fieldset.appendChild(builder.build())
969
+ }
935
970
  }
936
971
 
937
972
  export class LineString extends Path {
@@ -1230,26 +1265,6 @@ export class LineString extends Path {
1230
1265
  button.addEventListener('click', async () => this.computeRoute())
1231
1266
  }
1232
1267
 
1233
- addExtraEditFieldset(container) {
1234
- const options = [
1235
- 'properties._umap_options.textPath',
1236
- 'properties._umap_options.textPathColor',
1237
- 'properties._umap_options.textPathRepeat',
1238
- 'properties._umap_options.textPathRotate',
1239
- 'properties._umap_options.textPathSize',
1240
- 'properties._umap_options.textPathOffset',
1241
- 'properties._umap_options.textPathPosition',
1242
- ]
1243
- const builder = new MutatingForm(this, options, {
1244
- id: 'umap-feature-line-decoration',
1245
- })
1246
- const fieldset = DomUtil.createFieldset(container, translate('Line decoration'))
1247
- fieldset.appendChild(builder.build())
1248
- if (this._umap.properties.ORSAPIKey && this.isRoute()) {
1249
- this._editRoute(container)
1250
- }
1251
- }
1252
-
1253
1268
  async computeElevation() {
1254
1269
  if (!this._umap.properties.ORSAPIKey) return
1255
1270
  const importer = new OpenRouteService(this._umap)
@@ -1259,6 +1274,7 @@ export class LineString extends Path {
1259
1274
  this.geometry = geometry
1260
1275
  this.ui.resetTooltip()
1261
1276
  this.sync.update('geometry', this.geometry, oldGeometry)
1277
+ Alert.success(translate('Elevation has been added!'))
1262
1278
  }
1263
1279
  }
1264
1280
 
@@ -1274,6 +1290,13 @@ export class LineString extends Path {
1274
1290
  }
1275
1291
  })
1276
1292
  }
1293
+
1294
+ addExtraEditFieldset(container) {
1295
+ super.addExtraEditFieldset(container)
1296
+ if (this._umap.properties.ORSAPIKey && this.isRoute()) {
1297
+ this._editRoute(container)
1298
+ }
1299
+ }
1277
1300
  }
1278
1301
 
1279
1302
  export class Polygon extends Path {