umap-project 2.8.2__py3-none-any.whl → 2.9.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 (157) hide show
  1. umap/__init__.py +1 -1
  2. umap/asgi.py +12 -7
  3. umap/context_processors.py +1 -0
  4. umap/locale/en/LC_MESSAGES/django.po +102 -59
  5. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/fr/LC_MESSAGES/django.po +105 -61
  7. umap/management/commands/empty_trash.py +12 -1
  8. umap/migrations/0026_datalayer_modified_at_datalayer_share_status.py +26 -0
  9. umap/models.py +23 -3
  10. umap/settings/base.py +4 -1
  11. umap/static/umap/base.css +1 -1
  12. umap/static/umap/content.css +2 -22
  13. umap/static/umap/css/bar.css +7 -10
  14. umap/static/umap/css/form.css +28 -29
  15. umap/static/umap/css/icon.css +8 -2
  16. umap/static/umap/css/panel.css +2 -1
  17. umap/static/umap/css/tooltip.css +33 -31
  18. umap/static/umap/img/16-white.svg +2 -0
  19. umap/static/umap/img/16.svg +1 -1
  20. umap/static/umap/img/providers/bitbucket.png +0 -0
  21. umap/static/umap/img/providers/github.png +0 -0
  22. umap/static/umap/img/providers/keycloak.png +0 -0
  23. umap/static/umap/img/providers/openstreetmap-oauth2.png +0 -0
  24. umap/static/umap/img/providers/twitter-oauth2.png +0 -0
  25. umap/static/umap/img/source/16-white.svg +3 -1
  26. umap/static/umap/img/source/16.svg +1 -1
  27. umap/static/umap/js/components/alerts/alert.js +4 -1
  28. umap/static/umap/js/modules/browser.js +6 -6
  29. umap/static/umap/js/modules/caption.js +30 -7
  30. umap/static/umap/js/modules/data/features.js +21 -24
  31. umap/static/umap/js/modules/data/layer.js +71 -33
  32. umap/static/umap/js/modules/form/builder.js +241 -0
  33. umap/static/umap/js/modules/form/fields.js +1338 -0
  34. umap/static/umap/js/modules/formatter.js +5 -8
  35. umap/static/umap/js/modules/help.js +3 -1
  36. umap/static/umap/js/modules/importer.js +1 -1
  37. umap/static/umap/js/modules/permissions.js +5 -4
  38. umap/static/umap/js/modules/rendering/icon.js +5 -1
  39. umap/static/umap/js/modules/rendering/layers/classified.js +11 -7
  40. umap/static/umap/js/modules/rendering/layers/cluster.js +11 -1
  41. umap/static/umap/js/modules/rendering/map.js +0 -2
  42. umap/static/umap/js/modules/rules.js +2 -1
  43. umap/static/umap/js/modules/schema.js +5 -6
  44. umap/static/umap/js/modules/share.js +3 -3
  45. umap/static/umap/js/modules/sync/engine.js +18 -13
  46. umap/static/umap/js/modules/sync/updaters.js +8 -0
  47. umap/static/umap/js/modules/sync/websocket.js +10 -5
  48. umap/static/umap/js/modules/tableeditor.js +3 -2
  49. umap/static/umap/js/modules/ui/bar.js +17 -9
  50. umap/static/umap/js/modules/ui/base.js +7 -24
  51. umap/static/umap/js/modules/ui/tooltip.js +19 -11
  52. umap/static/umap/js/modules/umap.js +36 -24
  53. umap/static/umap/js/modules/utils.js +196 -12
  54. umap/static/umap/js/umap.controls.js +0 -12
  55. umap/static/umap/locale/br.js +21 -13
  56. umap/static/umap/locale/br.json +21 -13
  57. umap/static/umap/locale/ca.js +12 -4
  58. umap/static/umap/locale/ca.json +12 -4
  59. umap/static/umap/locale/cs_CZ.js +10 -4
  60. umap/static/umap/locale/cs_CZ.json +10 -4
  61. umap/static/umap/locale/de.js +12 -4
  62. umap/static/umap/locale/de.json +12 -4
  63. umap/static/umap/locale/el.js +12 -4
  64. umap/static/umap/locale/el.json +12 -4
  65. umap/static/umap/locale/en.js +9 -4
  66. umap/static/umap/locale/en.json +9 -4
  67. umap/static/umap/locale/es.js +20 -12
  68. umap/static/umap/locale/es.json +20 -12
  69. umap/static/umap/locale/eu.js +12 -4
  70. umap/static/umap/locale/eu.json +12 -4
  71. umap/static/umap/locale/fa_IR.js +12 -4
  72. umap/static/umap/locale/fa_IR.json +12 -4
  73. umap/static/umap/locale/fr.js +10 -5
  74. umap/static/umap/locale/fr.json +10 -5
  75. umap/static/umap/locale/gl.js +353 -345
  76. umap/static/umap/locale/gl.json +353 -345
  77. umap/static/umap/locale/hu.js +9 -4
  78. umap/static/umap/locale/hu.json +9 -4
  79. umap/static/umap/locale/it.js +100 -92
  80. umap/static/umap/locale/it.json +100 -92
  81. umap/static/umap/locale/ms.js +12 -4
  82. umap/static/umap/locale/ms.json +12 -4
  83. umap/static/umap/locale/nl.js +12 -4
  84. umap/static/umap/locale/nl.json +12 -4
  85. umap/static/umap/locale/pl.js +12 -4
  86. umap/static/umap/locale/pl.json +12 -4
  87. umap/static/umap/locale/pt.js +12 -4
  88. umap/static/umap/locale/pt.json +12 -4
  89. umap/static/umap/locale/pt_PT.js +12 -4
  90. umap/static/umap/locale/pt_PT.json +12 -4
  91. umap/static/umap/locale/th_TH.js +12 -4
  92. umap/static/umap/locale/th_TH.json +12 -4
  93. umap/static/umap/locale/zh_TW.js +10 -4
  94. umap/static/umap/locale/zh_TW.json +10 -4
  95. umap/static/umap/map.css +12 -8
  96. umap/static/umap/nav.css +2 -3
  97. umap/static/umap/unittests/utils.js +14 -0
  98. umap/static/umap/vars.css +2 -0
  99. umap/sync/__init__.py +0 -0
  100. umap/sync/app.py +181 -0
  101. umap/sync/payloads.py +49 -0
  102. umap/templates/auth/user_detail.html +4 -0
  103. umap/templates/auth/user_form.html +9 -6
  104. umap/templates/auth/user_stars.html +4 -0
  105. umap/templates/base.html +1 -1
  106. umap/templates/registration/login.html +2 -5
  107. umap/templates/umap/about.html +5 -0
  108. umap/templates/umap/about_summary.html +2 -2
  109. umap/templates/umap/components/provider.html +8 -0
  110. umap/templates/umap/js.html +0 -3
  111. umap/templates/umap/map_detail.html +1 -1
  112. umap/templates/umap/password_change.html +4 -0
  113. umap/templates/umap/password_change_done.html +4 -0
  114. umap/templates/umap/search.html +4 -0
  115. umap/templates/umap/team_confirm_delete.html +4 -0
  116. umap/templates/umap/team_detail.html +4 -0
  117. umap/templates/umap/team_form.html +4 -0
  118. umap/templates/umap/user_dashboard.html +1 -1
  119. umap/templates/umap/user_teams.html +4 -0
  120. umap/tests/base.py +3 -1
  121. umap/tests/integration/conftest.py +16 -23
  122. umap/tests/integration/test_basics.py +2 -2
  123. umap/tests/integration/test_caption.py +1 -0
  124. umap/tests/integration/test_draw_polygon.py +3 -3
  125. umap/tests/integration/test_edit_datalayer.py +1 -1
  126. umap/tests/integration/test_edit_map.py +3 -3
  127. umap/tests/integration/test_edit_polygon.py +1 -1
  128. umap/tests/integration/test_import.py +23 -1
  129. umap/tests/integration/test_optimistic_merge.py +1 -0
  130. umap/tests/integration/test_picto.py +8 -8
  131. umap/tests/integration/test_save.py +1 -0
  132. umap/tests/integration/test_star.py +13 -9
  133. umap/tests/integration/test_tableeditor.py +1 -0
  134. umap/tests/integration/test_websocket_sync.py +112 -33
  135. umap/tests/settings.py +2 -0
  136. umap/tests/test_datalayer.py +2 -3
  137. umap/tests/test_datalayer_views.py +20 -1
  138. umap/tests/test_empty_trash.py +10 -3
  139. umap/tests/test_map_views.py +11 -0
  140. umap/utils.py +24 -11
  141. umap/views.py +37 -6
  142. {umap_project-2.8.2.dist-info → umap_project-2.9.0b0.dist-info}/METADATA +15 -15
  143. {umap_project-2.8.2.dist-info → umap_project-2.9.0b0.dist-info}/RECORD +146 -145
  144. {umap_project-2.8.2.dist-info → umap_project-2.9.0b0.dist-info}/WHEEL +1 -1
  145. umap/management/commands/run_websocket_server.py +0 -23
  146. umap/settings/local_s3.py +0 -45
  147. umap/static/umap/bitbucket.png +0 -0
  148. umap/static/umap/github.png +0 -0
  149. umap/static/umap/js/umap.forms.js +0 -1242
  150. umap/static/umap/keycloak.png +0 -0
  151. umap/static/umap/openstreetmap.png +0 -0
  152. umap/static/umap/twitter.png +0 -0
  153. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +0 -468
  154. umap/tests/test_websocket_server.py +0 -22
  155. umap/websocket_server.py +0 -202
  156. {umap_project-2.8.2.dist-info → umap_project-2.9.0b0.dist-info}/entry_points.txt +0 -0
  157. {umap_project-2.8.2.dist-info → umap_project-2.9.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,3 @@
1
- // Uses U.FormBuilder not available as ESM
2
-
3
1
  // FIXME: this module should not depend on Leaflet
4
2
  import {
5
3
  DomUtil,
@@ -22,6 +20,7 @@ import { Point, LineString, Polygon } from './features.js'
22
20
  import TableEditor from '../tableeditor.js'
23
21
  import { ServerStored } from '../saving.js'
24
22
  import * as Schema from '../schema.js'
23
+ import { MutatingForm } from '../form/builder.js'
25
24
 
26
25
  export const LAYER_TYPES = [
27
26
  DefaultLayer,
@@ -250,6 +249,7 @@ export class DataLayer extends ServerStored {
250
249
  }
251
250
 
252
251
  fromGeoJSON(geojson, sync = true) {
252
+ if (!geojson) return []
253
253
  const features = this.addData(geojson, sync)
254
254
  this._geojson = geojson
255
255
  this.onDataLoaded()
@@ -283,7 +283,9 @@ export class DataLayer extends ServerStored {
283
283
  }
284
284
 
285
285
  backupData() {
286
- this._geojson_bk = Utils.CopyJSON(this._geojson)
286
+ if (this._geojson) {
287
+ this._geojson_bk = Utils.CopyJSON(this._geojson)
288
+ }
287
289
  }
288
290
 
289
291
  reindex() {
@@ -303,21 +305,43 @@ export class DataLayer extends ServerStored {
303
305
  return this.isRemoteLayer() && Boolean(this.options.remoteData?.dynamic)
304
306
  }
305
307
 
308
+ async getUrl(url, initialUrl) {
309
+ const response = await this._umap.request.get(url)
310
+ return new Promise((resolve) => {
311
+ if (response?.ok) return resolve(response.text())
312
+ Alert.error(
313
+ translate('Cannot load remote data for layer "{layer}" with url "{url}"', {
314
+ layer: this.getName(),
315
+ url: initialUrl || url,
316
+ })
317
+ )
318
+ })
319
+ }
320
+
306
321
  async fetchRemoteData(force) {
307
322
  if (!this.isRemoteLayer()) return
308
323
  if (!this.hasDynamicData() && this.hasDataLoaded() && !force) return
309
324
  if (!this.isVisible()) return
310
- let url = this._umap.renderUrl(this.options.remoteData.url)
325
+ // Keep non proxied url for later use in Alert.
326
+ const remoteUrl = this._umap.renderUrl(this.options.remoteData.url)
327
+ let url = remoteUrl
311
328
  if (this.options.remoteData.proxy) {
312
329
  url = this._umap.proxyUrl(url, this.options.remoteData.ttl)
313
330
  }
314
- const response = await this._umap.request.get(url)
315
- if (response?.ok) {
331
+ return await this.getUrl(url, remoteUrl).then((raw) => {
316
332
  this.clear()
317
333
  return this._umap.formatter
318
- .parse(await response.text(), this.options.remoteData.format)
334
+ .parse(raw, this.options.remoteData.format)
319
335
  .then((geojson) => this.fromGeoJSON(geojson))
320
- }
336
+ .catch((error) => {
337
+ Alert.error(
338
+ translate('Cannot parse remote data for layer "{layer}" with url "{url}"', {
339
+ layer: this.getName(),
340
+ url: remoteUrl,
341
+ })
342
+ )
343
+ })
344
+ })
321
345
  }
322
346
 
323
347
  isLoaded() {
@@ -444,14 +468,14 @@ export class DataLayer extends ServerStored {
444
468
  // otherwise the layer becomes uneditable.
445
469
  return this.makeFeatures(geojson, sync)
446
470
  } catch (err) {
447
- console.log('Error with DataLayer', this.id)
471
+ console.debug('Error with DataLayer', this.id)
448
472
  console.error(err)
449
473
  return []
450
474
  }
451
475
  }
452
476
 
453
477
  sortFeatures(collection) {
454
- const sortKeys = this._umap.getProperty('sortKey') || U.DEFAULT_LABEL_KEY
478
+ const sortKeys = this.getOption('sortKey') || U.DEFAULT_LABEL_KEY
455
479
  return Utils.sortFeatures(collection, sortKeys, U.lang)
456
480
  }
457
481
 
@@ -491,7 +515,7 @@ export class DataLayer extends ServerStored {
491
515
  feature = new Polygon(this._umap, this, geojson, id)
492
516
  break
493
517
  default:
494
- console.log(geojson)
518
+ console.debug(geojson)
495
519
  Alert.error(
496
520
  translate('Skipping unknown geometry.type: {type}', {
497
521
  type: geometry.type || 'undefined',
@@ -513,6 +537,9 @@ export class DataLayer extends ServerStored {
513
537
  if (data?.length) this.isDirty = true
514
538
  return data
515
539
  })
540
+ .catch((error) => {
541
+ Alert.error(translate('Import failed: invalid data'))
542
+ })
516
543
  }
517
544
 
518
545
  readFile(f) {
@@ -542,10 +569,9 @@ export class DataLayer extends ServerStored {
542
569
 
543
570
  async importFromUrl(uri, type) {
544
571
  uri = this._umap.renderUrl(uri)
545
- const response = await this._umap.request.get(uri)
546
- if (response?.ok) {
547
- return this.importRaw(await response.text(), type)
548
- }
572
+ return await this.getUrl(uri).then((raw) => {
573
+ return this.importRaw(raw, type)
574
+ })
549
575
  }
550
576
 
551
577
  getColor() {
@@ -574,9 +600,12 @@ export class DataLayer extends ServerStored {
574
600
  })
575
601
  }
576
602
 
577
- _delete() {
578
- this.isDeleted = true
603
+ del(sync = true) {
579
604
  this.erase()
605
+ if (sync) {
606
+ this.isDeleted = true
607
+ this.sync.delete()
608
+ }
580
609
  }
581
610
 
582
611
  empty() {
@@ -656,7 +685,7 @@ export class DataLayer extends ServerStored {
656
685
  {
657
686
  label: translate('Data is browsable'),
658
687
  handler: 'Switch',
659
- helpEntries: 'browsable',
688
+ helpEntries: ['browsable'],
660
689
  },
661
690
  ],
662
691
  [
@@ -668,20 +697,19 @@ export class DataLayer extends ServerStored {
668
697
  ],
669
698
  ]
670
699
  DomUtil.createTitle(container, translate('Layer properties'), 'icon-layers')
671
- let builder = new U.FormBuilder(this, metadataFields, {
672
- callback(e) {
673
- this._umap.onDataLayersChanged()
674
- if (e.helper.field === 'options.type') {
675
- this.edit()
676
- }
677
- },
700
+ let builder = new MutatingForm(this, metadataFields)
701
+ builder.on('set', ({ detail }) => {
702
+ this._umap.onDataLayersChanged()
703
+ if (detail.helper.field === 'options.type') {
704
+ this.edit()
705
+ }
678
706
  })
679
707
  container.appendChild(builder.build())
680
708
 
681
709
  const layerOptions = this.layer.getEditableOptions()
682
710
 
683
711
  if (layerOptions.length) {
684
- builder = new U.FormBuilder(this, layerOptions, {
712
+ builder = new MutatingForm(this, layerOptions, {
685
713
  id: 'datalayer-layer-properties',
686
714
  })
687
715
  const layerProperties = DomUtil.createFieldset(
@@ -704,7 +732,7 @@ export class DataLayer extends ServerStored {
704
732
  'options.fillOpacity',
705
733
  ]
706
734
 
707
- builder = new U.FormBuilder(this, shapeOptions, {
735
+ builder = new MutatingForm(this, shapeOptions, {
708
736
  id: 'datalayer-advanced-properties',
709
737
  })
710
738
  const shapeProperties = DomUtil.createFieldset(
@@ -719,11 +747,17 @@ export class DataLayer extends ServerStored {
719
747
  'options.zoomTo',
720
748
  'options.fromZoom',
721
749
  'options.toZoom',
750
+ 'options.sortKey',
722
751
  ]
723
752
 
724
- builder = new U.FormBuilder(this, optionsFields, {
753
+ builder = new MutatingForm(this, optionsFields, {
725
754
  id: 'datalayer-advanced-properties',
726
755
  })
756
+ builder.on('set', ({ detail }) => {
757
+ if (detail.helper.field === 'options.sortKey') {
758
+ this.reindex()
759
+ }
760
+ })
727
761
  const advancedProperties = DomUtil.createFieldset(
728
762
  container,
729
763
  translate('Advanced properties')
@@ -740,7 +774,7 @@ export class DataLayer extends ServerStored {
740
774
  'options.outlinkTarget',
741
775
  'options.interactive',
742
776
  ]
743
- builder = new U.FormBuilder(this, popupFields)
777
+ builder = new MutatingForm(this, popupFields)
744
778
  const popupFieldset = DomUtil.createFieldset(
745
779
  container,
746
780
  translate('Interaction options')
@@ -796,7 +830,7 @@ export class DataLayer extends ServerStored {
796
830
  container,
797
831
  translate('Remote data')
798
832
  )
799
- builder = new U.FormBuilder(this, remoteDataFields)
833
+ builder = new MutatingForm(this, remoteDataFields)
800
834
  remoteDataContainer.appendChild(builder.build())
801
835
  DomUtil.createButton(
802
836
  'button umap-verify',
@@ -819,7 +853,7 @@ export class DataLayer extends ServerStored {
819
853
  <i class="icon icon-24 icon-delete"></i>${translate('Delete')}
820
854
  </button>`)
821
855
  deleteButton.addEventListener('click', () => {
822
- this._delete()
856
+ this.del()
823
857
  this._umap.editPanel.close()
824
858
  })
825
859
  advancedButtons.appendChild(deleteButton)
@@ -1147,10 +1181,14 @@ export class DataLayer extends ServerStored {
1147
1181
  if (this.createdOnServer) {
1148
1182
  await this._umap.server.post(this.getDeleteUrl())
1149
1183
  }
1150
- delete this._umap.datalayers[stamp(this)]
1184
+ this.commitDelete()
1151
1185
  return true
1152
1186
  }
1153
1187
 
1188
+ commitDelete() {
1189
+ delete this._umap.datalayers[stamp(this)]
1190
+ }
1191
+
1154
1192
  getName() {
1155
1193
  return this.options.name || translate('Untitled layer')
1156
1194
  }
@@ -1221,7 +1259,7 @@ export class DataLayer extends ServerStored {
1221
1259
  this._umap.dialog
1222
1260
  .confirm(translate('Are you sure you want to delete this layer?'))
1223
1261
  .then(() => {
1224
- this._delete()
1262
+ this.del()
1225
1263
  })
1226
1264
  },
1227
1265
  this
@@ -0,0 +1,241 @@
1
+ import getClass from './fields.js'
2
+ import * as Utils from '../utils.js'
3
+ import { SCHEMA } from '../schema.js'
4
+ import { translate } from '../i18n.js'
5
+
6
+ export class Form extends Utils.WithEvents {
7
+ constructor(obj, fields, properties) {
8
+ super()
9
+ this.setProperties(properties)
10
+ this.defaultProperties = {}
11
+ this.obj = obj
12
+ this.form = Utils.loadTemplate('<form></form>')
13
+ this.setFields(fields)
14
+ if (this.properties.id) {
15
+ this.form.id = this.properties.id
16
+ }
17
+ if (this.properties.className) {
18
+ this.form.classList.add(...this.properties.className.split(' '))
19
+ }
20
+ }
21
+
22
+ setProperties(properties) {
23
+ this.properties = Object.assign({}, this.properties, properties)
24
+ }
25
+
26
+ setFields(fields) {
27
+ this.fields = fields || []
28
+ this.helpers = {}
29
+ }
30
+
31
+ build() {
32
+ this.form.innerHTML = ''
33
+ for (const definition of this.fields) {
34
+ this.buildField(this.makeField(definition))
35
+ }
36
+ return this.form
37
+ }
38
+
39
+ buildField(field) {
40
+ field.buildTemplate()
41
+ field.build()
42
+ }
43
+
44
+ makeField(field) {
45
+ // field can be either a string like "option.name" or a full definition array,
46
+ // like ['properties.tilelayer.tms', {handler: 'CheckBox', helpText: 'TMS format'}]
47
+ let properties
48
+ if (Array.isArray(field)) {
49
+ properties = field[1] || {}
50
+ field = field[0]
51
+ } else {
52
+ properties = this.defaultProperties[this.getName(field)] || {}
53
+ }
54
+ const class_ = getClass(properties.handler || 'Input')
55
+ this.helpers[field] = new class_(this, field, properties)
56
+ return this.helpers[field]
57
+ }
58
+
59
+ getter(field) {
60
+ const path = field.split('.')
61
+ let value = this.obj
62
+ for (const sub of path) {
63
+ try {
64
+ value = value[sub]
65
+ } catch {
66
+ console.log(field)
67
+ }
68
+ }
69
+ return value
70
+ }
71
+
72
+ setter(field, value) {
73
+ const path = field.split('.')
74
+ let obj = this.obj
75
+ let what
76
+ for (let i = 0, l = path.length; i < l; i++) {
77
+ what = path[i]
78
+ if (what === path[l - 1]) {
79
+ if (typeof value === 'undefined') {
80
+ delete obj[what]
81
+ } else {
82
+ obj[what] = value
83
+ }
84
+ } else {
85
+ obj = obj[what]
86
+ }
87
+ }
88
+ }
89
+
90
+ restoreField(field) {
91
+ const initial = this.helpers[field].initial
92
+ this.setter(field, initial)
93
+ }
94
+
95
+ getName(field) {
96
+ const fieldEls = field.split('.')
97
+ return fieldEls[fieldEls.length - 1]
98
+ }
99
+
100
+ fetchAll() {
101
+ for (const helper of Object.values(this.helpers)) {
102
+ helper.fetch()
103
+ }
104
+ }
105
+
106
+ syncAll() {
107
+ for (const helper of Object.values(this.helpers)) {
108
+ helper.sync()
109
+ }
110
+ }
111
+
112
+ onPostSync(helper) {
113
+ if (this.properties.callback) {
114
+ this.properties.callback(helper)
115
+ }
116
+ }
117
+
118
+ finish() {}
119
+
120
+ getTemplate(helper) {
121
+ return `
122
+ <div class="formbox" data-ref=container>
123
+ ${helper.getTemplate()}
124
+ <small class="help-text" data-ref=helpText></small>
125
+ </div>`
126
+ }
127
+ }
128
+
129
+ export class MutatingForm extends Form {
130
+ constructor(obj, fields, properties) {
131
+ super(obj, fields, properties)
132
+ this._umap = obj._umap || properties.umap
133
+ this.computeDefaultProperties()
134
+ // this.on('finish', this.finish)
135
+ }
136
+
137
+ computeDefaultProperties() {
138
+ const customHandlers = {
139
+ sortKey: 'PropertyInput',
140
+ easing: 'Switch',
141
+ facetKey: 'PropertyInput',
142
+ slugKey: 'PropertyInput',
143
+ labelKey: 'PropertyInput',
144
+ }
145
+ for (const [key, schema] of Object.entries(SCHEMA)) {
146
+ if (schema.type === Boolean) {
147
+ if (schema.nullable) schema.handler = 'NullableChoices'
148
+ else schema.handler = 'Switch'
149
+ } else if (schema.type === 'Text') {
150
+ schema.handler = 'Textarea'
151
+ } else if (schema.type === Number) {
152
+ if (schema.step) schema.handler = 'Range'
153
+ else schema.handler = 'IntInput'
154
+ } else if (schema.choices) {
155
+ const text_length = schema.choices.reduce(
156
+ (acc, [_, label]) => acc + label.length,
157
+ 0
158
+ )
159
+ // Try to be smart and use MultiChoice only
160
+ // for choices where labels are shorts…
161
+ if (text_length < 40) {
162
+ schema.handler = 'MultiChoice'
163
+ } else {
164
+ schema.handler = 'Select'
165
+ schema.selectOptions = schema.choices
166
+ }
167
+ } else {
168
+ switch (key) {
169
+ case 'color':
170
+ case 'fillColor':
171
+ schema.handler = 'ColorPicker'
172
+ break
173
+ case 'iconUrl':
174
+ schema.handler = 'IconUrl'
175
+ break
176
+ case 'licence':
177
+ schema.handler = 'LicenceChooser'
178
+ break
179
+ }
180
+ }
181
+
182
+ if (customHandlers[key]) {
183
+ schema.handler = customHandlers[key]
184
+ }
185
+ // Input uses this key for its type attribute
186
+ delete schema.type
187
+ this.defaultProperties[key] = schema
188
+ }
189
+ }
190
+
191
+ setter(field, value) {
192
+ super.setter(field, value)
193
+ this.obj.isDirty = true
194
+ if ('render' in this.obj) {
195
+ this.obj.render([field], this)
196
+ }
197
+ if ('sync' in this.obj) {
198
+ this.obj.sync.update(field, value)
199
+ }
200
+ }
201
+
202
+ getTemplate(helper) {
203
+ let template
204
+ if (helper.properties.inheritable) {
205
+ const extraClassName = helper.get(true) === undefined ? ' undefined' : ''
206
+ template = `
207
+ <div class="umap-field-${helper.name} formbox inheritable${extraClassName}">
208
+ <div class="header" data-ref=header>
209
+ ${helper.getLabelTemplate()}
210
+ <span class="actions show-on-defined" data-ref=actions></span>
211
+ <span class="buttons" data-ref=buttons>
212
+ <button type="button" class="button undefine" data-ref=undefine>${translate('clear')}</button>
213
+ <button type="button" class="button define" data-ref=define>${translate('define')}</button>
214
+ </span>
215
+ </div>
216
+ <div class="show-on-defined" data-ref=container>
217
+ ${helper.getTemplate()}
218
+ <small class="help-text" data-ref=helpText></small>
219
+ </div>
220
+ </div>`
221
+ } else {
222
+ template = `
223
+ <div class="formbox umap-field-${helper.name}" data-ref=container>
224
+ ${helper.getLabelTemplate()}
225
+ ${helper.getTemplate()}
226
+ <small class="help-text" data-ref=helpText></small>
227
+ </div>`
228
+ }
229
+ return template
230
+ }
231
+
232
+ build() {
233
+ super.build()
234
+ this._umap.help.parse(this.form)
235
+ return this.form
236
+ }
237
+
238
+ finish(helper) {
239
+ helper.input?.blur()
240
+ }
241
+ }