inviton-powerduck 0.0.128 → 0.0.130

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
  }
@@ -38,7 +38,10 @@ export default class LocalizedValueHelper {
38
38
  }
39
39
 
40
40
  for (const propKey in value) {
41
- return value[propKey];
41
+ const val = value[propKey];
42
+ if (val?.length > 0) {
43
+ return val;
44
+ }
42
45
  }
43
46
 
44
47
  return null as any;
@@ -891,7 +891,7 @@ class DataTableComponent extends TsxComponent<DataTableArgs> implements DataTabl
891
891
  }
892
892
  }
893
893
 
894
- markSelection(): void {
894
+ markSelection (): void {
895
895
  const mySelf = this;
896
896
  const escapeRegExp = function (string) {
897
897
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
@@ -909,26 +909,55 @@ class DataTableComponent extends TsxComponent<DataTableArgs> implements DataTabl
909
909
  };
910
910
 
911
911
  let regexBuilder = '';
912
- const markArr: string[] = [];
912
+ const markArr = [];
913
913
  if (this.filterArr != null) {
914
914
  this.filterArr.forEach((filterItem) => {
915
915
  if (regexBuilder.length > 0) {
916
916
  regexBuilder += '|';
917
917
  }
918
918
 
919
+ const columnId = filterItem.PropertyName?.toLowerCase();
920
+ const tds = Array.from($(mySelf.$el).find(`td.dt-col-${columnId}`));
921
+ if (tds == null || tds.length == 0) {
922
+ return;
923
+ }
924
+
919
925
  (getValueArr(filterItem) || []).forEach((filterValue) => {
920
- markArr.push(escapeRegExp(filterValue));
926
+ markArr.push({
927
+ value: escapeRegExp(filterValue),
928
+ elements: tds,
929
+ });
921
930
  });
922
931
  });
923
932
  }
924
933
 
925
934
  if (!isNullOrEmpty(this.fullTextQuery)) {
926
- regexBuilder += escapeRegExp(this.fullTextQuery);
935
+ const value = escapeRegExp(this.fullTextQuery);
936
+ const tds = Array.from($(mySelf.$el).find(`td`));
937
+ regexBuilder += value;
938
+
939
+ markArr.push({
940
+ value,
941
+ elements: tds,
942
+ });
927
943
  }
928
944
 
929
945
  if (DataTableConfig.filterMarking) {
930
946
  this.unmarkSelection(() => {
931
- mySelf.markInstance.mark(markArr);
947
+ for (const item of markArr) {
948
+ this.markInstance.mark(item.value, {
949
+ filter: (node) => {
950
+ const parentTd = $(node).closest('td')[0];
951
+ if (!parentTd) {
952
+ return false;
953
+ }
954
+
955
+ const matchesColumn = item.elements?.some(el => parentTd?.isEqualNode(el));
956
+ const matchesText = parentTd?.textContent?.toLowerCase().includes(item.value.toLowerCase());
957
+ return matchesColumn && matchesText;
958
+ }
959
+ });
960
+ }
932
961
  });
933
962
  }
934
963
  }
@@ -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;
@@ -40,6 +40,7 @@ class LocalizedStringWysiwigComponent extends TsxComponent<LocalizedStringInputA
40
40
  @Prop() appendClicked: () => void;
41
41
  @Prop() prependClicked: () => void;
42
42
  currentValue: ILocalizedText = null;
43
+ editorHeight: number = null;
43
44
 
44
45
  raiseChangeEvent(newValue: ILocalizedText): void {
45
46
  this.populateValidationDeclaration();
@@ -59,6 +60,10 @@ class LocalizedStringWysiwigComponent extends TsxComponent<LocalizedStringInputA
59
60
  this.raiseChangeEvent(value);
60
61
  }
61
62
 
63
+ onEditorResized (height: number): void {
64
+ this.editorHeight = height;
65
+ }
66
+
62
67
  getActualValue(lang: Language, value: ILocalizedText): string {
63
68
  if (value == null) {
64
69
  return null;
@@ -104,6 +109,8 @@ class LocalizedStringWysiwigComponent extends TsxComponent<LocalizedStringInputA
104
109
  key={lang.id}
105
110
  wrap={false}
106
111
  resizable={this.resizable}
112
+ initialHeight={this.editorHeight}
113
+ onResized={this.onEditorResized}
107
114
  label={null}
108
115
  value={this.getActualValue(lang.id, this.value)}
109
116
  changed={(e) => {
@@ -32,6 +32,8 @@ interface WysiwigEditorArgs extends FormItemWrapperArgs {
32
32
  useCommonUpload?: boolean;
33
33
  uploadPluginArgs?: () => any | any;
34
34
  resizable?: boolean;
35
+ initialHeight?: number;
36
+ onResized?: (height: number) => void;
35
37
  changed: (newValue: string) => void;
36
38
  }
37
39
 
@@ -56,6 +58,8 @@ class WysiwigEditorComponent extends TsxComponent<WysiwigEditorArgs> implements
56
58
  @Prop() appendClicked: () => void;
57
59
  @Prop() prependClicked: () => void;
58
60
  @Prop() changed: (newValue: string) => void;
61
+ @Prop() initialHeight: number = null;
62
+ @Prop() onResized?: (height: number) => void;
59
63
  @Prop({ default: true }) resizable: boolean;
60
64
  skipTrigger: boolean = false;
61
65
 
@@ -160,6 +164,25 @@ class WysiwigEditorComponent extends TsxComponent<WysiwigEditorArgs> implements
160
164
  innerEditor.addClass('vertical-resize');
161
165
  }
162
166
 
167
+ if (this.initialHeight) {
168
+ innerEditor.css('height', `${this.initialHeight}px`);
169
+ }
170
+
171
+ let lastNotifiedHeight = this.initialHeight || null;
172
+ if (this.onResized != null && this.resizable) {
173
+ const resizeObserver = new ResizeObserver((entries) => {
174
+ for (const entry of entries) {
175
+ const height = entry?.contentRect?.height;
176
+ if (height > 0 && (lastNotifiedHeight == null || Math.abs(height - lastNotifiedHeight) > 2)) { // To keep height stable
177
+ lastNotifiedHeight = height;
178
+ this.onResized(height);
179
+ }
180
+ }
181
+ });
182
+
183
+ resizeObserver.observe(innerEditor[0]);
184
+ }
185
+
163
186
  innerEditor.on('click', (e) => {
164
187
  const clickTarget = e.target;
165
188
  if (clickTarget != null && clickTarget.nodeName == 'IMG') {
@@ -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.130",
5
5
  "files": [
6
6
  "app/",
7
7
  "common/",