umap-project 3.3.5__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 (218) 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 +64 -56
  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 +75 -166
  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 -0
  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/base.js +1 -1
  74. umap/static/umap/js/modules/rendering/layers/classified.js +17 -10
  75. umap/static/umap/js/modules/rendering/layers/cluster.js +2 -2
  76. umap/static/umap/js/modules/rendering/template.js +44 -8
  77. umap/static/umap/js/modules/rendering/ui.js +29 -23
  78. umap/static/umap/js/modules/rules.js +4 -3
  79. umap/static/umap/js/modules/schema.js +3 -6
  80. umap/static/umap/js/modules/share.js +4 -3
  81. umap/static/umap/js/modules/tableeditor.js +50 -38
  82. umap/static/umap/js/modules/templates.js +2 -3
  83. umap/static/umap/js/modules/ui/bar.js +42 -18
  84. umap/static/umap/js/modules/ui/dialog.js +33 -31
  85. umap/static/umap/js/modules/ui/panel.js +21 -7
  86. umap/static/umap/js/modules/ui/tooltip.js +6 -5
  87. umap/static/umap/js/modules/umap.js +155 -46
  88. umap/static/umap/js/modules/utils.js +23 -1
  89. umap/static/umap/js/umap.core.js +1 -110
  90. umap/static/umap/locale/am_ET.js +40 -14
  91. umap/static/umap/locale/am_ET.json +40 -14
  92. umap/static/umap/locale/ar.js +40 -14
  93. umap/static/umap/locale/ar.json +40 -14
  94. umap/static/umap/locale/ast.js +40 -14
  95. umap/static/umap/locale/ast.json +40 -14
  96. umap/static/umap/locale/bg.js +40 -14
  97. umap/static/umap/locale/bg.json +40 -14
  98. umap/static/umap/locale/br.js +47 -21
  99. umap/static/umap/locale/br.json +47 -21
  100. umap/static/umap/locale/ca.js +40 -14
  101. umap/static/umap/locale/ca.json +40 -14
  102. umap/static/umap/locale/cs_CZ.js +40 -14
  103. umap/static/umap/locale/cs_CZ.json +40 -14
  104. umap/static/umap/locale/da.js +40 -14
  105. umap/static/umap/locale/da.json +40 -14
  106. umap/static/umap/locale/de.js +39 -13
  107. umap/static/umap/locale/de.json +39 -13
  108. umap/static/umap/locale/el.js +40 -14
  109. umap/static/umap/locale/el.json +40 -14
  110. umap/static/umap/locale/en.js +39 -13
  111. umap/static/umap/locale/en.json +39 -13
  112. umap/static/umap/locale/en_US.json +40 -14
  113. umap/static/umap/locale/es.js +40 -14
  114. umap/static/umap/locale/es.json +40 -14
  115. umap/static/umap/locale/et.js +79 -53
  116. umap/static/umap/locale/et.json +79 -53
  117. umap/static/umap/locale/eu.js +72 -46
  118. umap/static/umap/locale/eu.json +72 -46
  119. umap/static/umap/locale/fa_IR.js +40 -14
  120. umap/static/umap/locale/fa_IR.json +40 -14
  121. umap/static/umap/locale/fi.js +40 -14
  122. umap/static/umap/locale/fi.json +40 -14
  123. umap/static/umap/locale/fr.js +39 -13
  124. umap/static/umap/locale/fr.json +39 -13
  125. umap/static/umap/locale/gl.js +40 -14
  126. umap/static/umap/locale/gl.json +40 -14
  127. umap/static/umap/locale/he.js +40 -14
  128. umap/static/umap/locale/he.json +40 -14
  129. umap/static/umap/locale/hr.js +40 -14
  130. umap/static/umap/locale/hr.json +40 -14
  131. umap/static/umap/locale/hu.js +40 -14
  132. umap/static/umap/locale/hu.json +40 -14
  133. umap/static/umap/locale/id.js +40 -14
  134. umap/static/umap/locale/id.json +40 -14
  135. umap/static/umap/locale/is.js +40 -14
  136. umap/static/umap/locale/is.json +40 -14
  137. umap/static/umap/locale/it.js +40 -14
  138. umap/static/umap/locale/it.json +40 -14
  139. umap/static/umap/locale/ja.js +40 -14
  140. umap/static/umap/locale/ja.json +40 -14
  141. umap/static/umap/locale/ko.js +40 -14
  142. umap/static/umap/locale/ko.json +40 -14
  143. umap/static/umap/locale/lt.js +40 -14
  144. umap/static/umap/locale/lt.json +40 -14
  145. umap/static/umap/locale/ms.js +40 -14
  146. umap/static/umap/locale/ms.json +40 -14
  147. umap/static/umap/locale/nl.js +40 -14
  148. umap/static/umap/locale/nl.json +40 -14
  149. umap/static/umap/locale/no.js +40 -14
  150. umap/static/umap/locale/no.json +40 -14
  151. umap/static/umap/locale/pl.js +40 -14
  152. umap/static/umap/locale/pl.json +40 -14
  153. umap/static/umap/locale/pl_PL.json +40 -14
  154. umap/static/umap/locale/pt.js +40 -14
  155. umap/static/umap/locale/pt.json +40 -14
  156. umap/static/umap/locale/pt_BR.js +40 -14
  157. umap/static/umap/locale/pt_BR.json +40 -14
  158. umap/static/umap/locale/pt_PT.js +40 -14
  159. umap/static/umap/locale/pt_PT.json +40 -14
  160. umap/static/umap/locale/ro.js +40 -14
  161. umap/static/umap/locale/ro.json +40 -14
  162. umap/static/umap/locale/ru.js +40 -14
  163. umap/static/umap/locale/ru.json +40 -14
  164. umap/static/umap/locale/sk_SK.js +40 -14
  165. umap/static/umap/locale/sk_SK.json +40 -14
  166. umap/static/umap/locale/sl.js +40 -14
  167. umap/static/umap/locale/sl.json +40 -14
  168. umap/static/umap/locale/sr.js +40 -14
  169. umap/static/umap/locale/sr.json +40 -14
  170. umap/static/umap/locale/sv.js +40 -14
  171. umap/static/umap/locale/sv.json +40 -14
  172. umap/static/umap/locale/th_TH.js +40 -14
  173. umap/static/umap/locale/th_TH.json +40 -14
  174. umap/static/umap/locale/tr.js +40 -14
  175. umap/static/umap/locale/tr.json +40 -14
  176. umap/static/umap/locale/uk_UA.js +40 -14
  177. umap/static/umap/locale/uk_UA.json +40 -14
  178. umap/static/umap/locale/vi.js +40 -14
  179. umap/static/umap/locale/vi.json +40 -14
  180. umap/static/umap/locale/vi_VN.json +40 -14
  181. umap/static/umap/locale/zh.js +40 -14
  182. umap/static/umap/locale/zh.json +40 -14
  183. umap/static/umap/locale/zh_CN.json +40 -14
  184. umap/static/umap/locale/zh_TW.Big5.json +40 -14
  185. umap/static/umap/locale/zh_TW.js +40 -14
  186. umap/static/umap/locale/zh_TW.json +40 -14
  187. umap/static/umap/map.css +60 -223
  188. umap/static/umap/unittests/utils.js +18 -0
  189. umap/static/umap/vars.css +23 -5
  190. umap/templates/umap/components/alerts/alert.html +32 -29
  191. umap/templates/umap/css.html +2 -1
  192. umap/templates/umap/login_popup_end.html +18 -9
  193. umap/templates/umap/user_map_table.html +7 -2
  194. umap/tests/integration/conftest.py +2 -6
  195. umap/tests/integration/test_anonymous_owned_map.py +89 -36
  196. umap/tests/integration/test_basics.py +25 -1
  197. umap/tests/integration/test_browser.py +37 -0
  198. umap/tests/integration/test_datalayer.py +9 -16
  199. umap/tests/integration/test_draw_polygon.py +2 -0
  200. umap/tests/integration/test_edit_marker.py +1 -1
  201. umap/tests/integration/test_export_map.py +19 -0
  202. umap/tests/integration/test_fields.py +522 -0
  203. umap/tests/integration/test_filters.py +617 -0
  204. umap/tests/integration/test_import.py +15 -42
  205. umap/tests/integration/test_remote_data.py +60 -4
  206. umap/tests/integration/test_share.py +4 -4
  207. umap/tests/integration/test_tableeditor.py +31 -7
  208. umap/tests/integration/test_websocket_sync.py +3 -1
  209. umap/tests/test_dashboard.py +10 -0
  210. umap/urls.py +1 -0
  211. umap/views.py +5 -0
  212. {umap_project-3.3.5.dist-info → umap_project-3.4.0b0.dist-info}/METADATA +12 -12
  213. {umap_project-3.3.5.dist-info → umap_project-3.4.0b0.dist-info}/RECORD +216 -213
  214. umap/static/umap/js/modules/facets.js +0 -164
  215. umap/tests/integration/test_facets_browser.py +0 -279
  216. {umap_project-3.3.5.dist-info → umap_project-3.4.0b0.dist-info}/WHEEL +0 -0
  217. {umap_project-3.3.5.dist-info → umap_project-3.4.0b0.dist-info}/entry_points.txt +0 -0
  218. {umap_project-3.3.5.dist-info → umap_project-3.4.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -9,7 +9,7 @@ import {
9
9
  uMapAlert as Alert,
10
10
  uMapAlertConflict as AlertConflict,
11
11
  } from '../../components/alerts/alert.js'
12
- import { MutatingForm } from '../form/builder.js'
12
+ import { MutatingForm, Form } from '../form/builder.js'
13
13
  import { translate } from '../i18n.js'
14
14
  import { DataLayerPermissions } from '../permissions.js'
15
15
  import { Default as DefaultLayer } from '../rendering/layers/base.js'
@@ -21,8 +21,8 @@ import TableEditor from '../tableeditor.js'
21
21
  import * as Utils from '../utils.js'
22
22
  import { LineString, Point, Polygon } from './features.js'
23
23
  import Rules from '../rules.js'
24
- import Orderable from '../orderable.js'
25
- import { FeatureManager } from '../managers.js'
24
+ import { FeatureManager, FieldManager } from '../managers.js'
25
+ import { Filters } from '../filters.js'
26
26
 
27
27
  export const LAYER_TYPES = [
28
28
  DefaultLayer,
@@ -89,12 +89,14 @@ export class DataLayer {
89
89
  if (!this.createdOnServer) {
90
90
  if (this.showAtLoad()) this.show()
91
91
  }
92
- if (!this._needsFetch && !this._umap.fields.length) {
92
+ if (!this._needsFetch && !this._umap.fields.size) {
93
93
  this.properties.fields = [
94
94
  { key: U.DEFAULT_LABEL_KEY, type: 'String' },
95
95
  { key: 'description', type: 'Text' },
96
96
  ]
97
97
  }
98
+ this.fields = new FieldManager(this, this._umap.dialog)
99
+ this.filters = new Filters(this, this._umap)
98
100
 
99
101
  // Only layers that are displayed on load must be hidden/shown
100
102
  // Automatically, others will be shown manually, and thus will
@@ -129,7 +131,7 @@ export class DataLayer {
129
131
  }
130
132
 
131
133
  get cssId() {
132
- return `datalayer-${stamp(this)}`
134
+ return `datalayer-${this.id}`
133
135
  }
134
136
 
135
137
  get rank() {
@@ -146,17 +148,10 @@ export class DataLayer {
146
148
  this.properties.rank = value
147
149
  }
148
150
 
149
- get fields() {
150
- if (!this.properties.fields) this.properties.fields = []
151
- return this.properties.fields
152
- }
153
-
154
- set fields(fields) {
155
- this.properties.fields = fields
156
- }
157
-
158
151
  get fieldKeys() {
159
- return this.fields.map((field) => field.key)
152
+ // Needed to get a similar API from layer and uMap, but
153
+ // uMap whould return concat of all datalayers fields
154
+ return Array.from(this.fields.keys())
160
155
  }
161
156
 
162
157
  get sortKey() {
@@ -174,6 +169,18 @@ export class DataLayer {
174
169
  // Propagate will remove the fields it has already
175
170
  // processed
176
171
  fields = this.propagate(fields)
172
+ if (fields.includes('properties.fields')) {
173
+ this.fields?.pull()
174
+ if (this._umap.browser.isOpen()) {
175
+ this._umap.browser.buildFilters()
176
+ }
177
+ }
178
+ if (fields.includes('properties.filters')) {
179
+ this.filters.load()
180
+ if (this._umap.browser.isOpen()) {
181
+ this._umap.browser.buildFilters()
182
+ }
183
+ }
177
184
 
178
185
  const impacts = Utils.getImpactsFromSchema(fields)
179
186
 
@@ -225,17 +232,27 @@ export class DataLayer {
225
232
  }
226
233
 
227
234
  showAtLoad() {
228
- return this.autoLoaded() && this.showAtZoom()
235
+ return this.autoLoaded && this.showAtZoom()
229
236
  }
230
237
 
231
- autoLoaded() {
232
- if (!this._umap.datalayersFromQueryString) return this.properties.displayOnLoad
233
- const datalayerIds = this._umap.datalayersFromQueryString
234
- let loadMe = datalayerIds.includes(this.id.toString())
235
- if (this.properties.old_id) {
236
- loadMe = loadMe || datalayerIds.includes(this.properties.old_id.toString())
238
+ get autoLoaded() {
239
+ if (this._autoLoaded === undefined) {
240
+ if (this._umap.datalayersFromQueryString) {
241
+ const datalayerIds = this._umap.datalayersFromQueryString
242
+ this._autoLoaded = datalayerIds.includes(this.id.toString())
243
+ if (this.properties.old_id) {
244
+ this._autoLoaded =
245
+ this._autoLoaded || datalayerIds.includes(this.properties.old_id.toString())
246
+ }
247
+ } else {
248
+ this._autoLoaded = this.properties.displayOnLoad
249
+ }
237
250
  }
238
- return loadMe
251
+ return this._autoLoaded
252
+ }
253
+
254
+ set autoLoaded(value) {
255
+ this._autoLoaded = value
239
256
  }
240
257
 
241
258
  insertBefore(other) {
@@ -438,7 +455,8 @@ export class DataLayer {
438
455
  this.inferFields(feature)
439
456
  try {
440
457
  this.showFeature(feature)
441
- } catch {
458
+ } catch (error) {
459
+ console.error(error)
442
460
  if (this._umap.editEnabled) {
443
461
  Alert.error(translate('Skipping invalid geometry'))
444
462
  }
@@ -472,88 +490,43 @@ export class DataLayer {
472
490
  }
473
491
 
474
492
  inferFields(feature) {
475
- if (!this.properties.fields) this.properties.fields = []
476
- const keys = this.fieldKeys
477
493
  for (const key in feature.properties) {
478
494
  if (typeof feature.properties[key] !== 'object') {
479
495
  if (key.indexOf('_') === 0) continue
480
- if (keys.includes(key)) continue
481
- this.properties.fields.push({ key, type: 'String' })
496
+ if (this.fields.has(key)) continue
497
+ if (this._umap.fields.has(key)) continue
498
+ let type = 'String'
499
+ if (key === 'description') type = 'Text'
500
+ this.fields.add({ key, type })
482
501
  }
483
502
  }
503
+ this.fields.push()
484
504
  }
485
505
 
486
- async confirmDeleteProperty(property) {
487
- return this._umap.dialog
488
- .confirm(
489
- translate('Are you sure you want to delete this field on all the features?')
490
- )
491
- .then(() => {
492
- this.deleteProperty(property)
493
- })
506
+ renameField(oldName, newName) {
507
+ this.renameFeaturesField(oldName, newName)
494
508
  }
495
509
 
496
- async askForRenameProperty(property) {
497
- return this._umap.dialog
498
- .prompt(translate('Please enter the new name of this field'))
499
- .then(({ prompt }) => {
500
- if (!prompt || !this.validateName(prompt)) return
501
- this.renameProperty(property, prompt)
502
- })
503
- }
504
-
505
- renameProperty(oldName, newName) {
506
- this.sync.startBatch()
507
- const oldFields = Utils.CopyJSON(this.fields)
508
- for (const field of this.fields) {
509
- if (field.key === oldName) {
510
- field.key = newName
511
- break
512
- }
513
- }
514
- this.sync.update('properties.fields', this.fields, oldFields)
510
+ renameFeaturesField(oldName, newName) {
515
511
  this.features.forEach((feature) => {
516
- feature.renameProperty(oldName, newName)
512
+ feature.renameField(oldName, newName)
517
513
  })
518
- this.sync.commitBatch()
519
514
  }
520
515
 
521
- deleteProperty(property) {
522
- this.sync.startBatch()
523
- const oldFields = Utils.CopyJSON(this.fields)
524
- this.fields = this.fields.filter((field) => field.key !== property)
525
- this.sync.update('properties.fields', this.fields, oldFields)
526
- this.features.forEach((feature) => {
527
- feature.deleteProperty(property)
528
- })
529
- this.sync.commitBatch()
516
+ deleteField(name) {
517
+ this.deleteFeaturesField(name)
530
518
  }
531
519
 
532
- addProperty() {
533
- let resolve = undefined
534
- const promise = new Promise((r) => {
535
- resolve = r
536
- })
537
- this._umap.dialog
538
- .prompt(translate('Please enter the name of the property'))
539
- .then(({ prompt }) => {
540
- if (!prompt || !this.validateName(prompt)) return
541
- this.properties.fields.push({ key: prompt, type: 'String' })
542
- resolve()
520
+ deleteFeaturesField(name) {
521
+ if (!this._umap.fields.has(name) && !this.fields.has(name)) {
522
+ this.features.forEach((feature) => {
523
+ feature.deleteField(name)
543
524
  })
544
- return promise
525
+ }
545
526
  }
546
527
 
547
- validateName(name) {
548
- if (name.includes('.')) {
549
- Alert.error(translate('Name “{name}” should not contain a dot.', { name }))
550
- return false
551
- }
552
- if (this.fieldKeys.includes(name)) {
553
- Alert.error(translate('This name already exists: “{name}”', { name }))
554
- return false
555
- }
556
- return true
528
+ eachFeature(callback) {
529
+ this.features.forEach((feature) => callback(feature))
557
530
  }
558
531
 
559
532
  sortedValues(property) {
@@ -824,7 +797,7 @@ export class DataLayer {
824
797
  const builder = new MutatingForm(this, layerFields)
825
798
  const template = `
826
799
  <details id="layer-properties">
827
- <summary>${this.layer.getName()}: ${translate('settings')}</summary>
800
+ <summary><h4>${this.layer.getName()}: ${translate('settings')}</h4></summary>
828
801
  <fieldset data-ref=fieldset></fieldset>
829
802
  </details>
830
803
  `
@@ -918,64 +891,6 @@ export class DataLayer {
918
891
  fieldset.appendChild(builder.build())
919
892
  }
920
893
 
921
- _editFields(container) {
922
- const template = `
923
- <details id="fields">
924
- <summary>${translate('Manage Fields')}</summary>
925
- <fieldset>
926
- <ul data-ref=ul></ul>
927
- <button type="button" data-ref=add><i class="icon icon-16 icon-add"></i>${translate('Add a new field')}</button>
928
- </fieldset>
929
- </details>
930
- `
931
- const [fieldset, { ul, add }] = Utils.loadTemplateWithRefs(template)
932
- add.addEventListener('click', () => {
933
- this.addProperty().then(() => {
934
- this.edit().then((panel) => {
935
- panel.scrollTo('details#fields')
936
- })
937
- })
938
- })
939
- container.appendChild(fieldset)
940
- for (const field of this.fields) {
941
- const [row, { rename, del }] = Utils.loadTemplateWithRefs(
942
- `<li class="orderable" data-key="${field.key}">
943
- <button class="icon icon-16 icon-edit" title="${translate('Rename this field')}" data-ref=rename></button>
944
- <button class="icon icon-16 icon-delete" title="${translate('Delete this field')}" data-ref=del></button>
945
- <i class="icon icon-16 icon-drag" title="${translate('Drag to reorder')}"></i>
946
- ${field.key}
947
- </li>`
948
- )
949
- ul.appendChild(row)
950
- rename.addEventListener('click', () => {
951
- this.askForRenameProperty(field.key).then(() => {
952
- this.edit().then((panel) => {
953
- panel.scrollTo('details#fields')
954
- })
955
- })
956
- })
957
- del.addEventListener('click', () => {
958
- this.confirmDeleteProperty(field.key).then(() => {
959
- this.edit().then((panel) => {
960
- panel.scrollTo('details#fields')
961
- })
962
- })
963
- })
964
- }
965
- const onReorder = (src, dst, initialIndex, finalIndex) => {
966
- const orderedKeys = Array.from(ul.querySelectorAll('li')).map(
967
- (el) => el.dataset.key
968
- )
969
- const oldFields = Utils.CopyJSON(this.properties.fields)
970
- this.properties.fields.sort(
971
- (fieldA, fieldB) =>
972
- orderedKeys.indexOf(fieldA.key) > orderedKeys.indexOf(fieldB.key)
973
- )
974
- this.sync.update('properties.fields', this.properties.fields, oldFields)
975
- }
976
- const orderable = new Orderable(ul, onReorder)
977
- }
978
-
979
894
  _editRemoteDataProperties(container) {
980
895
  // XXX I'm not sure **why** this is needed (as it's set during `this.initialize`)
981
896
  // but apparently it's needed.
@@ -1086,9 +1001,7 @@ export class DataLayer {
1086
1001
  this._editInteractionProperties(container)
1087
1002
  this._editTextPathProperties(container)
1088
1003
  this._editRemoteDataProperties(container)
1089
- if (!this.isRemoteLayer()) {
1090
- this._editFields(container)
1091
- }
1004
+ this.fields.edit(container)
1092
1005
  this.rules.edit(container)
1093
1006
 
1094
1007
  if (this._umap.properties.urls.datalayer_versions) {
@@ -1213,7 +1126,7 @@ export class DataLayer {
1213
1126
  // From now on, do not try to how/hide
1214
1127
  // automatically this layer, as user
1215
1128
  // has taken control on this.
1216
- this._forcedVisibility = true
1129
+ this.autoLoaded = false
1217
1130
  let display = force
1218
1131
  if (force === undefined) {
1219
1132
  if (!this.isVisible()) display = true
@@ -1284,7 +1197,8 @@ export class DataLayer {
1284
1197
  }
1285
1198
 
1286
1199
  umapGeoJSON() {
1287
- const geojson = this._umap.formatter.toFeatureCollection(this.features.all())
1200
+ const features = this.isRemoteLayer() ? [] : this.features.all()
1201
+ const geojson = this._umap.formatter.toFeatureCollection(features)
1288
1202
  geojson._umap_options = this.properties
1289
1203
  return geojson
1290
1204
  }
@@ -1321,12 +1235,12 @@ export class DataLayer {
1321
1235
  async save() {
1322
1236
  if (this.isDeleted) return await this.saveDelete()
1323
1237
  if (!this.isRemoteLayer() && !this.isLoaded()) return
1324
- const geojson = this.umapGeoJSON()
1325
1238
  const formData = new FormData()
1326
1239
  formData.append('name', this.properties.name)
1327
1240
  formData.append('display_on_load', !!this.properties.displayOnLoad)
1328
1241
  formData.append('rank', this.rank)
1329
1242
  formData.append('settings', this.prepareProperties())
1243
+ const geojson = this.umapGeoJSON()
1330
1244
  // Filename support is shaky, don't do it for now.
1331
1245
  const blob = new Blob([JSON.stringify(geojson)], { type: 'application/json' })
1332
1246
  formData.append('geojson', blob)
@@ -1428,13 +1342,12 @@ export class DataLayer {
1428
1342
  )) {
1429
1343
  container.innerHTML = ''
1430
1344
  if (this.layer.renderLegend) return this.layer.renderLegend(container)
1431
- const keys = new Set(this.fieldKeys)
1432
1345
  const rules = new Map()
1433
1346
  for (const rule of this.rules) {
1434
1347
  rules.set(rule.condition, rule)
1435
1348
  }
1436
1349
  for (const rule of this._umap.rules) {
1437
- if (!rules.has(rule.condition) && keys.has(rule.key)) {
1350
+ if (!rules.has(rule.condition) && this.fields.has(rule.key)) {
1438
1351
  rules.set(rule.condition, rule)
1439
1352
  }
1440
1353
  }
@@ -1458,6 +1371,11 @@ export class DataLayer {
1458
1371
  'icon-eye',
1459
1372
  translate('Show/hide layer')
1460
1373
  )
1374
+ const table = DomUtil.createButtonIcon(
1375
+ container,
1376
+ 'icon-table show-on-edit',
1377
+ translate('Edit properties in a table')
1378
+ )
1461
1379
  const zoomTo = DomUtil.createButtonIcon(
1462
1380
  container,
1463
1381
  'icon-zoom',
@@ -1468,11 +1386,6 @@ export class DataLayer {
1468
1386
  'icon-edit show-on-edit',
1469
1387
  translate('Edit')
1470
1388
  )
1471
- const table = DomUtil.createButtonIcon(
1472
- container,
1473
- 'icon-table show-on-edit',
1474
- translate('Edit properties in a table')
1475
- )
1476
1389
  const remove = DomUtil.createButtonIcon(
1477
1390
  container,
1478
1391
  'icon-delete show-on-edit',
@@ -1490,16 +1403,12 @@ export class DataLayer {
1490
1403
  }
1491
1404
  DomEvent.on(toggle, 'click', () => this.toggle())
1492
1405
  DomEvent.on(zoomTo, 'click', this.zoomTo, this)
1493
- container.classList.add(this.getHidableClass())
1406
+ container.classList.add(this.cssId)
1494
1407
  container.classList.toggle('off', !this.isVisible())
1495
1408
  }
1496
1409
 
1497
1410
  getHidableElements() {
1498
- return document.querySelectorAll(`.${this.getHidableClass()}`)
1499
- }
1500
-
1501
- getHidableClass() {
1502
- return `show_with_datalayer_${stamp(this)}`
1411
+ return document.querySelectorAll(`.${this.cssId}`)
1503
1412
  }
1504
1413
 
1505
1414
  propagateDelete() {
@@ -0,0 +1,109 @@
1
+ // Utils that needs the DOM
2
+ import * as Utils from './utils.js'
3
+ import { translate } from './i18n.js'
4
+ import Tooltip from './ui/tooltip.js'
5
+
6
+ export const copyToClipboard = (textToCopy) => {
7
+ const tooltip = new Tooltip()
8
+ // https://stackoverflow.com/a/65996386
9
+ // Navigator clipboard api needs a secure context (https)
10
+ if (navigator.clipboard && window.isSecureContext) {
11
+ navigator.clipboard.writeText(textToCopy)
12
+ } else {
13
+ // Use the 'out of viewport hidden text area' trick
14
+ const textArea = document.createElement('textarea')
15
+ textArea.value = textToCopy
16
+
17
+ // Move textarea out of the viewport so it's not visible
18
+ textArea.style.position = 'absolute'
19
+ textArea.style.left = '-999999px'
20
+
21
+ document.body.prepend(textArea)
22
+ textArea.select()
23
+
24
+ try {
25
+ document.execCommand('copy')
26
+ } catch (error) {
27
+ console.error(error)
28
+ } finally {
29
+ textArea.remove()
30
+ }
31
+ }
32
+ tooltip.open({ content: translate('✅ Copied!'), duration: 5000 })
33
+ }
34
+
35
+ export const copiableInput = (parent, label, value) => {
36
+ const [container, { input, button }] = Utils.loadTemplateWithRefs(`
37
+ <div class="copiable-input">
38
+ <label>${label}<input type="text" readOnly value="${value}" data-ref=input /></label>
39
+ <button type="button" class="icon icon-24 icon-copy" title="${translate('copy')}" data-ref=button></button>
40
+ </div>
41
+ `)
42
+ button.addEventListener('click', () => copyToClipboard(input.value))
43
+ parent.appendChild(container)
44
+ return input
45
+ }
46
+
47
+ // From https://gist.github.com/Accudio/b9cb16e0e3df858cef0d31e38f1fe46f
48
+ // convert colour in range 0-255 to the modifier used within luminance calculation
49
+ const colourMod = (colour) => {
50
+ const sRGB = colour / 255
51
+ let mod = ((sRGB + 0.055) / 1.055) ** 2.4
52
+ if (sRGB < 0.03928) mod = sRGB / 12.92
53
+ return mod
54
+ }
55
+ const RGBRegex = /rgb *\( *([0-9]{1,3}) *, *([0-9]{1,3}) *, *([0-9]{1,3}) *\)/
56
+
57
+ export const textColorFromBackgroundColor = (el, bgcolor) => {
58
+ return contrastedColor(el, bgcolor) ? '#ffffff' : '#000000'
59
+ }
60
+
61
+ const contrastWCAG21 = (rgb) => {
62
+ const [r, g, b] = rgb
63
+ // luminance of inputted colour
64
+ const lum = 0.2126 * colourMod(r) + 0.7152 * colourMod(g) + 0.0722 * colourMod(b)
65
+ // white has a luminance of 1
66
+ const whiteLum = 1
67
+ const contrast = (whiteLum + 0.05) / (lum + 0.05)
68
+ return contrast > 3 ? 1 : 0
69
+ }
70
+ const colorNameToHex = (str) => {
71
+ const ctx = document.createElement('canvas').getContext('2d')
72
+ ctx.fillStyle = str
73
+ return ctx.fillStyle
74
+ }
75
+ export const hexToRGB = (hex) => {
76
+ return hex
77
+ .replace(
78
+ /^#?([a-f\d])([a-f\d])([a-f\d])$/i,
79
+ (m, r, g, b) => `#${r}${r}${g}${g}${b}${b}`
80
+ )
81
+ .substring(1)
82
+ .match(/.{2}/g)
83
+ .map((x) => Number.parseInt(x, 16))
84
+ }
85
+
86
+ const CACHE_CONSTRAST = {}
87
+ export const contrastedColor = (el, bgcolor) => {
88
+ // Return 0 for black and 1 for white
89
+ // bgcolor is a human color, it can be a any keyword (purple…)
90
+ if (typeof CACHE_CONSTRAST[bgcolor] !== 'undefined') return CACHE_CONSTRAST[bgcolor]
91
+ let rgb = window.getComputedStyle(el).getPropertyValue('background-color')
92
+ rgb = RGBRegex.exec(rgb)
93
+ if (rgb && rgb.length === 4) {
94
+ rgb = [
95
+ Number.parseInt(rgb[1], 10),
96
+ Number.parseInt(rgb[2], 10),
97
+ Number.parseInt(rgb[3], 10),
98
+ ]
99
+ } else {
100
+ // The element may not yet be added to the DOM, so let's try
101
+ // another way
102
+ const hex = colorNameToHex(bgcolor)
103
+ rgb = hexToRGB(hex)
104
+ }
105
+ if (!rgb) return 1
106
+ const out = contrastWCAG21(rgb)
107
+ if (bgcolor) CACHE_CONSTRAST[bgcolor] = out
108
+ return out
109
+ }