inviton-powerduck 0.0.128 → 0.0.129

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.
@@ -83,4 +83,9 @@ export interface IPowerduckSystemResources {
83
83
  signInX: string;
84
84
  signInFigma: string;
85
85
  signInDribble: string;
86
+ gpsBoundingBoxLabel: string;
87
+ gpsBoundingBoxX1: string;
88
+ gpsBoundingBoxX2: string;
89
+ gpsBoundingBoxY1: string;
90
+ gpsBoundingBoxY2: string;
86
91
  }
@@ -0,0 +1,113 @@
1
+ import { Component, Prop, toNative } from 'vue-facing-decorator';
2
+ import TsxComponent from '../../app/vuetsx';
3
+ import { ValidationState } from '../../common/static-wrappers/interfaces/validation-interface';
4
+ import BoundingBox from '../../data/bounding-box';
5
+ import FlexContainer from '../form/flex-container';
6
+ import FlexFormItem from '../form/form-item-flex';
7
+ import FormItemWrapper from '../form/form-item-wrapper';
8
+ import OpenStreetMap from '../open-street-map/open-street-map';
9
+ import NumericInput, { NumericInputMode } from './numeric-input';
10
+ import PowerduckState from '../../app/powerduck-state';
11
+
12
+ interface GpsBoundingBoxInputArgs {
13
+ bBoxModel: BoundingBox;
14
+ mandatory?: boolean;
15
+ }
16
+
17
+ @Component
18
+ class GpsBoundingBoxInputComponent extends TsxComponent<GpsBoundingBoxInputArgs> implements GpsBoundingBoxInputArgs {
19
+ @Prop() bBoxModel: BoundingBox;
20
+ @Prop() mandatory?: boolean;
21
+
22
+ onChange (field: keyof BoundingBox, value: number) {
23
+ if (value) {
24
+ this.bBoxModel[field] = parseFloat(value.toFixed(7));
25
+ } else {
26
+ this.bBoxModel[field] = null;
27
+ }
28
+ }
29
+
30
+ renderInput (
31
+ h: any,
32
+ label: string,
33
+ value: number,
34
+ onChanged?: (value: number) => void,
35
+ showClearValueButton?: boolean,
36
+ validationState?: ValidationState,
37
+ ) {
38
+ return (
39
+ <FlexFormItem flexFill={true}>
40
+ <NumericInput
41
+ mandatory={this.mandatory}
42
+ label={label}
43
+ value={value}
44
+ step={0.000001}
45
+ mode={NumericInputMode.Clasic}
46
+ showClearValueButton={showClearValueButton}
47
+ validationState={validationState}
48
+ changed={(e) => {
49
+ if (onChanged != null) {
50
+ onChanged(e);
51
+ }
52
+ }}
53
+ />
54
+ </FlexFormItem>
55
+ );
56
+ }
57
+
58
+ render (h) {
59
+ if (!this.bBoxModel) {
60
+ return null;
61
+ }
62
+
63
+ return (
64
+ <div>
65
+ <FormItemWrapper label={PowerduckState.getResourceValue('gpsBoundingBoxLabel')} mandatory={this.mandatory}>
66
+ <FlexContainer fullWidthOnMobile={true}>
67
+ <FlexFormItem>
68
+ {this.renderInput(
69
+ h,
70
+ PowerduckState.getResourceValue('gpsBoundingBoxX1'),
71
+ this.bBoxModel.x1,
72
+ e => this.onChange('x1', e),
73
+ )}
74
+ {this.renderInput(
75
+ h,
76
+ PowerduckState.getResourceValue('gpsBoundingBoxX2'),
77
+ this.bBoxModel.x2,
78
+ e => this.onChange('x2', e),
79
+ )}
80
+ </FlexFormItem>
81
+ <FlexFormItem>
82
+ {this.renderInput(
83
+ h,
84
+ PowerduckState.getResourceValue('gpsBoundingBoxY1'),
85
+ this.bBoxModel.y1,
86
+ e => this.onChange('y1', e),
87
+ )}
88
+ {this.renderInput(
89
+ h,
90
+ PowerduckState.getResourceValue('gpsBoundingBoxY2'),
91
+ this.bBoxModel.y2,
92
+ e => this.onChange('y2', e),
93
+ )}
94
+ </FlexFormItem>
95
+ </FlexContainer>
96
+ </FormItemWrapper>
97
+
98
+ <FormItemWrapper label={null} mandatory={false}>
99
+ <div class="rounded">
100
+ <OpenStreetMap
101
+ ref="openStreetMap"
102
+ isCenterHidden={true}
103
+ bBox={this.bBoxModel}
104
+ />
105
+ </div>
106
+ </FormItemWrapper>
107
+ </div>
108
+ );
109
+ }
110
+ }
111
+
112
+ const GpsBoundingBoxInput = toNative(GpsBoundingBoxInputComponent);
113
+ export default GpsBoundingBoxInput;
@@ -1,10 +1,11 @@
1
- import { LGeoJson, LLayerGroup, LMap, LMarker, LTileLayer } from '@vue-leaflet/vue-leaflet';
1
+ import { LGeoJson, LLayerGroup, LMap, LMarker, LPolygon, LTileLayer } from '@vue-leaflet/vue-leaflet';
2
2
  import L from 'leaflet';
3
3
  import * as LCluster from 'leaflet.markercluster';
4
4
  import { Prop, toNative, Watch } from 'vue-facing-decorator';
5
5
  import TsxComponent, { Component } from '../../app/vuetsx';
6
6
  import { isNullOrEmpty } from '../../common/utils/is-null-or-empty';
7
7
  import { ModalUtils } from '../modal/modal-utils';
8
+ import BoundingBox from '../../data/bounding-box';
8
9
  import iconUrl from './img/marker-icon.png';
9
10
  import GestureHandling from './ts/gesture-handling/gesture-handling';
10
11
  import 'leaflet/dist/leaflet.css';
@@ -57,6 +58,7 @@ interface OpenStreetMapClusterOptions {
57
58
  interface OpenStreetMapArgs {
58
59
  latitude?: string;
59
60
  longitude?: string;
61
+ bBox?: BoundingBox;
60
62
  name?: string;
61
63
  address?: string;
62
64
  is16by9?: boolean;
@@ -75,7 +77,7 @@ interface OpenStreetMapArgs {
75
77
  }
76
78
 
77
79
  export class OpenStreetMapHelper {
78
- static getMarkerIcon(): L.Icon<L.IconOptions> {
80
+ static getMarkerIcon (): L.Icon<L.IconOptions> {
79
81
  return L.icon({
80
82
  iconUrl,
81
83
  iconAnchor: [
@@ -109,11 +111,13 @@ export interface GoogleMapRefreshArgs {
109
111
  'l-marker': LMarker,
110
112
  'l-geo-json': LGeoJson,
111
113
  'l-layer-group': LLayerGroup,
114
+ 'l-polygon': LPolygon,
112
115
  },
113
116
  })
114
117
  export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> implements OpenStreetMapArgs {
115
118
  @Prop() latitude!: string;
116
119
  @Prop() longitude!: string;
120
+ @Prop() bBox?: BoundingBox;
117
121
  @Prop() name!: string;
118
122
  @Prop() address!: string;
119
123
  @Prop() is16by9!: boolean;
@@ -132,12 +136,14 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
132
136
  leafletIcon: any = null;
133
137
  markerClusters: LCluster.MarkerClusterGroup = null;
134
138
 
135
- mounted() {
139
+ mounted () {
136
140
  this.initIcon();
137
141
  this.waitForLeaflet().then(() => {
138
142
  ModalUtils.handleModalMountDelay(this, () => {
139
143
  this.initGestureHandling();
140
144
  this.invalidateSize();
145
+ this.onBoundingBoxChanged();
146
+
141
147
  if (this.initComplete != null) {
142
148
  this.$nextTick(() => {
143
149
  this.initComplete(this.getLeaflet(), this.getMap());
@@ -147,7 +153,15 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
147
153
  });
148
154
  }
149
155
 
150
- async waitForLeaflet(timeout = 15000, interval = 100) {
156
+ @Watch('bBox', { immediate: false, deep: true })
157
+ onBoundingBoxChanged () {
158
+ const bounds = this.getBoundingBoxLatLngBounds();
159
+ if (bounds) {
160
+ this.getLeaflet()?.fitBounds(bounds);
161
+ }
162
+ }
163
+
164
+ async waitForLeaflet (timeout = 15000, interval = 100) {
151
165
  const startTime = Date.now();
152
166
  while (!this.getLeaflet()) {
153
167
  if (Date.now() - startTime > timeout) {
@@ -160,25 +174,25 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
160
174
  return this.getLeaflet();
161
175
  }
162
176
 
163
- initIcon() {
177
+ initIcon () {
164
178
  if (this.leafletIcon == null) {
165
179
  this.leafletIcon = OpenStreetMapHelper.getMarkerIcon();
166
180
  }
167
181
  }
168
182
 
169
183
  @Watch('geoJSON')
170
- onGeoJsonChanged() {
184
+ onGeoJsonChanged () {
171
185
  if (this.geoJSONClustering) {
172
186
  this.initCluster();
173
187
  }
174
188
  }
175
189
 
176
190
  @Watch('mapUrl')
177
- onMapUrlChanged() {
191
+ onMapUrlChanged () {
178
192
  this.invalidateSize();
179
193
  }
180
194
 
181
- initGestureHandling() {
195
+ initGestureHandling () {
182
196
  if (this.gestureHandling == true) {
183
197
  const leaflet = this.getLeaflet();
184
198
  if ((leaflet as any)._gesturesBinder != true) {
@@ -188,10 +202,10 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
188
202
  }
189
203
  }
190
204
 
191
- initCluster() {
205
+ initCluster () {
192
206
  this.markerClusters?.clearLayers();
193
207
  this.markerClusters = new LCluster.MarkerClusterGroup(this.clusterOptions || {
194
- iconCreateFunction(cluster) {
208
+ iconCreateFunction (cluster) {
195
209
  const html = `<div class="cluster-marker ">${cluster.getChildCount()}<div class="pulse-animation"></div></div>`;
196
210
  return L.divIcon({ html, className: 'cluster-icon' });
197
211
  },
@@ -209,7 +223,7 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
209
223
  }
210
224
  }
211
225
 
212
- invalidateSize() {
226
+ invalidateSize () {
213
227
  const leaflet = this.getLeaflet();
214
228
  if (leaflet != null) {
215
229
  this.$nextTick(() => {
@@ -218,41 +232,88 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
218
232
  }
219
233
  }
220
234
 
221
- getNumeric(val: string): number {
235
+ getNumeric (val: string): number {
222
236
  return Number((val || '').toString().split(',').join(''));
223
237
  }
224
238
 
225
- getZoomValue(): number {
239
+ getZoomValue (): number {
226
240
  if (this.defaultZoom != null) {
227
241
  return this.defaultZoom;
242
+ } else if (this.bBox != null) {
243
+ return undefined;
228
244
  } else {
229
245
  return 16;
230
246
  }
231
247
  }
232
248
 
233
- getLocPoint() {
249
+ getCenter () {
250
+ if (this.bBox != null) {
251
+ const { x1, x2, y1, y2 } = this.bBox;
252
+ const lat = (y1 + y2) / 2;
253
+ const lng = (x1 + x2) / 2;
254
+ return [lat, lng];
255
+ }
256
+
257
+ const locArr = this.getLocArr();
258
+ return locArr;
259
+ }
260
+
261
+ getLocPoint () {
234
262
  return {
235
263
  lat: this.getNumeric(this.latitude),
236
264
  lng: this.getNumeric(this.longitude),
237
265
  };
238
266
  }
239
267
 
240
- getLocArr() {
268
+ getLocArr () {
241
269
  return [
242
270
  this.getNumeric(this.latitude),
243
271
  this.getNumeric(this.longitude),
244
272
  ];
245
273
  }
246
274
 
247
- getMap() {
275
+ getMap () {
248
276
  return this.$refs.mainMap;
249
277
  }
250
278
 
251
- getLeaflet() {
279
+ getLeaflet () {
252
280
  return (this.getMap() as any)?.leafletObject;
253
281
  }
254
282
 
255
- render(h) {
283
+ getBoundingBoxCorners () {
284
+ if (!this.bBox) {
285
+ return [];
286
+ }
287
+
288
+ const { x1, x2, y1, y2 } = this.bBox;
289
+ if ([x1, x2, y1, y2].some(p => p == null)) {
290
+ return [];
291
+ }
292
+
293
+ const minLat = Math.min(y1, y2);
294
+ const maxLat = Math.max(y1, y2);
295
+ const minLng = Math.min(x1, x2);
296
+ const maxLng = Math.max(x1, x2);
297
+
298
+ return [
299
+ [maxLat, minLng],
300
+ [maxLat, maxLng],
301
+ [minLat, maxLng],
302
+ [minLat, minLng],
303
+ [maxLat, minLng],
304
+ ];
305
+ }
306
+
307
+ getBoundingBoxLatLngBounds (): L.LatLngBounds {
308
+ const corners = this.getBoundingBoxCorners();
309
+ if (corners.length < 2) {
310
+ return null;
311
+ }
312
+
313
+ return L.latLngBounds(corners);
314
+ }
315
+
316
+ render (h) {
256
317
  if (this.is16by9 == false) {
257
318
  return this.renderLeaflet(h, { height: '100%' });
258
319
  }
@@ -270,17 +331,26 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
270
331
  );
271
332
  }
272
333
 
273
- private renderLeaflet(h, style: object) {
334
+ private renderLeaflet (h, style: object) {
274
335
  const locArr = this.getLocArr();
275
336
 
276
337
  return (
277
- <l-map style={style} zoom={this.getZoomValue()} options={this.mapOptions} maxZoom={this.maxZoom} center={locArr} ref="mainMap">
338
+ <l-map style={style} zoom={this.getZoomValue()} options={this.mapOptions} maxZoom={this.maxZoom} center={this.getCenter()} ref="mainMap">
278
339
  <l-tile-layer url={this.mapUrl} options={this.options} noWrap attribution={'&copy; <a target="_blank" href="http://osm.org/copyright">OpenStreetMap</a> contributors'}></l-tile-layer>
279
340
  {!this.isCenterHidden
280
- && <l-marker lat-lng={locArr} icon={this.leafletIcon}></l-marker>}
341
+ && <l-marker lat-lng={locArr} icon={this.leafletIcon}></l-marker>}
281
342
 
282
343
  {!isNullOrEmpty(this.geoJSON)
283
- && <l-geo-json ref="geoJson" options={this.geoJSONConfig != null && this.geoJSONConfig} geojson={this.geoJSONClustering ? [] : this.geoJSON}></l-geo-json>}
344
+ && <l-geo-json ref="geoJson" options={this.geoJSONConfig != null && this.geoJSONConfig} geojson={this.geoJSONClustering ? [] : this.geoJSON}></l-geo-json>}
345
+
346
+ {this.bBox && (
347
+ <l-polygon
348
+ lat-lngs={this.getBoundingBoxCorners()}
349
+ color="blue"
350
+ weight={2}
351
+ fillOpacity={0.1}
352
+ />
353
+ )}
284
354
 
285
355
  {this.$slots.default?.()}
286
356
  </l-map>
@@ -0,0 +1,6 @@
1
+ export default class BoundingBox {
2
+ x1: number = null;
3
+ x2: number = null;
4
+ y1: number = null;
5
+ y2: number = null;
6
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "inviton-powerduck",
3
3
  "type": "module",
4
- "version": "0.0.128",
4
+ "version": "0.0.129",
5
5
  "files": [
6
6
  "app/",
7
7
  "common/",