inviton-powerduck 0.0.331 → 0.0.332

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.
@@ -37,6 +37,7 @@ export interface IPowerduckSystemResources {
37
37
  fileDownloadTitle: string;
38
38
  imageCrop: string;
39
39
  uploadImage: string;
40
+ uploadFailed: string;
40
41
  copyToClipboard: string;
41
42
  copyToClipboardSuccess: string;
42
43
  loginExpired: string;
@@ -3,16 +3,32 @@ import PowerduckState from '../../app/powerduck-state';
3
3
  import { AppHttpProvider } from '../api-http';
4
4
  import { DomainHelper } from './domain-helper';
5
5
 
6
+ const ABSOLUTE_URL_RE = /^https?:\/\//i;
7
+ const LEADING_SLASH_ABSOLUTE_RE = /^\/https?:\/\//i;
8
+
6
9
  export default class FileHelper {
7
10
  static getFullPath(file: Photo) {
8
- if (file == null) {
11
+ if (file == null || file.path == null) {
9
12
  return null;
10
13
  }
11
14
 
15
+ const rawPath = file.path.trim();
16
+
17
+ // 1. Already-absolute URL (Bunny CDN, S3, data:, protocol-relative) — return as-is.
18
+ if (ABSOLUTE_URL_RE.test(rawPath) || rawPath.startsWith('//') || rawPath.startsWith('data:')) {
19
+ return rawPath;
20
+ }
21
+
22
+ // 2. Stray leading-slash before scheme ("/https://..."): strip the slash.
23
+ if (LEADING_SLASH_ABSOLUTE_RE.test(rawPath)) {
24
+ return rawPath.slice(1);
25
+ }
26
+
27
+ // 3. Legacy relative path (e.g. "/photos/resorts/file.png") — resolve against backend domain.
12
28
  if (AppHttpProvider.enforceDomain == '/') {
13
- return PowerduckState.getCdnPath() + PowerduckState.getFilesPath() + file.path;
29
+ return PowerduckState.getCdnPath() + PowerduckState.getFilesPath() + rawPath;
14
30
  }
15
31
 
16
- return DomainHelper.getDomainFromUrl(AppHttpProvider.enforceDomain, true) + PowerduckState.getFilesPath() + file.path;
32
+ return DomainHelper.getDomainFromUrl(AppHttpProvider.enforceDomain, true) + PowerduckState.getFilesPath() + rawPath;
17
33
  }
18
34
  }
@@ -1,6 +1,7 @@
1
1
  import type { ImageResponse, OnUploadImageResponse } from '../../data/image';
2
2
  import { globalState } from '../../app/global-state';
3
3
  import PowerduckState from '../../app/powerduck-state';
4
+ import NotificationProvider from '../../components/ui/notification';
4
5
  import { AppHttpProvider } from '../api-http';
5
6
  import { BrowserImageCompression } from './broswer-image-compression';
6
7
  import { isNullOrEmpty } from './is-null-or-empty';
@@ -25,7 +26,7 @@ export class UploadImageHelper {
25
26
  disableCompression: boolean = false,
26
27
  compressionMaxMb: number = 0.5,
27
28
  url?: string,
28
- ): Promise<ImageResponse> {
29
+ ): Promise<ImageResponse | null> {
29
30
  if (!(disableCompression || file.type == 'image/svg+xml')) {
30
31
  file = await BrowserImageCompression.compress(file, {
31
32
  maxSizeMB: compressionMaxMb,
@@ -49,6 +50,12 @@ export class UploadImageHelper {
49
50
  credentials: UploadImageHelperConfig.includeCredentials ? PowerduckState.getIsDebugMode() ? 'include' : 'same-origin' : 'omit',
50
51
  });
51
52
 
53
+ if (!response.ok) {
54
+ NotificationProvider.showErrorMessage(PowerduckState.getResourceValue('uploadFailed'));
55
+
56
+ return null;
57
+ }
58
+
52
59
  return (await response.json()) as ImageResponse;
53
60
  }
54
61
 
@@ -32,6 +32,10 @@ class GpsBoundingBoxInputComponent extends TsxComponent<GpsBoundingBoxInputArgs>
32
32
 
33
33
  onChange(field: keyof BoundingBox, value: number) {
34
34
  // QA_AT-11: align with `GpsInput` and round to 6 decimals (~11 cm).
35
+ // QA_AT-24: NumericInput now string-truncates while typing (via
36
+ // maxDecimals={6} in renderInput). This toFixed(6) round remains as a
37
+ // defence-in-depth net for non-input paths — paste, programmatic
38
+ // bbox prefills from country / GPS centroid recompute, etc.
35
39
  if (value) {
36
40
  this.bBoxModel[field] = parseFloat(value.toFixed(6));
37
41
  } else {
@@ -58,6 +62,7 @@ class GpsBoundingBoxInputComponent extends TsxComponent<GpsBoundingBoxInputArgs>
58
62
  label={label}
59
63
  value={value}
60
64
  step={0.000001}
65
+ maxDecimals={6}
61
66
  mode={NumericInputMode.Clasic}
62
67
  showClearValueButton={showClearValueButton}
63
68
  validationState={validationState}
@@ -61,12 +61,17 @@ class GpsInputComponent extends TsxComponent<GpsInputArgs> implements GpsInputAr
61
61
  label={null}
62
62
  value={this.gpsModel.latitude}
63
63
  step={0.000001}
64
+ maxDecimals={6}
64
65
  mode={NumericInputMode.Clasic}
65
66
  showClearValueButton={this.showClearValueButton}
66
67
  validationState={this.validationStateLatitude}
67
68
  disabled={this.disabled}
68
69
  changed={(e) => {
69
70
  if (e) {
71
+ // QA_AT-24: NumericInput now string-truncates while typing
72
+ // (via maxDecimals={6}). The toFixed(6) round here remains as
73
+ // a defence-in-depth net for non-input paths — map
74
+ // contextmenu click, programmatic centroid prefills, paste.
70
75
  this.gpsModel.latitude = parseFloat(e.toFixed(6));
71
76
  } else {
72
77
  this.gpsModel.latitude = null;
@@ -81,12 +86,14 @@ class GpsInputComponent extends TsxComponent<GpsInputArgs> implements GpsInputAr
81
86
  label={null}
82
87
  value={this.gpsModel.longitude}
83
88
  step={0.000001}
89
+ maxDecimals={6}
84
90
  mode={NumericInputMode.Clasic}
85
91
  showClearValueButton={this.showClearValueButton}
86
92
  validationState={this.validationStateLongitude}
87
93
  disabled={this.disabled}
88
94
  changed={(e) => {
89
95
  if (e) {
96
+ // QA_AT-24: see latitude comment — same defence-in-depth net.
90
97
  this.gpsModel.longitude = parseFloat(e.toFixed(6));
91
98
  } else {
92
99
  this.gpsModel.longitude = null;
@@ -23,6 +23,26 @@ interface NumericInputArgs extends Omit<FormItemWrapperArgs, 'errorId'> {
23
23
  updateMode?: 'input' | 'change';
24
24
  placeholder?: string;
25
25
  decimalsAlwaysVisible?: boolean;
26
+ /**
27
+ * QA_AT-24: when set (Classic mode only), the input clamps the visible
28
+ * value to at most this many decimal places AS THE USER TYPES — not only
29
+ * on blur / save. Excess decimal characters are stripped via string
30
+ * truncation (NOT numeric rounding) so digits the operator typed are
31
+ * never silently rewritten; the DOM `input.value` is rewritten in place.
32
+ * Intermediate states like `12.` are preserved so the operator can
33
+ * continue typing.
34
+ *
35
+ * The `changed` callback respects `updateMode`: under the default
36
+ * (change-on-blur) it is NOT fired per-keystroke even when truncation
37
+ * happens — the trailing native `change` on blur reads the truncated DOM
38
+ * value and propagates it. Callers that want per-keystroke updates must
39
+ * opt in with `updateMode='input'`.
40
+ *
41
+ * Undefined = legacy unbounded-precision behaviour preserved for all
42
+ * existing callers. Spinner mode ignores this prop (Spinner editor
43
+ * already enforces decimals via Intl.NumberFormat on blur).
44
+ */
45
+ maxDecimals?: number;
26
46
  changed: (newValue: number) => void;
27
47
  mode?: NumericInputMode;
28
48
  disabled?: boolean;
@@ -52,6 +72,7 @@ class NumericInputComponent extends TsxComponent<NumericInputArgs> implements Nu
52
72
  @Prop() appendClicked: () => void;
53
73
  @Prop() prependClicked: () => void;
54
74
  @Prop() decimalsAlwaysVisible!: boolean;
75
+ @Prop() maxDecimals?: number;
55
76
  @Prop() mode: NumericInputMode;
56
77
  @Prop() updateMode?: 'input' | 'change';
57
78
  @Prop() disabled?: boolean;
@@ -92,6 +113,50 @@ class NumericInputComponent extends TsxComponent<NumericInputArgs> implements Nu
92
113
  }
93
114
  }
94
115
 
116
+ /**
117
+ * QA_AT-24: when `maxDecimals` is set and the typed value carries more
118
+ * decimal places than allowed, rewrite the DOM `input.value` to the
119
+ * truncated representation. Returns the truncated numeric value, or
120
+ * `null` when no truncation happened or the input is not yet a complete
121
+ * number — e.g. the intermediate state `12.` keeps its trailing dot so
122
+ * the operator can continue typing.
123
+ *
124
+ * Non-destructive for intermediate states: typing `12.` leaves `12.` in
125
+ * the DOM; only EXCESS decimal characters past `maxDecimals` are
126
+ * stripped. Uses string truncation (NOT numeric rounding) so digits the
127
+ * operator already typed are never silently rewritten — the save-side
128
+ * `parseFloat(e.toFixed(6))` in GpsInput / GpsBoundingBoxInput remains as
129
+ * a defence-in-depth rounding net for non-input paths.
130
+ */
131
+ private clampToMaxDecimals(inputEl: HTMLInputElement): number | null {
132
+ if (this.maxDecimals == null || this.maxDecimals < 0) {
133
+ return null;
134
+ }
135
+
136
+ const raw = inputEl.value;
137
+ if (raw == null || raw === '') {
138
+ return null;
139
+ }
140
+
141
+ // Preserve trailing "." and partial inputs like "12." — they have a
142
+ // length-0 decimal substring which is <= cap.
143
+ const dotIndex = raw.indexOf('.');
144
+ if (dotIndex < 0) {
145
+ return null;
146
+ }
147
+
148
+ const decimalPart = raw.slice(dotIndex + 1);
149
+ if (decimalPart.length <= this.maxDecimals) {
150
+ return null;
151
+ }
152
+
153
+ const truncatedString = raw.slice(0, dotIndex + 1 + this.maxDecimals);
154
+ inputEl.value = truncatedString;
155
+ const parsed = parseFloat(truncatedString);
156
+
157
+ return isNaN(parsed) ? null : parsed;
158
+ }
159
+
95
160
  getMode(): NumericInputMode {
96
161
  if (this.mode != null) {
97
162
  return this.mode;
@@ -150,6 +215,22 @@ class NumericInputComponent extends TsxComponent<NumericInputArgs> implements Nu
150
215
  value={inputValue}
151
216
  onChange={e => this.raiseChangeEvent(e)}
152
217
  onInput={(e) => {
218
+ // QA_AT-24: input-time decimal clamp. Always trims the DOM
219
+ // `input.value` when `maxDecimals` is exceeded so the operator
220
+ // never sees more digits than allowed; the `changed` callback
221
+ // however respects `updateMode` — under the default
222
+ // change-on-blur contract the trailing native `change` reads
223
+ // the truncated DOM value and propagates it, so we don't fire
224
+ // per-keystroke (which previously caused mid-typing reactive
225
+ // storms on `gpsModel.latitude` → map re-centering →
226
+ // validation re-runs).
227
+ if (this.maxDecimals != null && this.maxDecimals >= 0) {
228
+ const truncated = this.clampToMaxDecimals(e.target as HTMLInputElement);
229
+ if (truncated != null && this.changed != null && this.updateMode == 'input') {
230
+ this.changed(truncated);
231
+ }
232
+ }
233
+
153
234
  if (this.updateMode == 'input') {
154
235
  this.handleClassicInput(e);
155
236
  }
@@ -1,3 +1,4 @@
1
+ import type BoundingBox from '../../data/bounding-box';
1
2
  import { LGeoJson, LLayerGroup, LMap, LMarker, LPolygon, LTileLayer } from '@vue-leaflet/vue-leaflet';
2
3
  import L from 'leaflet';
3
4
  import * as LCluster from 'leaflet.markercluster';
@@ -5,7 +6,6 @@ import { Prop, toNative, Watch } from 'vue-facing-decorator';
5
6
  import TsxComponent, { Component } from '../../app/vuetsx';
6
7
  import { isNullOrEmpty } from '../../common/utils/is-null-or-empty';
7
8
  import { ModalUtils } from '../modal/modal-utils';
8
- import BoundingBox from '../../data/bounding-box';
9
9
  import iconUrl from './img/marker-icon.png';
10
10
  import GestureHandling from './ts/gesture-handling/gesture-handling';
11
11
  import 'leaflet/dist/leaflet.css';
@@ -77,7 +77,7 @@ interface OpenStreetMapArgs {
77
77
  }
78
78
 
79
79
  export class OpenStreetMapHelper {
80
- static getMarkerIcon (): L.Icon<L.IconOptions> {
80
+ static getMarkerIcon(): L.Icon<L.IconOptions> {
81
81
  return L.icon({
82
82
  iconUrl,
83
83
  iconAnchor: [
@@ -135,8 +135,21 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
135
135
  @Prop() initComplete?: (map: any, mapHandler: any) => void;
136
136
  leafletIcon: any = null;
137
137
  markerClusters: LCluster.MarkerClusterGroup = null;
138
-
139
- mounted () {
138
+ /**
139
+ * QA_AT-21: self-heal observer. Leaflet caches container size at mount
140
+ * (`L.Map._size`); when the host element starts at 0×0 (hidden tab pane,
141
+ * collapsed sidebar, dismissed modal, deferred reveal) only one tile in
142
+ * the top-left renders and `containerPointToLatLng` returns drifted
143
+ * coordinates for right-click pin moves. `ResizeObserver` fires once the
144
+ * element gets its real size and recalls `invalidateSize()` — generic
145
+ * mechanism that benefits every consumer (admin location/resort modals,
146
+ * shop search-results-map-view, product-picker-dropdown-map, ...).
147
+ */
148
+ private _resizeObserver: ResizeObserver | null = null;
149
+ private _lastObservedWidth: number = 0;
150
+ private _pendingResizeRaf: number | null = null;
151
+
152
+ mounted() {
140
153
  this.initIcon();
141
154
  this.waitForLeaflet().then(() => {
142
155
  ModalUtils.handleModalMountDelay(this, () => {
@@ -149,21 +162,103 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
149
162
  this.initComplete(this.getLeaflet(), this.getMap());
150
163
  });
151
164
  }
165
+
166
+ // QA_AT-21: attach AFTER the initial invalidateSize so the
167
+ // first reading we cache reflects the real mounted size and
168
+ // the observer fires only on subsequent meaningful resizes.
169
+ this.attachResizeObserver();
170
+ });
171
+ });
172
+ }
173
+
174
+ beforeUnmount() {
175
+ // QA_AT-21: clean up the observer so detached components don't keep
176
+ // calling `invalidateSize()` on a torn-down map instance.
177
+ if (this._resizeObserver != null) {
178
+ this._resizeObserver.disconnect();
179
+ this._resizeObserver = null;
180
+ }
181
+
182
+ if (this._pendingResizeRaf != null && typeof (globalThis as any).cancelAnimationFrame === 'function') {
183
+ (globalThis as any).cancelAnimationFrame(this._pendingResizeRaf);
184
+ this._pendingResizeRaf = null;
185
+ }
186
+ }
187
+
188
+ private attachResizeObserver() {
189
+ const el = this.$el as HTMLElement | null;
190
+ if (el == null) {
191
+ return;
192
+ }
193
+
194
+ // Guard for SSR + jsdom (`ResizeObserver` only exists in real browsers;
195
+ // Chromium 64+ / Firefox 69+).
196
+ if (typeof globalThis === 'undefined' || typeof (globalThis as any).ResizeObserver !== 'function') {
197
+ return;
198
+ }
199
+
200
+ const RO = (globalThis as any).ResizeObserver as typeof ResizeObserver;
201
+ this._lastObservedWidth = el.getBoundingClientRect().width || 0;
202
+ this._resizeObserver = new RO((entries) => {
203
+ const entry = entries[0];
204
+ if (entry == null) {
205
+ return;
206
+ }
207
+
208
+ const width = entry.contentRect?.width ?? 0;
209
+ // Skip no-op resizes — `invalidateSize()` is cheap but schedules a
210
+ // re-render; gate on width-delta > 1 px to prevent feedback loops.
211
+ if (Math.abs(width - this._lastObservedWidth) < 1) {
212
+ return;
213
+ }
214
+
215
+ this._lastObservedWidth = width;
216
+ // Only run once the container has a real size so the meaningful
217
+ // first invalidate happens AFTER the container becomes visible.
218
+ if (width <= 0) {
219
+ return;
220
+ }
221
+
222
+ // QA_AT-21: rAF-coalesce rapid RO fires (window-resize drag,
223
+ // sidebar collapse animation, transition end chains) into one
224
+ // `invalidateSize()` per animation frame. Also defuses the
225
+ // browser "ResizeObserver loop completed with undelivered
226
+ // notifications" warning if Leaflet's redraw chains back into a
227
+ // sub-element size change. We use schedule-once-per-frame
228
+ // (skip-if-pending) rather than cancel-and-reschedule — the
229
+ // pending callback already reads current DOM state at fire time,
230
+ // so the latest size is picked up without re-arming.
231
+ const raf = (globalThis as any).requestAnimationFrame as ((cb: () => void) => number) | undefined;
232
+ if (typeof raf !== 'function') {
233
+ this.invalidateSize();
234
+ return;
235
+ }
236
+
237
+ if (this._pendingResizeRaf != null) {
238
+ return;
239
+ }
240
+
241
+ this._pendingResizeRaf = raf(() => {
242
+ this._pendingResizeRaf = null;
243
+ this.invalidateSize();
152
244
  });
153
245
  });
246
+ this._resizeObserver.observe(el);
154
247
  }
155
248
 
156
249
  @Watch('bBox', { immediate: false, deep: true })
157
- onBoundingBoxChanged () {
250
+ onBoundingBoxChanged() {
158
251
  const bounds = this.getBoundingBoxLatLngBounds();
159
252
  if (bounds) {
160
253
  this.getLeaflet()?.fitBounds(bounds);
161
254
  }
162
255
  }
163
256
 
164
- async waitForLeaflet (timeout = 15000, interval = 100) {
257
+ async waitForLeaflet(timeout = 15000, interval = 100) {
258
+ // eslint-disable-next-line no-restricted-syntax
165
259
  const startTime = Date.now();
166
260
  while (!this.getLeaflet()) {
261
+ // eslint-disable-next-line no-restricted-syntax
167
262
  if (Date.now() - startTime > timeout) {
168
263
  console.error('Leaflet initialization timed out.');
169
264
  return null; // or throw an error, depending on your needs
@@ -174,27 +269,27 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
174
269
  return this.getLeaflet();
175
270
  }
176
271
 
177
- initIcon () {
272
+ initIcon() {
178
273
  if (this.leafletIcon == null) {
179
274
  this.leafletIcon = OpenStreetMapHelper.getMarkerIcon();
180
275
  }
181
276
  }
182
277
 
183
278
  @Watch('geoJSON')
184
- onGeoJsonChanged () {
279
+ onGeoJsonChanged() {
185
280
  if (this.geoJSONClustering) {
186
281
  this.waitForLeaflet().then(() => {
187
- this.initCluster()
188
- })
282
+ this.initCluster();
283
+ });
189
284
  }
190
285
  }
191
286
 
192
287
  @Watch('mapUrl')
193
- onMapUrlChanged () {
288
+ onMapUrlChanged() {
194
289
  this.invalidateSize();
195
290
  }
196
291
 
197
- initGestureHandling () {
292
+ initGestureHandling() {
198
293
  if (this.gestureHandling == true) {
199
294
  const leaflet = this.getLeaflet();
200
295
  if ((leaflet as any)._gesturesBinder != true) {
@@ -204,10 +299,10 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
204
299
  }
205
300
  }
206
301
 
207
- initCluster () {
302
+ initCluster() {
208
303
  this.markerClusters?.clearLayers();
209
304
  this.markerClusters = new LCluster.MarkerClusterGroup(this.clusterOptions || {
210
- iconCreateFunction (cluster) {
305
+ iconCreateFunction(cluster) {
211
306
  const html = `<div class="cluster-marker ">${cluster.getChildCount()}<div class="pulse-animation"></div></div>`;
212
307
  return L.divIcon({ html, className: 'cluster-icon' });
213
308
  },
@@ -225,7 +320,7 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
225
320
  }
226
321
  }
227
322
 
228
- invalidateSize () {
323
+ invalidateSize() {
229
324
  const leaflet = this.getLeaflet();
230
325
  if (leaflet != null) {
231
326
  this.$nextTick(() => {
@@ -234,11 +329,11 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
234
329
  }
235
330
  }
236
331
 
237
- getNumeric (val: string): number {
332
+ getNumeric(val: string): number {
238
333
  return Number((val || '').toString().split(',').join(''));
239
334
  }
240
335
 
241
- getZoomValue (): number {
336
+ getZoomValue(): number {
242
337
  if (this.defaultZoom != null) {
243
338
  return this.defaultZoom;
244
339
  } else if (this.bBox != null) {
@@ -248,60 +343,71 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
248
343
  }
249
344
  }
250
345
 
251
- getCenter () {
346
+ getCenter() {
252
347
  if (this.bBox != null) {
253
348
  const { x1, x2, y1, y2 } = this.bBox;
254
349
  const lat = (y1 + y2) / 2;
255
350
  const lng = (x1 + x2) / 2;
256
- return [lat, lng];
351
+ return [
352
+ lat,
353
+ lng,
354
+ ];
257
355
  }
258
356
 
259
357
  const locArr = this.getLocArr();
260
358
  return locArr;
261
359
  }
262
360
 
263
- getLocPoint () {
361
+ getLocPoint() {
264
362
  return {
265
363
  lat: this.getNumeric(this.latitude),
266
364
  lng: this.getNumeric(this.longitude),
267
365
  };
268
366
  }
269
367
 
270
- getLocArr () {
368
+ getLocArr() {
271
369
  return [
272
370
  this.getNumeric(this.latitude),
273
371
  this.getNumeric(this.longitude),
274
372
  ];
275
373
  }
276
374
 
277
- getMap () {
375
+ getMap() {
278
376
  return this.$refs.mainMap;
279
377
  }
280
378
 
281
- getTileLayerOptions () {
379
+ getTileLayerOptions() {
282
380
  const options = { ...(this.options || {}) } as any;
283
381
  if (typeof URL !== 'undefined' && typeof URL.canParse === 'function' && URL.canParse(this.mapUrl)) {
284
382
  const urlHostname = new URL(this.mapUrl).hostname;
285
- const osmHosts = ['tile.openstreetmap.org', 'tile.osm.org'];
383
+ const osmHosts = [
384
+ 'tile.openstreetmap.org',
385
+ 'tile.osm.org',
386
+ ];
286
387
  if (osmHosts.some(host => urlHostname.endsWith(host)) && options.referrerPolicy == null) {
287
388
  options.referrerPolicy = 'strict-origin-when-cross-origin';
288
389
  }
289
390
  }
290
-
391
+
291
392
  return options;
292
393
  }
293
394
 
294
- getLeaflet () {
395
+ getLeaflet() {
295
396
  return (this.getMap() as any)?.leafletObject;
296
397
  }
297
398
 
298
- getBoundingBoxCorners () {
399
+ getBoundingBoxCorners() {
299
400
  if (!this.bBox) {
300
401
  return [];
301
402
  }
302
403
 
303
404
  const { x1, x2, y1, y2 } = this.bBox;
304
- if ([x1, x2, y1, y2].some(p => p == null)) {
405
+ if ([
406
+ x1,
407
+ x2,
408
+ y1,
409
+ y2,
410
+ ].some(p => p == null)) {
305
411
  return [];
306
412
  }
307
413
 
@@ -311,15 +417,30 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
311
417
  const maxLng = Math.max(x1, x2);
312
418
 
313
419
  return [
314
- [maxLat, minLng],
315
- [maxLat, maxLng],
316
- [minLat, maxLng],
317
- [minLat, minLng],
318
- [maxLat, minLng],
420
+ [
421
+ maxLat,
422
+ minLng,
423
+ ],
424
+ [
425
+ maxLat,
426
+ maxLng,
427
+ ],
428
+ [
429
+ minLat,
430
+ maxLng,
431
+ ],
432
+ [
433
+ minLat,
434
+ minLng,
435
+ ],
436
+ [
437
+ maxLat,
438
+ minLng,
439
+ ],
319
440
  ];
320
441
  }
321
442
 
322
- getBoundingBoxLatLngBounds (): L.LatLngBounds {
443
+ getBoundingBoxLatLngBounds(): L.LatLngBounds {
323
444
  const corners = this.getBoundingBoxCorners();
324
445
  if (corners.length < 2) {
325
446
  return null;
@@ -328,7 +449,7 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
328
449
  return L.latLngBounds(corners);
329
450
  }
330
451
 
331
- render (h) {
452
+ render(h) {
332
453
  if (this.is16by9 == false) {
333
454
  return this.renderLeaflet(h, { height: '100%' });
334
455
  }
@@ -346,17 +467,17 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
346
467
  );
347
468
  }
348
469
 
349
- private renderLeaflet (h, style: object) {
470
+ private renderLeaflet(h, style: object) {
350
471
  const locArr = this.getLocArr();
351
472
 
352
473
  return (
353
474
  <l-map style={style} zoom={this.getZoomValue()} options={this.mapOptions} maxZoom={this.maxZoom} center={this.getCenter()} ref="mainMap">
354
475
  <l-tile-layer url={this.mapUrl} options={this.getTileLayerOptions()} noWrap attribution={'&copy; <a target="_blank" href="http://osm.org/copyright">OpenStreetMap</a> contributors'}></l-tile-layer>
355
476
  {!this.isCenterHidden
356
- && <l-marker lat-lng={locArr} icon={this.leafletIcon}></l-marker>}
477
+ && <l-marker lat-lng={locArr} icon={this.leafletIcon}></l-marker>}
357
478
 
358
479
  {!isNullOrEmpty(this.geoJSON)
359
- && <l-geo-json ref="geoJson" options={this.geoJSONConfig != null && this.geoJSONConfig} geojson={this.geoJSONClustering ? [] : this.geoJSON}></l-geo-json>}
480
+ && <l-geo-json ref="geoJson" options={this.geoJSONConfig != null && this.geoJSONConfig} geojson={this.geoJSONClustering ? [] : this.geoJSON}></l-geo-json>}
360
481
 
361
482
  {this.bBox && (
362
483
  <l-polygon
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "inviton-powerduck",
3
3
  "type": "module",
4
- "version": "0.0.331",
4
+ "version": "0.0.332",
5
5
  "files": [
6
6
  "app/",
7
7
  "common/",