@lblod/ember-rdfa-editor-lblod-plugins 20.0.0 → 21.0.0

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 (50) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/addon/components/location-plugin/edit.gts +5 -2
  3. package/addon/components/location-plugin/insert.gts +99 -50
  4. package/addon/components/location-plugin/map.gts +246 -68
  5. package/addon/components/snippet-plugin/nodes/snippet.gts +194 -0
  6. package/addon/components/snippet-plugin/search-modal.ts +2 -0
  7. package/addon/components/snippet-plugin/snippet-insert.ts +15 -6
  8. package/addon/components/snippet-plugin/snippets/snippet-preview.ts +5 -2
  9. package/addon/components/structure-plugin/_private/structure.gts +23 -1
  10. package/addon/models/sign.ts +2 -2
  11. package/addon/plugins/decision-plugin/utils/build-article-structure.ts +8 -3
  12. package/addon/plugins/location-plugin/node-contents/address.ts +6 -5
  13. package/addon/plugins/location-plugin/node-contents/area.ts +60 -0
  14. package/addon/plugins/location-plugin/node-contents/index.ts +13 -7
  15. package/addon/plugins/location-plugin/node-contents/place.ts +8 -5
  16. package/addon/plugins/location-plugin/node-contents/point.ts +29 -15
  17. package/addon/plugins/location-plugin/node.ts +10 -6
  18. package/addon/plugins/location-plugin/utils/address-helpers.ts +2 -2
  19. package/addon/plugins/location-plugin/utils/geo-helpers.ts +94 -51
  20. package/addon/plugins/snippet-plugin/nodes/snippet.ts +86 -0
  21. package/addon/plugins/standard-template-plugin/index.ts +0 -27
  22. package/addon/plugins/structure-plugin/node.ts +172 -47
  23. package/addon/services/roadsign-registry.ts +3 -1
  24. package/addon/utils/translation.ts +10 -0
  25. package/app/components/snippet-plugin/nodes/snippet.js +1 -0
  26. package/app/styles/snippet-plugin.scss +56 -0
  27. package/declarations/addon/components/location-plugin/insert.d.ts +2 -0
  28. package/declarations/addon/components/location-plugin/map.d.ts +10 -6
  29. package/declarations/addon/components/snippet-plugin/nodes/snippet.d.ts +25 -0
  30. package/declarations/addon/components/snippet-plugin/search-modal.d.ts +2 -0
  31. package/declarations/addon/components/snippet-plugin/snippet-insert.d.ts +1 -1
  32. package/declarations/addon/components/snippet-plugin/snippets/snippet-preview.d.ts +1 -1
  33. package/declarations/addon/components/structure-plugin/_private/structure.d.ts +6 -1
  34. package/declarations/addon/models/sign.d.ts +2 -2
  35. package/declarations/addon/plugins/location-plugin/node-contents/area.d.ts +4 -0
  36. package/declarations/addon/plugins/location-plugin/node-contents/index.d.ts +9 -5
  37. package/declarations/addon/plugins/location-plugin/node-contents/point.d.ts +3 -3
  38. package/declarations/addon/plugins/location-plugin/utils/geo-helpers.d.ts +25 -13
  39. package/declarations/addon/plugins/snippet-plugin/nodes/snippet.d.ts +3 -0
  40. package/declarations/addon/plugins/standard-template-plugin/index.d.ts +0 -12
  41. package/declarations/addon/plugins/structure-plugin/node.d.ts +3 -0
  42. package/declarations/addon/utils/translation.d.ts +5 -1
  43. package/package.json +3 -1
  44. package/pnpm-lock.yaml +25 -0
  45. package/translations/en-US.yaml +16 -0
  46. package/translations/nl-BE.yaml +17 -0
  47. package/types/ember-leaflet.d.ts +72 -23
  48. package/types/tracked-toolbox/index.d.ts +2 -1
  49. package/addon/plugins/standard-template-plugin/utils/nodes.ts +0 -485
  50. package/declarations/addon/plugins/standard-template-plugin/utils/nodes.d.ts +0 -13
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # @lblod/ember-rdfa-editor-lblod-plugins
2
2
 
3
+ ## 21.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#449](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/449) [`335cb673df926d26a0d421a958c414d334653575`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/335cb673df926d26a0d421a958c414d334653575) Thanks [@elpoelma](https://github.com/elpoelma)! - Drop obsolete decision nodes from `standard-template` plugin
8
+
9
+ ### Minor Changes
10
+
11
+ - [#443](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/443) [`9f53a0c5a0e0db0414b0c84aed978d01752ccbf8`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/9f53a0c5a0e0db0414b0c84aed978d01752ccbf8) Thanks [@piemonkey](https://github.com/piemonkey)! - Add ability to specify area locations in the location-plugin
12
+
13
+ - [#445](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/445) [`06fe546cea3d3e7076f3a0ab4549c8de389a43d7`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/06fe546cea3d3e7076f3a0ab4549c8de389a43d7) Thanks [@lagartoverde](https://github.com/lagartoverde)! - Support for repeatable fragments
14
+
15
+ Addition of a custom, interactive `fragment` node and nodeview.
16
+ Using the fragment node interactive buttons, you can replace, add and remove fragments.
17
+
18
+ - [#444](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/444) [`3eea339d0d4bd5bf746d6c8cd9c99cf95e127825`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/3eea339d0d4bd5bf746d6c8cd9c99cf95e127825) Thanks [@elpoelma](https://github.com/elpoelma)! - Addition of a backwards-compatible parsing-rule for decision articles to `structure` node-spec
19
+
20
+ - [#446](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/446) [`f9b4a65a743b214d4f5fa2ba3936fc9fca9fa4c2`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/f9b4a65a743b214d4f5fa2ba3936fc9fca9fa4c2) Thanks [@elpoelma](https://github.com/elpoelma)! - Introduce some modifications to new `structure` node-spec:
21
+ - Drop `structureName` node-attribute. This attribute has been replaced by both the `structureType` and `displayStructureName` attributes.
22
+ - Introduction of a required `structureType` attribute. Examples of `structureType` values are:
23
+ - `article`
24
+ - `title`
25
+ - `chapter`
26
+ - `section`
27
+ - `subsection`
28
+ - `paragraph`
29
+ - Introduction of a `displayStructureName` attribute. This attribute controls whether the internationalizated (based on the document language) version of the structure name is displayed inside the header of the structure. The internationalized structure name is based on the `structureType` value and the entries included in the translation files. `displayStructureName` has a default value of `false`.
30
+
31
+ ### Patch Changes
32
+
33
+ - [#447](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/447) [`f06edfdf1681fc2c52e46462cad8faa5d97215c4`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/f06edfdf1681fc2c52e46462cad8faa5d97215c4) Thanks [@elpoelma](https://github.com/elpoelma)! - Ensure `zonality` is set up as optional when fetching traffic signs
34
+
3
35
  ## 20.0.0
4
36
 
5
37
  ### Major Changes
@@ -349,10 +349,13 @@ export default class LocationPluginEditComponent extends Component<Signature> {
349
349
  <Group.Radio @value='place'>
350
350
  {{t 'location-plugin.types.place'}}
351
351
  </Group.Radio>
352
+ <Group.Radio @value='area'>
353
+ {{t 'location-plugin.types.area'}}
354
+ </Group.Radio>
352
355
  </AuRadioGroup>
353
356
  </fs.content>
354
357
  </AuFieldset>
355
- {{#if (eq @locationType 'place')}}
358
+ {{#unless (eq @locationType 'address')}}
356
359
  <AuFormRow>
357
360
  <AuLabel for='place-name'>
358
361
  {{t 'location-plugin.search.place-name.label'}}*
@@ -371,7 +374,7 @@ export default class LocationPluginEditComponent extends Component<Signature> {
371
374
  <p class='au-u-para-tiny au-u-margin-none'>
372
375
  {{t 'location-plugin.search.hint'}}
373
376
  </p>
374
- {{/if}}
377
+ {{/unless}}
375
378
  <AuFormRow>
376
379
  <AuLabel for='municipality-select'>
377
380
  {{t 'location-plugin.search.municipality.label'}}
@@ -16,11 +16,13 @@ import { ResolvedPNode } from '@lblod/ember-rdfa-editor/utils/_private/types';
16
16
 
17
17
  import { Address } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/location-plugin/utils/address-helpers';
18
18
  import {
19
+ Area,
19
20
  convertWGS84CoordsToLambert,
20
21
  GeoPos,
21
22
  type GlobalCoordinates,
22
23
  Place,
23
24
  Point,
25
+ Polygon,
24
26
  } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/location-plugin/utils/geo-helpers';
25
27
  import { replaceSelectionWithAddress } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/location-plugin/utils/node-utils';
26
28
  import { type LocationPluginConfig } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/location-plugin/node';
@@ -30,6 +32,23 @@ import LocationMap, { type LocationType } from './map';
30
32
 
31
33
  export type CurrentLocation = Address | GlobalCoordinates | undefined;
32
34
 
35
+ function updateFromNode<T extends Address | Place | Area, R>(
36
+ Class: new (...args: unknown[]) => T,
37
+ extractFunc: (current: T) => R,
38
+ defaultValue?: R,
39
+ ) {
40
+ return (component: LocationPluginInsertComponent): R | undefined => {
41
+ const curLocation = component.selectedLocationNode?.value.attrs.value as
42
+ | Address
43
+ | Area
44
+ | Place
45
+ | null;
46
+ return curLocation instanceof Class
47
+ ? extractFunc(curLocation)
48
+ : defaultValue;
49
+ };
50
+ }
51
+
33
52
  interface Signature {
34
53
  Args: {
35
54
  controller: SayController;
@@ -46,43 +65,41 @@ export default class LocationPluginInsertComponent extends Component<Signature>
46
65
  @tracked isLoading = false;
47
66
 
48
67
  @trackedReset({
49
- memo: 'selectedLocationNode',
50
- update(component: LocationPluginInsertComponent) {
51
- const currentLocation = component.selectedLocationNode?.value.attrs
52
- .value as Address | Place | null;
53
- return currentLocation instanceof Address ? currentLocation : undefined;
54
- },
68
+ memo: 'modalOpen',
69
+ update: updateFromNode(Address, (address) => address),
55
70
  })
56
71
  addressToInsert?: Address;
57
72
 
58
73
  @trackedReset({
59
- memo: 'selectedLocationNode',
60
- update(component: LocationPluginInsertComponent) {
61
- const currentLocation = component.selectedLocationNode?.value.attrs
62
- .value as Address | Place | null;
63
- return currentLocation instanceof Place
64
- ? currentLocation.location.location
65
- : undefined;
66
- },
74
+ memo: 'modalOpen',
75
+ update: updateFromNode(Place, (place) => place.location.location),
67
76
  })
68
77
  savedLocation: GeoPos | undefined;
69
78
 
70
79
  @trackedReset({
71
- memo: 'selectedLocationNode',
80
+ memo: 'modalOpen',
72
81
  update(component: LocationPluginInsertComponent) {
73
- const currentLocation = component.selectedLocationNode?.value.attrs
74
- .value as Address | Place | null;
75
- return currentLocation instanceof Place ? currentLocation.name : '';
82
+ return (
83
+ updateFromNode(Place, (place) => place.name)(component) ??
84
+ updateFromNode(Area, (area) => area.name, '')(component)
85
+ );
76
86
  },
77
87
  })
78
88
  placeName: string = '';
79
89
 
80
90
  @trackedReset({
81
- memo: 'selectedLocationNode',
91
+ memo: 'modalOpen',
92
+ update: updateFromNode(Area, (area) => area.shape.locations),
93
+ })
94
+ savedArea: GeoPos[] | undefined;
95
+
96
+ @trackedReset({
97
+ memo: 'modalOpen',
82
98
  update(component: LocationPluginInsertComponent) {
83
- const currentLocation = component.selectedLocationNode?.value.attrs
84
- .value as Address | Place | null;
85
- return currentLocation instanceof Place ? 'place' : 'address';
99
+ return (
100
+ updateFromNode(Place, () => 'place')(component) ??
101
+ updateFromNode(Area, () => 'area', 'address')(component)
102
+ );
86
103
  },
87
104
  })
88
105
  locationType: LocationType = 'address';
@@ -121,14 +138,27 @@ export default class LocationPluginInsertComponent extends Component<Signature>
121
138
  }
122
139
 
123
140
  get canInsert() {
141
+ // trackedReset only actually resets any values if the values are used somewhere. Since we're
142
+ // not actually rendering anything inside the modal if this.modalOpen is false, we need to use
143
+ // these values somewhere outside of the modal contents block.
144
+ this.addressToInsert;
145
+ this.placeName;
146
+ this.savedLocation;
147
+ this.savedArea;
148
+ this.locationType;
124
149
  return !!this.controller.activeEditorView.props.nodeViews?.oslo_location;
125
150
  }
126
151
 
127
152
  get disableConfirm() {
128
- if (this.locationType === 'place') {
129
- return !this.selectedLocationNode || !this.placeName || !this.chosenPoint;
130
- } else {
131
- return !this.selectedLocationNode || !this.addressToInsert;
153
+ switch (this.locationType) {
154
+ case 'place':
155
+ return (
156
+ !this.selectedLocationNode || !this.placeName || !this.chosenPoint
157
+ );
158
+ case 'address':
159
+ return !this.selectedLocationNode || !this.addressToInsert;
160
+ default:
161
+ return !this.selectedLocationNode || !this.placeName || !this.savedArea;
132
162
  }
133
163
  }
134
164
 
@@ -139,9 +169,8 @@ export default class LocationPluginInsertComponent extends Component<Signature>
139
169
 
140
170
  @action
141
171
  setChosenPoint(point: GlobalCoordinates) {
142
- // Since we only use the global coordinates, we don't have to do a conversion to lambert here
143
172
  this.chosenPoint = {
144
- lambert: { x: 0, y: 0 },
173
+ lambert: convertWGS84CoordsToLambert(point),
145
174
  global: point,
146
175
  };
147
176
  }
@@ -153,6 +182,7 @@ export default class LocationPluginInsertComponent extends Component<Signature>
153
182
 
154
183
  @action
155
184
  closeModal() {
185
+ this.chosenPoint = undefined;
156
186
  this.modalOpen = false;
157
187
  }
158
188
 
@@ -179,41 +209,58 @@ export default class LocationPluginInsertComponent extends Component<Signature>
179
209
  this.addressToInsert = address;
180
210
  }
181
211
 
212
+ @action
213
+ setArea(vertices: GlobalCoordinates[]) {
214
+ this.savedArea = vertices.map((vertex) => ({
215
+ global: vertex,
216
+ lambert: convertWGS84CoordsToLambert(vertex),
217
+ }));
218
+ }
219
+
182
220
  @action
183
221
  confirmLocation() {
184
222
  if (this.selectedLocationNode) {
185
- let toInsert: Address | Place | undefined;
223
+ let toInsert: Address | Place | Area | undefined;
186
224
  const { pos } = this.selectedLocationNode;
187
225
  if (this.locationType === 'address' && this.addressToInsert) {
188
226
  toInsert = this.addressToInsert;
189
- this.controller.withTransaction((tr) => {
190
- return tr.setNodeAttribute(pos, 'value', toInsert);
191
- });
192
- this.modalOpen = false;
193
227
  } else if (
194
228
  this.locationType === 'place' &&
195
229
  this.chosenPoint?.global &&
196
230
  this.placeName
197
231
  ) {
198
- convertWGS84CoordsToLambert(this.chosenPoint.global).then((lambert) => {
199
- toInsert = new Place({
200
- uri: this.nodeContentsUtils.fallbackPlaceUri(),
201
- name: this.placeName,
202
- location: new Point({
203
- uri: this.nodeContentsUtils.fallbackPointUri(),
204
- location: {
205
- lambert,
206
- global: this.chosenPoint?.global,
207
- },
208
- }),
209
- });
210
- this.controller.withTransaction((tr) => {
211
- return tr.setNodeAttribute(pos, 'value', toInsert);
212
- });
213
- this.modalOpen = false;
214
- this.chosenPoint = undefined;
232
+ toInsert = new Place({
233
+ uri: this.nodeContentsUtils.fallbackPlaceUri(),
234
+ name: this.placeName,
235
+ location: new Point({
236
+ uri: this.nodeContentsUtils.fallbackGeometryUri(),
237
+ location: {
238
+ lambert: convertWGS84CoordsToLambert(this.chosenPoint.global),
239
+ global: this.chosenPoint?.global,
240
+ },
241
+ }),
242
+ });
243
+ this.chosenPoint = undefined;
244
+ } else if (
245
+ this.locationType === 'area' &&
246
+ this.savedArea &&
247
+ this.placeName
248
+ ) {
249
+ toInsert = new Area({
250
+ uri: this.nodeContentsUtils.fallbackPlaceUri(),
251
+ name: this.placeName,
252
+ shape: new Polygon({
253
+ uri: this.nodeContentsUtils.fallbackGeometryUri(),
254
+ locations: this.savedArea,
255
+ }),
215
256
  });
216
257
  }
258
+ if (toInsert) {
259
+ this.controller.withTransaction((tr) => {
260
+ return tr.setNodeAttribute(pos, 'value', toInsert);
261
+ });
262
+ this.modalOpen = false;
263
+ }
217
264
  }
218
265
  }
219
266
 
@@ -263,6 +310,8 @@ export default class LocationPluginInsertComponent extends Component<Signature>
263
310
  @location={{this.chosenPoint}}
264
311
  @existingLocation={{this.savedLocation}}
265
312
  @setLocation={{this.setChosenPoint}}
313
+ @existingArea={{this.savedArea}}
314
+ @setArea={{this.setArea}}
266
315
  />
267
316
  </div>
268
317
  </modal.Body>
@@ -1,14 +1,18 @@
1
1
  import Component from '@glimmer/component';
2
- import { action } from '@ember/object';
3
2
  import { tracked } from '@glimmer/tracking';
3
+ import { action } from '@ember/object';
4
+ import { fn } from '@ember/helper';
4
5
  import and from 'ember-truth-helpers/helpers/and';
5
6
  import eq from 'ember-truth-helpers/helpers/eq';
6
7
  import t from 'ember-intl/helpers/t';
7
- import { LeafletMap } from 'ember-leaflet';
8
+ import {
9
+ LeafletMap,
10
+ type LeafletMapSig,
11
+ type LeafletMapStart,
12
+ } from 'ember-leaflet';
13
+ import { type LatLngBoundsExpression } from 'leaflet';
8
14
  import { type LeafletMouseEvent } from 'leaflet';
9
- import { restartableTask } from 'ember-concurrency';
10
- import { task as trackedTask } from 'ember-resources/util/ember-concurrency';
11
- import AuLoader from '@appuniversum/ember-appuniversum/components/au-loader';
15
+ import { trackedReset } from 'tracked-toolbox';
12
16
  import { Address } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/location-plugin/utils/address-helpers';
13
17
  import {
14
18
  convertLambertCoordsToWGS84,
@@ -16,13 +20,23 @@ import {
16
20
  type GlobalCoordinates,
17
21
  } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/location-plugin/utils/geo-helpers';
18
22
 
19
- export type LocationType = 'address' | 'place';
23
+ export type LocationType = 'address' | 'place' | 'area';
20
24
 
25
+ const MAP_TILE_ATTRIBUTION =
26
+ '&copy; <a target="_blank" href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors';
27
+ const MAP_TILE_URL = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
21
28
  const COORD_SYSTEM_CENTER: GlobalCoordinates = {
22
29
  lng: 4.450822554431,
23
30
  lat: 50.50526730529,
24
31
  };
32
+ const COORD_SYSTEM_START = {
33
+ center: COORD_SYSTEM_CENTER,
34
+ zoom: 7,
35
+ };
25
36
 
37
+ function isLast(array: unknown[], index: number) {
38
+ return array.length === index + 1;
39
+ }
26
40
  function displayLocation(location: GeoPos | undefined) {
27
41
  const { lambert } = location ?? {};
28
42
  return lambert
@@ -37,95 +51,259 @@ interface Signature {
37
51
  location?: GeoPos;
38
52
  setLocation: (loc: GlobalCoordinates) => void;
39
53
  existingLocation?: GeoPos;
54
+ existingArea?: GeoPos[];
55
+ setArea: (vertices: GlobalCoordinates[]) => void;
40
56
  };
41
57
  Element: HTMLElement;
42
58
  }
43
59
 
44
- async function ensureGlobalCoordinates(geoPos: GeoPos) {
45
- if (geoPos.global) {
46
- return geoPos.global;
60
+ function ensureGlobalCoordinates(geoPos: GeoPos) {
61
+ return geoPos.global || convertLambertCoordsToWGS84(geoPos.lambert);
62
+ }
63
+ function areaLocations(
64
+ locationType: LocationType,
65
+ positions: GeoPos[] | undefined,
66
+ ) {
67
+ return locationType === 'area' && positions?.length !== 0
68
+ ? positions?.map((pos) => ensureGlobalCoordinates(pos))
69
+ : undefined;
70
+ }
71
+
72
+ /** Find the southWest and northEast corners bounding an arbitrary set of points */
73
+ function generateBoundsFromShape(
74
+ areaForBounds: GeoPos[] | undefined,
75
+ ): LatLngBoundsExpression | undefined {
76
+ if (areaForBounds && areaForBounds.length !== 0) {
77
+ const southWest = { ...ensureGlobalCoordinates(areaForBounds[0]) };
78
+ const northEast = { ...ensureGlobalCoordinates(areaForBounds[0]) };
79
+ areaForBounds.forEach((pos) => {
80
+ const vertex = ensureGlobalCoordinates(pos);
81
+ if (vertex.lat < southWest.lat) {
82
+ southWest.lat = vertex.lat;
83
+ } else if (vertex.lat > northEast.lat) {
84
+ northEast.lat = vertex.lat;
85
+ }
86
+ if (vertex.lng < southWest.lng) {
87
+ southWest.lng = vertex.lng;
88
+ } else if (vertex.lng > northEast.lng) {
89
+ northEast.lng = vertex.lng;
90
+ }
91
+ });
92
+ return [
93
+ [southWest.lat, southWest.lng],
94
+ [northEast.lat, northEast.lng],
95
+ ];
96
+ }
97
+ return undefined;
98
+ }
99
+
100
+ type MapWrapperSig = {
101
+ Args: Omit<
102
+ LeafletMapSig['Args'],
103
+ 'bounds' | 'center' | 'zoom' | 'lat' | 'lng'
104
+ > & {
105
+ mapStart: LeafletMapStart;
106
+ };
107
+ Blocks: LeafletMapSig['Blocks'];
108
+ };
109
+
110
+ /**
111
+ * LeafletMap doesn't handle undefined being passed for the bounds argument, so we use a wrapper
112
+ * component to make sure only to pass the relevant args
113
+ */
114
+ class MapWrapper extends Component<MapWrapperSig> {
115
+ get unwrappedBounds() {
116
+ return 'bounds' in this.args.mapStart ? this.args.mapStart.bounds : false;
117
+ }
118
+ get isOrigin() {
119
+ return (
120
+ 'center' in this.args.mapStart &&
121
+ this.args.mapStart.center === COORD_SYSTEM_CENTER
122
+ );
123
+ }
124
+ get unwrappedCenter() {
125
+ return 'center' in this.args.mapStart
126
+ ? {
127
+ center: this.args.mapStart.center,
128
+ zoom: this.args.mapStart.zoom,
129
+ }
130
+ : false;
131
+ }
132
+ get unwrappedLatLng() {
133
+ return 'lat' in this.args.mapStart
134
+ ? {
135
+ lat: this.args.mapStart.lat,
136
+ lng: this.args.mapStart.lng,
137
+ zoom: this.args.mapStart.zoom,
138
+ }
139
+ : false;
47
140
  }
48
- return await convertLambertCoordsToWGS84(geoPos.lambert);
141
+ <template>
142
+ {{#if this.unwrappedBounds}}
143
+ <LeafletMap
144
+ @bounds={{this.unwrappedBounds}}
145
+ @onClick={{@onClick}}
146
+ as |layers|
147
+ >
148
+ {{yield layers}}
149
+ </LeafletMap>
150
+ {{else if this.isOrigin}}
151
+ {{! If we're at the default center, use hard-coded values to avoid map jumps }}
152
+ <LeafletMap
153
+ @center={{COORD_SYSTEM_CENTER}}
154
+ @zoom={{7}}
155
+ @onClick={{@onClick}}
156
+ as |layers|
157
+ >
158
+ {{yield layers}}
159
+ </LeafletMap>
160
+ {{else if this.unwrappedCenter}}
161
+ <LeafletMap
162
+ @center={{this.unwrappedCenter.center}}
163
+ @zoom={{this.unwrappedCenter.zoom}}
164
+ @onClick={{@onClick}}
165
+ as |layers|
166
+ >
167
+ {{yield layers}}
168
+ </LeafletMap>
169
+ {{else if this.unwrappedLatLng}}
170
+ <LeafletMap
171
+ @lat={{this.unwrappedLatLng.lat}}
172
+ @lng={{this.unwrappedLatLng.lng}}
173
+ @zoom={{this.unwrappedLatLng.zoom}}
174
+ @onClick={{@onClick}}
175
+ as |layers|
176
+ >
177
+ {{yield layers}}
178
+ </LeafletMap>
179
+ {{/if}}
180
+ </template>
49
181
  }
50
182
 
51
183
  export default class LocationPluginMapComponent extends Component<Signature> {
52
- @tracked mapCenter = COORD_SYSTEM_CENTER;
53
- @tracked zoom = 7;
54
- @tracked existingLocationCoords: GlobalCoordinates | undefined;
184
+ @tracked vertices: GlobalCoordinates[] = [];
55
185
 
56
- addressLatLngLookup = restartableTask(
57
- async (): Promise<GlobalCoordinates | undefined> => {
58
- const { address, existingLocation } = this.args;
59
- if (address) {
60
- this.mapCenter = await ensureGlobalCoordinates(
61
- address.location.location,
62
- );
63
- this.zoom = 19;
64
- } else if (existingLocation) {
65
- this.mapCenter = await ensureGlobalCoordinates(existingLocation);
66
- this.existingLocationCoords = this.mapCenter;
67
- this.zoom = 19;
186
+ // Use untracked properties as otherwise the map jumps to any area or location we pick
187
+ existingAreaBounds = generateBoundsFromShape(this.args.existingArea);
188
+ existingLocationCoords = this.args.existingLocation
189
+ ? ensureGlobalCoordinates(this.args.existingLocation)
190
+ : undefined;
191
+
192
+ @trackedReset({
193
+ memo(component: LocationPluginMapComponent) {
194
+ return (
195
+ component.args.address ||
196
+ component.args.existingLocation ||
197
+ component.existingAreaBounds
198
+ );
199
+ },
200
+ update(component: LocationPluginMapComponent) {
201
+ if (component.args.address) {
202
+ return {
203
+ center: ensureGlobalCoordinates(
204
+ component.args.address.location.location,
205
+ ),
206
+ zoom: 18,
207
+ };
208
+ } else if (component.args.existingLocation) {
209
+ return {
210
+ center: ensureGlobalCoordinates(component.args.existingLocation),
211
+ zoom: 18,
212
+ };
213
+ } else if (component.existingAreaBounds) {
214
+ return {
215
+ bounds: component.existingAreaBounds,
216
+ };
217
+ } else {
218
+ return COORD_SYSTEM_START;
68
219
  }
69
- return COORD_SYSTEM_CENTER;
70
220
  },
71
- );
72
- addressLatLng = trackedTask<GlobalCoordinates | undefined>(
73
- this,
74
- this.addressLatLngLookup,
75
- () => [this.args.address, this.args.existingLocation],
76
- );
221
+ })
222
+ mapLocation: LeafletMapStart = COORD_SYSTEM_START;
77
223
 
78
- get doRenderMap() {
79
- return this.addressLatLng.value && this.zoom;
224
+ get foundAddress() {
225
+ return 'center' in this.mapLocation ? this.mapLocation.center : false;
80
226
  }
81
227
 
82
228
  @action
83
229
  onMapClick(event: LeafletMouseEvent) {
84
230
  if (this.args.locationType === 'place') {
85
231
  this.args.setLocation(event.latlng);
232
+ } else if (this.args.locationType === 'area') {
233
+ this.vertices = [...this.vertices, event.latlng];
234
+ }
235
+ }
236
+
237
+ @action
238
+ onVertexClick(index: number) {
239
+ if (index + 1 === this.vertices.length) {
240
+ // Last vertex, click to remove
241
+ this.vertices = this.vertices.slice(0, -1);
242
+ } else if (index === 0) {
243
+ // First vertex, click to end
244
+ this.args.setArea(this.vertices);
245
+ this.vertices = [];
86
246
  }
87
247
  }
88
248
 
89
249
  <template>
90
250
  <div class='map-wrapper' ...attributes>
91
- {{#if this.addressLatLng.isRunning}}
92
- <AuLoader @hideMessage={{true}}>
93
- {{t 'common.initialBounds.loading'}}
94
- </AuLoader>
95
- {{else if this.addressLatLng.value}}
96
- <LeafletMap
97
- @center={{this.mapCenter}}
98
- @zoom={{this.zoom}}
99
- @onClick={{this.onMapClick}}
100
- as |layers|
101
- >
102
- <layers.tile
103
- @url='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
104
- @attribution='&copy; <a target="_blank" href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
105
- />
106
- {{#if (and @address (eq @locationType 'address'))}}
107
- <layers.marker @location={{this.mapCenter}} as |marker|>
108
- <marker.popup>
109
- {{@address.formatted}}
110
- </marker.popup>
111
- </layers.marker>
112
- {{/if}}
113
- {{#if this.existingLocationCoords}}
251
+ <MapWrapper
252
+ @mapStart={{this.mapLocation}}
253
+ @onClick={{this.onMapClick}}
254
+ as |layers|
255
+ >
256
+ <layers.tile
257
+ @url={{MAP_TILE_URL}}
258
+ @attribution={{MAP_TILE_ATTRIBUTION}}
259
+ />
260
+ {{#if (and @address (eq @locationType 'address') this.foundAddress)}}
261
+ <layers.marker @location={{this.foundAddress}} as |marker|>
262
+ <marker.tooltip>
263
+ {{@address.formatted}}
264
+ </marker.tooltip>
265
+ </layers.marker>
266
+ {{/if}}
267
+ {{#if this.existingLocationCoords}}
268
+ <layers.marker
269
+ @opacity={{if @location 0.3 1}}
270
+ @location={{this.existingLocationCoords}}
271
+ as |marker|
272
+ >
273
+ <marker.tooltip>
274
+ {{displayLocation @existingLocation}}
275
+ </marker.tooltip>
276
+ </layers.marker>
277
+ {{/if}}
278
+ {{#if (and @location (eq @locationType 'place'))}}
279
+ <layers.marker @location={{@location.global}} />
280
+ {{/if}}
281
+ {{#if (eq @locationType 'area')}}
282
+ <layers.polyline @locations={{this.vertices}} />
283
+ {{#each this.vertices as |vertex index|}}
114
284
  <layers.marker
115
- @opacity={{if @location 0.3 1}}
116
- @location={{this.existingLocationCoords}}
285
+ @location={{vertex}}
286
+ @onClick={{(fn this.onVertexClick index)}}
117
287
  as |marker|
118
288
  >
119
- <marker.popup>
120
- {{displayLocation @existingLocation}}
121
- </marker.popup>
289
+ {{#if (eq index 0)}}
290
+ <marker.tooltip>
291
+ {{t 'location-plugin.map.hint.finish-shape'}}
292
+ </marker.tooltip>
293
+ {{else if (isLast this.vertices index)}}
294
+ <marker.tooltip>
295
+ {{t 'location-plugin.map.hint.delete-point'}}
296
+ </marker.tooltip>
297
+ {{/if}}
122
298
  </layers.marker>
123
- {{/if}}
124
- {{#if (and @location (eq @locationType 'place'))}}
125
- <layers.marker @location={{@location.global}} />
126
- {{/if}}
127
- </LeafletMap>
128
- {{/if}}
299
+ {{/each}}
300
+ {{/if}}
301
+ {{#if (areaLocations @locationType @existingArea)}}
302
+ <layers.polygon
303
+ @locations={{(areaLocations @locationType @existingArea)}}
304
+ />
305
+ {{/if}}
306
+ </MapWrapper>
129
307
  </div>
130
308
  </template>
131
309
  }