inviton-powerduck 0.0.137 → 0.0.139

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.
@@ -50,6 +50,7 @@ export interface IPowerduckSystemResources {
50
50
  quickEdit: string;
51
51
  quickEditItemsCount: string;
52
52
  quickEditErrorNamed: string;
53
+ translateFromThisLanguage: string;
53
54
 
54
55
  languageSK: string;
55
56
  languageEN: string;
@@ -76,6 +77,7 @@ export interface IPowerduckSystemResources {
76
77
  validationErrorIpAddress: string;
77
78
  validationErrorMacAddress: string;
78
79
  validationErrorGeneric: string;
80
+ uploadImageInvalidFileType: string;
79
81
 
80
82
  signInGoogle: string;
81
83
  signInFacebook: string;
@@ -267,7 +267,7 @@ export class DialogUtils {
267
267
  DialogIcons.Error,
268
268
  false,
269
269
  );
270
- console.log(modalSelector);
270
+ // console.log(modalSelector);
271
271
  modalContext.show();
272
272
  return modalSelector;
273
273
  }
@@ -240,7 +240,6 @@
240
240
 
241
241
  this._onKeyUp = function (e) {
242
242
  var value = e.which ? e.which : e.keyCode;
243
- console.log(value);
244
243
 
245
244
  if (value > 31) {
246
245
  clearTimeout(self._t);
@@ -1,6 +1,6 @@
1
1
  import { arraySort } from './array-sort';
2
2
 
3
- interface ArrayExtended<T> extends Array<T> {
3
+ export interface ArrayExtended<T> extends Array<T> {
4
4
  /**
5
5
  * Removes given items from the Array
6
6
  *
@@ -16,6 +16,11 @@ interface ArrayExtended<T> extends Array<T> {
16
16
  */
17
17
  insertAt: (item: T, index: number) => void;
18
18
 
19
+ /**
20
+ * Clones current array into new instance
21
+ */
22
+ clone: () => ArrayExtended<T>;
23
+
19
24
  /**
20
25
  * Sorts array by given property
21
26
  *
@@ -75,10 +80,10 @@ interface ArrayExtended<T> extends Array<T> {
75
80
  }
76
81
 
77
82
  if (!(Array.prototype as any).find) {
78
- (Array.prototype as any).find = function (callback) {
83
+ (Array.prototype as any).find = function (callback, ...args) {
79
84
  const list = new Object(this);
80
85
  const length = (list as any).length >>> 0;
81
- const thisArg = arguments[1];
86
+ const thisArg = args[0];
82
87
  for (let i = 0; i < length; i++) {
83
88
  const element = list[i];
84
89
  if (callback.call(
@@ -98,9 +103,9 @@ export const arrayExtend = <T>(arr: T[]): ArrayExtended<T> => {
98
103
  arr = [];
99
104
  }
100
105
 
101
- ((arr as any)).remove = function (items: []): void {
102
- for (let i = 0, len = arguments.length; i < len; i++) {
103
- const item = arguments[i];
106
+ ((arr as any)).remove = (items: [], ...args): void => {
107
+ for (let i = 0, len = args.length; i < len; i++) {
108
+ const item = args[i];
104
109
  const itemIndex = arr.indexOf(item);
105
110
 
106
111
  if (itemIndex > -1) {
@@ -109,7 +114,11 @@ export const arrayExtend = <T>(arr: T[]): ArrayExtended<T> => {
109
114
  }
110
115
  };
111
116
 
112
- ((arr as any)).insertAt = function (item, index) {
117
+ ((arr as any)).clone = function () {
118
+ return arrayExtend(this.slice(0));
119
+ };
120
+
121
+ ((arr as any)).insertAt = (item, index) => {
113
122
  arr.splice(
114
123
  index,
115
124
  0,
@@ -117,7 +126,7 @@ export const arrayExtend = <T>(arr: T[]): ArrayExtended<T> => {
117
126
  );
118
127
  };
119
128
 
120
- ((arr as any)).contains = function (item) {
129
+ ((arr as any)).contains = (item) => {
121
130
  if (item != null) {
122
131
  let idProp = 'Id';
123
132
  if (item[idProp] == null) {
@@ -137,7 +146,7 @@ export const arrayExtend = <T>(arr: T[]): ArrayExtended<T> => {
137
146
  return arr.includes(item);
138
147
  };
139
148
 
140
- ((arr as any)).min = function <T>(callbackfn: (value: T, index: number, array: T[]) => number, defaultValue?: number): number {
149
+ ((arr as any)).min = <T>(callbackfn: (value: T, index: number, array: T[]) => number, defaultValue?: number): number => {
141
150
  if (arr.length == 0) {
142
151
  return defaultValue;
143
152
  }
@@ -153,7 +162,7 @@ export const arrayExtend = <T>(arr: T[]): ArrayExtended<T> => {
153
162
  )), Number.MAX_VALUE);
154
163
  };
155
164
 
156
- ((arr as any)).max = function <T>(callbackfn: (value: T, index: number, array: T[]) => number, defaultValue?: number): number {
165
+ ((arr as any)).max = <T>(callbackfn: (value: T, index: number, array: T[]) => number, defaultValue?: number): number => {
157
166
  if (arr.length == 0) {
158
167
  return defaultValue;
159
168
  }
@@ -169,7 +178,7 @@ export const arrayExtend = <T>(arr: T[]): ArrayExtended<T> => {
169
178
  )), 0);
170
179
  };
171
180
 
172
- ((arr as any)).sum = function <T>(callbackfn: (value: T, index: number, array: T[]) => number, defaultValue?: number): number {
181
+ ((arr as any)).sum = <T>(callbackfn: (value: T, index: number, array: T[]) => number, defaultValue?: number): number => {
173
182
  if (arr.length == 0) {
174
183
  return defaultValue;
175
184
  }
@@ -185,28 +194,22 @@ export const arrayExtend = <T>(arr: T[]): ArrayExtended<T> => {
185
194
  ), 0);
186
195
  };
187
196
 
188
- ((arr as any)).selectMany = function (callback): any {
189
- return arr.reduce((
190
- prevVal,
197
+ ((arr as any)).selectMany = (callback): any => arr.reduce((
198
+ prevVal,
199
+ u,
200
+ i,
201
+ ) => [
202
+ ...prevVal,
203
+ ...(callback(
191
204
  u,
192
205
  i,
193
- ) => [
194
- ...prevVal,
195
- ...(callback(
196
- u,
197
- i,
198
- arr,
199
- ) as any),
200
- ], []);
201
- };
206
+ arr,
207
+ ) as any),
208
+ ], []);
202
209
 
203
- ((arr as any)).distinct = function (): any {
204
- return arr.reduce((un, u) => ({ ...un, u }), {});
205
- };
210
+ ((arr as any)).distinct = (): any => arr.reduce((un, u) => ({ ...un, u }), {});
206
211
 
207
- ((arr as any)).sortBy = function <T>(propName: string | ((item: T) => string | number)): ArrayExtended<T> {
208
- return arraySort(arr as any, propName) as any;
209
- };
212
+ ((arr as any)).sortBy = <T>(propName: string | ((item: T) => string | number)): ArrayExtended<T> => arraySort(arr as any, propName) as any;
210
213
 
211
214
  return arr as any;
212
215
  };
@@ -25,7 +25,7 @@ export class BrowserImageCompression {
25
25
  compressedImage.isCompressed = true;
26
26
  return compressedImage;
27
27
  } catch (error) {
28
- console.log(error);
28
+ console.error(error);
29
29
  return file;
30
30
  }
31
31
  }
@@ -25,7 +25,7 @@ export namespace LanguageUtils {
25
25
  return name;
26
26
  }
27
27
 
28
- export function getLanguageList(): Array<LanguageListItem> {
28
+ export function getLanguageList(sort?: boolean): Array<LanguageListItem> {
29
29
  const retVal = <Array<LanguageListItem>>[];
30
30
 
31
31
  const getLanguage = (langCode: Language): LanguageListItem => {
@@ -80,6 +80,7 @@ export namespace LanguageUtils {
80
80
  }
81
81
  }
82
82
 
83
+ if (sort === true) {
83
84
  try {
84
85
  retVal.sort((a, b) => {
85
86
  return a.text.localeCompare(b.text);
@@ -87,6 +88,7 @@ export namespace LanguageUtils {
87
88
  } catch (e) {
88
89
  arraySort(retVal, 'text');
89
90
  }
91
+ }
90
92
 
91
93
  LanguageUtils.getLanguageList = function () {
92
94
  return retVal;
@@ -22,7 +22,7 @@ export const VueJsxTransform = {
22
22
  }
23
23
 
24
24
  if (data?.onClick != null) {
25
- console.log(data);
25
+ console.error(data);
26
26
  (window as any).kurek = data;
27
27
  }
28
28
 
@@ -1,7 +1,7 @@
1
1
  import type { Validation } from '@vuelidate/core';
2
2
  import type { DropdownButtonItemArgs } from '../dropdown-button/dropdown-button-item';
3
3
  import type { FormItemWrapperArgs, MarginType } from '../form/form-item-wrapper';
4
- import { Prop, toNative } from 'vue-facing-decorator';
4
+ import { Prop, toNative, Watch } from 'vue-facing-decorator';
5
5
  import PowerduckState from '../../app/powerduck-state';
6
6
  import TsxComponent, { Component } from '../../app/vuetsx';
7
7
  import { isNullOrEmpty } from '../../common/utils/is-null-or-empty';
@@ -68,6 +68,11 @@ class BootstrapToggleComponent extends TsxComponent<BootstrapToggleArgs> impleme
68
68
 
69
69
  }
70
70
 
71
+ @Watch('value')
72
+ onValueChanged() {
73
+ this.$forceUpdate();
74
+ }
75
+
71
76
  getCaptionTrue(): string {
72
77
  return this.captionTrue || PowerduckState.getResourceValue('yes');
73
78
  }
@@ -84,7 +84,7 @@ class PieChartComponent extends TsxComponent<PieArgs> implements PieArgs {
84
84
  this.destroyChart();
85
85
 
86
86
  this.$nextTick(() => {
87
- console.log(this.getCanvasContext() == null);
87
+ // console.log(this.getCanvasContext() == null);
88
88
 
89
89
  this._chart = new Chart(this.getCanvasContext(), {
90
90
  type: this.type || PieChartType.Doughnut,
@@ -67,16 +67,15 @@ export class DataTableStaticComponent extends TsxComponent<DataTableStaticArgs>
67
67
  return this.getTable().normalizeStringForSearch(str);
68
68
  }
69
69
 
70
- private post(data: any): Promise<any> {
71
- const self = this;
72
- return new Promise((resolve, reject) => {
73
- const rowArr = [...(self.rows || [])];
70
+ private post(_data: any): Promise<any> {
71
+ return new Promise((resolve) => {
72
+ const rowArr = [...(this.rows || [])];
74
73
 
75
74
  resolve({
76
75
  TotalCount: rowArr.length,
77
76
  TotalFilteredCount: rowArr.length,
78
77
  Rows: rowArr,
79
- InitRows: self.rows,
78
+ InitRows: this.rows,
80
79
  });
81
80
  });
82
81
  }
@@ -130,4 +129,5 @@ export class DataTableStaticComponent extends TsxComponent<DataTableStaticArgs>
130
129
  }
131
130
 
132
131
  const DataTableStatic = toNative(DataTableStaticComponent);
132
+ export type DataTableStaticType = typeof DataTableStatic.prototype;
133
133
  export default DataTableStatic;
@@ -294,6 +294,7 @@ export interface TableColumn {
294
294
  cssClass?: string;
295
295
  visible?: boolean;
296
296
  sortable?: boolean;
297
+ searchable?: boolean;
297
298
  filterType?: DataTableFilterItemType;
298
299
  filterItems?: DataTableFilterItemCollection;
299
300
  filterAllowExclusivity?: boolean;
@@ -2283,7 +2284,6 @@ class DataTableComponent extends TsxComponent<DataTableArgs> implements DataTabl
2283
2284
  return '';
2284
2285
  } else if (filterType == DataTableFilterItemType.Text || filterType == null) {
2285
2286
  return (<TextBox updateMode="input" cssClass="dt-filter-input" wrap={false} label={null} placeholder={`${capitalize(PowerduckState.getResourceValue('search'))}...`} value={this.currentAdvancedFilterState[dtColumn.id]} changed={(e) => { this.performInputFilter(dtColumn, e); }} appendIcon={closeIcon} appendIconClicked={appendIconClicked} />);
2286
- // return (<input class="dt-filter-input" placeholder={AppState.resources.search.capitalize() + "..."} onKeyup={(e) => { this.performInputFilter(dtColumn, e) }} value={this.currentAdvancedFilterState[dtColumn.id]} />)
2287
2287
  } else if (filterType == DataTableFilterItemType.Dropdown) {
2288
2288
  return (
2289
2289
  <div class="dt-filter-dropdown">
@@ -2291,7 +2291,7 @@ class DataTableComponent extends TsxComponent<DataTableArgs> implements DataTabl
2291
2291
  label={null}
2292
2292
  options={dtColumn.filterItems}
2293
2293
  wrap={false}
2294
- disableSearch={true}
2294
+ disableSearch={!dtColumn.searchable}
2295
2295
  dropdownAutoWidth={true}
2296
2296
  multiselect={true}
2297
2297
  multiselectMode={MultiselectMode.Checkboxes}
@@ -2492,5 +2492,5 @@ class DataTableComponent extends TsxComponent<DataTableArgs> implements DataTabl
2492
2492
  })();
2493
2493
 
2494
2494
  const DataTable = toNative(DataTableComponent);
2495
- export const DataTableType = typeof DataTable.prototype;
2495
+ export type DataTableType = typeof DataTable.prototype;
2496
2496
  export default DataTable;
@@ -70,8 +70,6 @@ function handleDragging(event) {
70
70
  draggingItem.style.top = `${event.pageY - mouseOffsetY}px`;
71
71
  draggingItem.style.left = `${event.pageX - mouseOffsetX}px`;
72
72
 
73
- console.log(event.pageX);
74
-
75
73
  const draggingItemCoordinates = getDOMNodePosition(draggingItem);
76
74
  const prevItem = draggingItem.previousElementSibling;
77
75
  const nextItem = draggingItem.nextElementSibling;
@@ -970,7 +970,7 @@ class DropdownListComponent extends TsxComponent<DropdownListArgs> implements Dr
970
970
  return $(`${builder}</span></span>`);
971
971
  };
972
972
  } else {
973
- console.log('Skipping tagsButtons - one of required conditions not met');
973
+ // console.log('Skipping tagsButtons - one of required conditions not met');
974
974
  }
975
975
  }
976
976
  }
@@ -1218,4 +1218,5 @@ class DropdownListComponent extends TsxComponent<DropdownListArgs> implements Dr
1218
1218
  }
1219
1219
 
1220
1220
  const DropdownList = toNative(DropdownListComponent);
1221
+ export type DropdownListType = typeof DropdownList.prototype;
1221
1222
  export default DropdownList;
@@ -84,12 +84,39 @@ import { PortalUtils } from '../../../common/utils/utils';
84
84
  self.$element.select2({
85
85
  allowClear: true,
86
86
  ajax: {
87
- transport(
87
+ transport (
88
88
  params,
89
89
  success,
90
90
  failure,
91
91
  ) {
92
- success({ results: self.currentData || [] });
92
+ const stripDiacritics = (text: string) => {
93
+ const DIACRITICS = ($ as any).fn.select2.amd.require('select2/diacritics');
94
+ const match = a => DIACRITICS[a] || a;
95
+ return text.replace(/[^\u0000-\u007E]/g, match);
96
+ };
97
+
98
+ const normalizeForSearch = (text: string) => {
99
+ if (text == null) {
100
+ return null;
101
+ }
102
+ return stripDiacritics(text).toUpperCase();
103
+ };
104
+
105
+ let data = self.currentData || [];
106
+ const searchTerm = normalizeForSearch(params.data?.term?.trim());
107
+
108
+ if (searchTerm == null || searchTerm.trim() == '') {
109
+ success({ results: data || [] });
110
+ return;
111
+ }
112
+
113
+ const filteredData = data.filter(item => {
114
+ const itemText = normalizeForSearch(item.text);
115
+ return itemText.includes(searchTerm);
116
+ });
117
+
118
+ success({ results: filteredData || [] });
119
+ // success({ results: data || [] });
93
120
  },
94
121
  },
95
122
  language: options.language,
@@ -329,7 +329,7 @@ class TimegridCalendarComponent extends TsxComponent<TimegridCalendarArgs> imple
329
329
  resourceGroupField: 'building',
330
330
  events: this.getEvents(),
331
331
  eventRender: (args) => {
332
- console.log('rendering');
332
+ // console.log('rendering');
333
333
  args.el.classList.add('tg-has-menu');
334
334
  args.el.setAttribute('data-uuid', args.event.extendedProps.uuid);
335
335
  args.el.setAttribute('data-event-id', args.event.id);
@@ -99,7 +99,7 @@ class ImageCropModalComponent extends TsxComponent<ImageCropModalBindingArgs> im
99
99
  const start = new Date().getTime();
100
100
  const img = new Image();
101
101
  img.onload = function () {
102
- const waitForLoad = function (callback) {
102
+ const waitForLoad = (callback) => {
103
103
  if (new Date().getTime() - start > 700) {
104
104
  callback();
105
105
  } else {
@@ -150,18 +150,6 @@ class ImageCropModalComponent extends TsxComponent<ImageCropModalBindingArgs> im
150
150
  (this.$refs.imageCropModal as typeof Modal.prototype).hide();
151
151
  }
152
152
 
153
- private getImageFormat(dataUrl): string {
154
- if (dataUrl.length > 100) {
155
- dataUrl = dataUrl.substring(0, 100);
156
- }
157
-
158
- if (dataUrl.contains('image/png')) {
159
- return 'image/png';
160
- } else {
161
- return 'image/jpeg';
162
- }
163
- }
164
-
165
153
  private handleSaveButtonClicked() {
166
154
  this.blocked = true;
167
155
 
@@ -169,7 +157,6 @@ class ImageCropModalComponent extends TsxComponent<ImageCropModalBindingArgs> im
169
157
  const canvas = this.$refs.imageCropCanvas as HTMLCanvasElement;
170
158
  const ctx = canvas.getContext('2d');
171
159
  const selection = this.getCurrentSelection();
172
-
173
160
  image.src = this.getFileName();
174
161
  image.onload = async () => {
175
162
  this.drawCroppedImageOnCanvas(
@@ -196,7 +183,7 @@ class ImageCropModalComponent extends TsxComponent<ImageCropModalBindingArgs> im
196
183
  this.blocked = false;
197
184
  }, 500);
198
185
  },
199
- this.getImageFormat(image.src),
186
+ await this.getImageFormat(image.src),
200
187
  0.9,
201
188
  );
202
189
  };
@@ -272,6 +259,58 @@ class ImageCropModalComponent extends TsxComponent<ImageCropModalBindingArgs> im
272
259
  }
273
260
  }
274
261
 
262
+ private async getImageFormat(url: string): Promise<string | null> {
263
+ const response = await fetch(url, { mode: 'cors' });
264
+ if (!response.ok) {
265
+ throw new Error('Failed to fetch image');
266
+ }
267
+
268
+ const buffer = await response.arrayBuffer();
269
+ const bytes = new Uint8Array(buffer);
270
+
271
+ if (
272
+ bytes[0] === 0x89
273
+ && bytes[1] === 0x50
274
+ && bytes[2] === 0x4E
275
+ && bytes[3] === 0x47
276
+ ) {
277
+ return 'image/png';
278
+ }
279
+
280
+ if (
281
+ bytes[0] === 0xFF
282
+ && bytes[1] === 0xD8
283
+ && bytes[bytes.length - 2] === 0xFF
284
+ && bytes[bytes.length - 1] === 0xD9
285
+ ) {
286
+ return 'image/jpeg';
287
+ }
288
+
289
+ if (
290
+ bytes[0] === 0x47
291
+ && bytes[1] === 0x49
292
+ && bytes[2] === 0x46
293
+ && bytes[3] === 0x38
294
+ ) {
295
+ return 'image/gif';
296
+ }
297
+
298
+ if (
299
+ bytes[0] === 0x52
300
+ && bytes[1] === 0x49
301
+ && bytes[2] === 0x46
302
+ && bytes[3] === 0x46
303
+ && bytes[8] === 0x57
304
+ && bytes[9] === 0x45
305
+ && bytes[10] === 0x42
306
+ && bytes[11] === 0x50
307
+ ) {
308
+ return 'image/webp';
309
+ }
310
+
311
+ return null;
312
+ }
313
+
275
314
  private getCurrentSelection(): ImageCropSelection {
276
315
  const img = $(this.getImageElement()).first();
277
316
  const cropHolder = $(this.$refs.imageCroppingContainer).find('.jcrop-active').first();
@@ -2,8 +2,11 @@ import type { _ButtonArgsBase, ButtonLayout, ButtonSize } from '../button/button
2
2
  import type { UploadButtonFileObtainedArgs } from '../button/upload-button';
3
3
  import type { ImageCroppedUploadArgs } from './image-cropping-modal';
4
4
  import { Prop, toNative } from 'vue-facing-decorator';
5
+ import PowerduckState from '../../app/powerduck-state';
5
6
  import TsxComponent, { Component } from '../../app/vuetsx';
6
7
  import { BrowserImageCompression } from '../../common/utils/broswer-image-compression';
8
+ import { formatString } from '../../common/utils/format-string';
9
+ import { PortalUtils } from '../../common/utils/utils';
7
10
  import UploadButton from '../button/upload-button';
8
11
  import LoadingIndicator from '../loading-indicator';
9
12
  import NotificationProvider from '../ui/notification';
@@ -27,6 +30,7 @@ interface UploadImageAndCropButtonArgs extends _ButtonArgsBase {
27
30
  uploadComplete: (args: any) => void;
28
31
  autoCommit?: boolean;
29
32
  disableCompression?: boolean;
33
+ allowedTypes?: string[];
30
34
  compressionMaxSizeMb?: number;
31
35
  isLoading?: boolean;
32
36
  }
@@ -54,6 +58,7 @@ class UploadImageAndCropButtonComponent extends TsxComponent<UploadImageAndCropB
54
58
  @Prop() uploadComplete: (args: any) => void;
55
59
  @Prop() autoCommit: boolean;
56
60
  @Prop() disableCompression: boolean;
61
+ @Prop() allowedTypes: string[];
57
62
  @Prop() compressionMaxSizeMb?: number;
58
63
  isInternalLoading: boolean = false;
59
64
 
@@ -61,6 +66,11 @@ class UploadImageAndCropButtonComponent extends TsxComponent<UploadImageAndCropB
61
66
  const urlCreator = window.URL || window.webkitURL;
62
67
  const imgUrl = urlCreator.createObjectURL(e.file);
63
68
 
69
+ if (this.allowedTypes?.length > 0 && !this.allowedTypes.includes(e.file.type)) {
70
+ NotificationProvider.showErrorMessage(formatString(PowerduckState.getResourceValue('uploadImageInvalidFileType'), PortalUtils.htmlEscape(this.allowedTypes.join(', '))));
71
+ return;
72
+ }
73
+
64
74
  if (this.useCropper != false) {
65
75
  (this.$refs.imgCrop as typeof ImageCropModal.prototype).show({
66
76
  fileName: e.file.name,
@@ -78,9 +88,9 @@ class UploadImageAndCropButtonComponent extends TsxComponent<UploadImageAndCropB
78
88
  file: e.file,
79
89
  fileName: e.file.name,
80
90
  dataUrl: null,
81
- }).then((resp) => {
91
+ }).then(() => {
82
92
  this.isInternalLoading = false;
83
- }).catch((err) => {
93
+ }).catch((_err) => {
84
94
  this.isInternalLoading = false;
85
95
  });
86
96
  }
@@ -91,8 +101,6 @@ class UploadImageAndCropButtonComponent extends TsxComponent<UploadImageAndCropB
91
101
  }
92
102
 
93
103
  async getUploadPromise(args: ImageCroppedUploadArgs): Promise<any> {
94
- const self = this;
95
-
96
104
  if (!(this.disableCompression || args.file.type == 'image/svg+xml')) {
97
105
  args.file = await BrowserImageCompression.compress(args.file, {
98
106
  maxSizeMB: this.compressionMaxSizeMb,
@@ -101,8 +109,8 @@ class UploadImageAndCropButtonComponent extends TsxComponent<UploadImageAndCropB
101
109
 
102
110
  return new Promise((resolve, reject) => {
103
111
  const formData = new FormData();
104
- if (self.uploadArgs != null) {
105
- const argsVal = self.uploadArgs(args);
112
+ if (this.uploadArgs != null) {
113
+ const argsVal = this.uploadArgs(args);
106
114
  if (argsVal != null) {
107
115
  for (const key in argsVal) {
108
116
  formData.append(key, argsVal[key]);
@@ -110,10 +118,10 @@ class UploadImageAndCropButtonComponent extends TsxComponent<UploadImageAndCropB
110
118
  }
111
119
  }
112
120
 
113
- if (self.autoCommit === true) {
121
+ if (this.autoCommit === true) {
114
122
  const request = new XMLHttpRequest();
115
- request.open('POST', self.uploadUrl);
116
- request.onreadystatechange = function () {
123
+ request.open('POST', this.uploadUrl);
124
+ request.onreadystatechange = () => {
117
125
  if (request.readyState == 4) {
118
126
  let response;
119
127
  try {
@@ -123,8 +131,8 @@ class UploadImageAndCropButtonComponent extends TsxComponent<UploadImageAndCropB
123
131
  }
124
132
 
125
133
  if (request.status == 200) {
126
- if (self.uploadComplete) {
127
- self.uploadComplete(response);
134
+ if (this.uploadComplete) {
135
+ this.uploadComplete(response);
128
136
  }
129
137
 
130
138
  resolve(response);
@@ -144,8 +152,8 @@ class UploadImageAndCropButtonComponent extends TsxComponent<UploadImageAndCropB
144
152
 
145
153
  request.send(formData);
146
154
  } else {
147
- const argsVal = self.uploadArgs(args);
148
- self.uploadComplete(argsVal);
155
+ const argsVal = this.uploadArgs(args);
156
+ this.uploadComplete(argsVal);
149
157
  resolve(formData);
150
158
  }
151
159
  });
@@ -1,5 +1,6 @@
1
1
  import type { ILocalizedText } from '../../common/localized-text';
2
2
  import type { ValidationState } from '../../common/static-wrappers/interfaces/validation-interface';
3
+ import type { TranslateGetRequest, TranslateGetResponse } from './translate';
3
4
  import { Prop, toNative } from 'vue-facing-decorator';
4
5
  import TsxComponent, { Component } from '../../app/vuetsx';
5
6
  import { Language } from '../../common/enums/language';
@@ -19,6 +20,8 @@ interface LocalizedInfoInputArgs {
19
20
  validationState?: ValidationState;
20
21
  changed: (e: ILocalizedText) => void;
21
22
  mandatory?: boolean;
23
+ translatable?: boolean;
24
+ translateApiMethod?: <RQ extends TranslateGetRequest, RS extends TranslateGetResponse>(data?: RQ) => Promise<RS>;
22
25
  }
23
26
 
24
27
  @Component
@@ -32,6 +35,8 @@ class LocalizedInfoInputComponent extends TsxComponent<LocalizedInfoInputArgs> i
32
35
  @Prop() displayType: 'input' | 'textarea';
33
36
  @Prop() changed: (e: ILocalizedText) => void;
34
37
  @Prop() mandatory?: boolean;
38
+ @Prop({ default: false }) translatable: boolean;
39
+ @Prop() translateApiMethod: <RQ extends TranslateGetRequest, RS extends TranslateGetResponse>(data?: RQ) => Promise<RS>;
35
40
 
36
41
  getValueForTmrLanguage(lang: Language): string {
37
42
  return this.value[lang];
@@ -99,6 +104,8 @@ class LocalizedInfoInputComponent extends TsxComponent<LocalizedInfoInputArgs> i
99
104
  changed={(e) => {
100
105
  this.valueChanged(e);
101
106
  }}
107
+ translatable={this.translatable}
108
+ translateApiMethod={this.translateApiMethod}
102
109
  supportedLanguages={[
103
110
  Language.sk,
104
111
  Language.en,
@@ -125,6 +132,8 @@ class LocalizedInfoInputComponent extends TsxComponent<LocalizedInfoInputArgs> i
125
132
  changed={(e) => {
126
133
  this.valueChanged(e);
127
134
  }}
135
+ translatable={this.translatable}
136
+ translateApiMethod={this.translateApiMethod}
128
137
  supportedLanguages={[
129
138
  Language.sk,
130
139
  Language.en,
@@ -1,6 +1,7 @@
1
1
  import type { ILocalizedText } from '../../common/localized-text';
2
2
  import type { DropdownButtonItemArgs } from '../dropdown-button/dropdown-button-item';
3
3
  import type { FormItemWrapperArgs, HintType, MarginType } from '../form/form-item-wrapper';
4
+ import type { TranslateGetRequest, TranslateGetResponse } from './translate';
4
5
  import { Prop, toNative } from 'vue-facing-decorator';
5
6
  import PowerduckState from '../../app/powerduck-state';
6
7
  import TsxComponent, { Component } from '../../app/vuetsx';
@@ -16,6 +17,7 @@ import Modal, { ModalSize } from '../modal/modal';
16
17
  import ModalBody from '../modal/modal-body';
17
18
  import ModalFooter from '../modal/modal-footer';
18
19
  import globalIcon from './img/global.png';
20
+ import Translate from './translate';
19
21
  import './css/localized-string-input.css';
20
22
 
21
23
  interface LocalizedStringInputArgs extends FormItemWrapperArgs {
@@ -23,6 +25,8 @@ interface LocalizedStringInputArgs extends FormItemWrapperArgs {
23
25
  changed: (e: ILocalizedText) => void;
24
26
  supportedLanguages?: Language[];
25
27
  placeholder?: string;
28
+ translatable?: boolean;
29
+ translateApiMethod?: <RQ extends TranslateGetRequest, RS extends TranslateGetResponse>(data?: RQ) => Promise<RS>;
26
30
  }
27
31
 
28
32
  @Component
@@ -44,6 +48,8 @@ class LocalizedStringInputComponent extends TsxComponent<LocalizedStringInputArg
44
48
  @Prop() hintType!: HintType;
45
49
  @Prop() appendIcon: string;
46
50
  @Prop() prependIcon: string;
51
+ @Prop({default: false}) translatable: boolean;
52
+ @Prop() translateApiMethod: <RQ extends TranslateGetRequest, RS extends TranslateGetResponse>(data?: RQ) => Promise<RS>;
47
53
  @Prop() appendClicked: () => void;
48
54
  @Prop() prependClicked: () => void;
49
55
  currentValue: ILocalizedText = null;
@@ -231,6 +237,21 @@ class LocalizedStringInputComponent extends TsxComponent<LocalizedStringInputArg
231
237
  <img class="lsi-icon" src={LanguageUtils.getLanguageFlagUrl(lang.flag)} />
232
238
  </label>
233
239
  <div class="col-md-9">
240
+ <div class="d-flex justify-content-end">
241
+ {this.translatable !== false && (
242
+ <Translate
243
+ value={this.currentValue}
244
+ currentLangId={lang.id}
245
+ langArr={LanguageUtils.getLanguageList()}
246
+ changeValue={(langId, value) => {
247
+ this.handleModalValueChange(value, { id: langId });
248
+ this.$forceUpdate();
249
+ }}
250
+ getActualValue={(langId, value) => value[langId]}
251
+ apiMethod={this.translateApiMethod}
252
+ />
253
+ )}
254
+ </div>
234
255
  <div class="form-group maxwidth-input ">
235
256
  <input
236
257
  type="text"
@@ -2,6 +2,7 @@ import type { Language } from '../../common/enums/language';
2
2
  import type { ILocalizedText } from '../../common/localized-text';
3
3
  import type { DropdownButtonItemArgs } from '../dropdown-button/dropdown-button-item';
4
4
  import type { FormItemWrapperArgs, HintType, MarginType } from '../form/form-item-wrapper';
5
+ import type { TranslateGetRequest, TranslateGetResponse } from './translate';
5
6
  import { Prop, toNative } from 'vue-facing-decorator';
6
7
  import PowerduckState from '../../app/powerduck-state';
7
8
  import TsxComponent, { Component } from '../../app/vuetsx';
@@ -10,12 +11,15 @@ import FormItemWrapper from '../form/form-item-wrapper';
10
11
  import { TabPage } from '../tabs/tab-page';
11
12
  import Tabs, { TabsRenderMode } from '../tabs/tabs';
12
13
  import TextArea from './textarea';
14
+ import Translate from './translate';
13
15
 
14
16
  interface LocalizedStringInputArgs extends FormItemWrapperArgs {
15
17
  value: ILocalizedText;
16
18
  changed: (e: ILocalizedText) => void;
17
19
  supportedLanguages?: Language[];
18
20
  placeholder?: string;
21
+ translatable?: boolean;
22
+ translateApiMethod?: <RQ extends TranslateGetRequest, RS extends TranslateGetResponse>(data?: RQ) => Promise<RS>;
19
23
  }
20
24
 
21
25
  @Component
@@ -36,6 +40,8 @@ class LocalizedStringTextareaComponent extends TsxComponent<LocalizedStringInput
36
40
  @Prop() hintType!: HintType;
37
41
  @Prop() appendIcon: string;
38
42
  @Prop() prependIcon: string;
43
+ @Prop({ default: false }) translatable: boolean;
44
+ @Prop() translateApiMethod: <RQ extends TranslateGetRequest, RS extends TranslateGetResponse>(data?: RQ) => Promise<RS>;
39
45
  @Prop() appendClicked: () => void;
40
46
  @Prop() prependClicked: () => void;
41
47
  currentValue: ILocalizedText = null;
@@ -110,6 +116,16 @@ class LocalizedStringTextareaComponent extends TsxComponent<LocalizedStringInput
110
116
  }}
111
117
  rows={4}
112
118
  />
119
+ {this.translatable !== false && (
120
+ <Translate
121
+ value={this.value}
122
+ currentLangId={lang.id}
123
+ langArr={langArr}
124
+ changeValue={this.changeValue.bind(this)}
125
+ getActualValue={(langId, value) => this.getActualValue(langId, value as ILocalizedText)}
126
+ apiMethod={this.translateApiMethod}
127
+ />
128
+ )}
113
129
  </TabPage>
114
130
  ))}
115
131
  </Tabs>
@@ -2,6 +2,7 @@ import type { Language } from '../../common/enums/language';
2
2
  import type { ILocalizedText } from '../../common/localized-text';
3
3
  import type { DropdownButtonItemArgs } from '../dropdown-button/dropdown-button-item';
4
4
  import type { FormItemWrapperArgs, MarginType } from '../form/form-item-wrapper';
5
+ import type { TranslateGetRequest, TranslateGetResponse } from './translate';
5
6
  import { Prop, toNative } from 'vue-facing-decorator';
6
7
  import PowerduckState from '../../app/powerduck-state';
7
8
  import TsxComponent, { Component } from '../../app/vuetsx';
@@ -9,6 +10,7 @@ import { LanguageUtils } from '../../common/utils/language-utils';
9
10
  import FormItemWrapper from '../form/form-item-wrapper';
10
11
  import { TabPage } from '../tabs/tab-page';
11
12
  import Tabs, { TabsRenderMode } from '../tabs/tabs';
13
+ import Translate from './translate';
12
14
  import WysiwigEditor from './wysiwig';
13
15
 
14
16
  interface LocalizedStringInputArgs extends FormItemWrapperArgs {
@@ -17,6 +19,8 @@ interface LocalizedStringInputArgs extends FormItemWrapperArgs {
17
19
  supportedLanguages?: Language[];
18
20
  placeholder?: string;
19
21
  resizable?: boolean;
22
+ translatable?: boolean;
23
+ translateApiMethod?: <RQ extends TranslateGetRequest, RS extends TranslateGetResponse>(data?: RQ) => Promise<RS>;
20
24
  }
21
25
 
22
26
  @Component
@@ -39,6 +43,8 @@ class LocalizedStringWysiwigComponent extends TsxComponent<LocalizedStringInputA
39
43
  @Prop() prependIcon: string;
40
44
  @Prop() appendClicked: () => void;
41
45
  @Prop() prependClicked: () => void;
46
+ @Prop({ default: false }) translatable: boolean;
47
+ @Prop() translateApiMethod: <RQ extends TranslateGetRequest, RS extends TranslateGetResponse>(data?: RQ) => Promise<RS>;
42
48
  currentValue: ILocalizedText = null;
43
49
  editorHeight: number = null;
44
50
 
@@ -117,6 +123,16 @@ class LocalizedStringWysiwigComponent extends TsxComponent<LocalizedStringInputA
117
123
  this.changeValue(lang.id, e);
118
124
  }}
119
125
  />
126
+ {this.translatable !== false && (
127
+ <Translate
128
+ value={this.value}
129
+ currentLangId={lang.id}
130
+ langArr={langArr}
131
+ changeValue={this.changeValue.bind(this)}
132
+ getActualValue={(langId, value) => this.getActualValue(langId, value as ILocalizedText)}
133
+ apiMethod={this.translateApiMethod}
134
+ />
135
+ )}
120
136
  </TabPage>
121
137
  ))}
122
138
  </Tabs>
@@ -0,0 +1,100 @@
1
+ import type { Language } from '../../common/enums/language';
2
+ import type { ILocalizedText } from '../../common/localized-text';
3
+ import type LocalizedText from '../../common/localized-text';
4
+ import { Prop } from 'vue-facing-decorator';
5
+ import PowerduckState from '../../app/powerduck-state';
6
+ import { TsxComponentExtendedBase } from '../../app/vuestsx-extended';
7
+ import { Component } from '../../app/vuetsx';
8
+ import { LanguageUtils } from '../../common/utils/language-utils';
9
+ import TextButton from '../button/text-button';
10
+
11
+ interface TranslateArgs {
12
+ value: ILocalizedText | LocalizedText;
13
+ currentLangId: Language;
14
+ langArr: LanguageUtils.LanguageListItem[];
15
+ changeValue: (langId: Language, value: string) => void;
16
+ getActualValue: (langId: Language, value: ILocalizedText | LocalizedText) => string;
17
+ apiMethod: <RQ extends TranslateGetRequest, RS extends TranslateGetResponse>(data?: RQ) => Promise<RS>;
18
+ }
19
+
20
+ export class TranslateGetRequest {
21
+ fromLang: string;
22
+ toLang: string[];
23
+ text: string;
24
+ }
25
+
26
+ export class TranslateGetResponse {
27
+ result: LocalizedText;
28
+ }
29
+
30
+ @Component
31
+ export default class Translate extends TsxComponentExtendedBase<TranslateArgs> implements TranslateArgs {
32
+ @Prop() value: ILocalizedText | LocalizedText;
33
+ @Prop() currentLangId: Language;
34
+ @Prop() langArr: LanguageUtils.LanguageListItem[];
35
+ @Prop() apiMethod: <RQ extends TranslateGetRequest, RS extends TranslateGetResponse>(data?: RQ) => Promise<RS>;
36
+ @Prop() changeValue: (langId: Language, value: string) => void;
37
+ @Prop() getActualValue: (langId: Language, value: ILocalizedText | LocalizedText) => string;
38
+ translateLoading = false;
39
+
40
+ async translateValue(
41
+ value: string,
42
+ fromLang: Language,
43
+ langArr: LanguageUtils.LanguageListItem[],
44
+ ) {
45
+ console.log('value', value);
46
+ this.translateLoading = true;
47
+ const toLang = langArr.filter(x => !this.getActualValue(x.id, this.value))?.map(x => x.id);
48
+ console.log('toland', toLang, langArr);
49
+ if (toLang.length === 0) {
50
+ this.translateLoading = false;
51
+ return;
52
+ }
53
+
54
+ const resp = await this.tryGetDataByArgs<TranslateGetResponse, TranslateGetRequest>({
55
+ blockRoot: false,
56
+ apiMethod: this.apiMethod,
57
+ requestArgs: {
58
+ fromLang,
59
+ toLang,
60
+ text: value,
61
+ },
62
+ });
63
+
64
+ if (resp?.result != null) {
65
+ for (const key in resp.result) {
66
+ const langCode = LanguageUtils.getLanguageEnum(key);
67
+ this.changeValue(langCode, resp.result[key]);
68
+ }
69
+ } else {
70
+ console.error('Error while getting translation');
71
+ }
72
+
73
+ this.translateLoading = false;
74
+ }
75
+
76
+ render(h) {
77
+ return this.translateLoading
78
+ ? (
79
+ <div class="d-flex flex-row gap-1 align-items-center">
80
+ <div class="spinner-border spinner-border-sm" role="status">
81
+ <span class="sr-only">Loading...</span>
82
+ </div>
83
+ <span>Translating...</span>
84
+ </div>
85
+ )
86
+ : (
87
+ <TextButton
88
+ icon="icon icon-translate"
89
+ text={PowerduckState.getResourceValue('translateFromThisLanguage')}
90
+ clicked={() => {
91
+ this.translateValue(
92
+ this.getActualValue(this.currentLangId, this.value),
93
+ this.currentLangId,
94
+ this.langArr,
95
+ );
96
+ }}
97
+ />
98
+ );
99
+ }
100
+ }
@@ -314,5 +314,5 @@ class ModalComponent extends TsxComponent<ModalArgs> implements ModalArgs {
314
314
  }
315
315
 
316
316
  const Modal = toNative(ModalComponent);
317
- export const ModalType = typeof Modal.prototype;
317
+ export type ModalType = typeof Modal.prototype;
318
318
  export default Modal;
@@ -56,13 +56,11 @@ class PhotoManagerComponent extends TsxComponentExtended<PhotoManagerArgs> imple
56
56
  return [];
57
57
  }
58
58
 
59
- return this.photos.map((p, i) => {
60
- return {
61
- Id: p.id as any,
62
- ImageUrl: p.fullPath, // this.getFullPath(p),
63
- SortOrder: p.sortOrder,
64
- };
65
- });
59
+ return this.photos.map((p, i) => ({
60
+ Id: p.id as any,
61
+ ImageUrl: p.fullPath, // this.getFullPath(p),
62
+ SortOrder: p.sortOrder,
63
+ }));
66
64
  }
67
65
 
68
66
  getFullPath(img: Photo) {
@@ -71,7 +69,7 @@ class PhotoManagerComponent extends TsxComponentExtended<PhotoManagerArgs> imple
71
69
 
72
70
  parseServerResponse(resp): DropzoneGalleryItem {
73
71
  if (resp.fileData == null) {
74
- throw 'break';
72
+ throw new Error('break');
75
73
  }
76
74
 
77
75
  const retVal: PhotoManagerUberModel = resp.fileData;
@@ -90,7 +88,7 @@ class PhotoManagerComponent extends TsxComponentExtended<PhotoManagerArgs> imple
90
88
 
91
89
  render(h) {
92
90
  if (this.photos == null) {
93
- throw 'Photo collection cannot be null, always has to be at least empty array';
91
+ throw new Error('Photo collection cannot be null, always has to be at least empty array');
94
92
  }
95
93
 
96
94
  this.photos.forEach((p, i) => {
@@ -132,8 +130,6 @@ class PhotoManagerComponent extends TsxComponentExtended<PhotoManagerArgs> imple
132
130
  changed={(items) => {
133
131
  const itemsClone = [...(items || [])];
134
132
 
135
- console.log(items);
136
-
137
133
  this.photos.splice(0, this.photos.length);
138
134
  for (const p of itemsClone) {
139
135
  this.photos.push({
@@ -30,7 +30,7 @@ class ShareModalComponent extends TsxComponent<ShareComponentBindingArgs> implem
30
30
  // on success
31
31
  })
32
32
  .catch((error) => {
33
- console.log('Error sharing:', error);
33
+ console.error('Error sharing:', error);
34
34
  });
35
35
 
36
36
  return;
@@ -23,7 +23,7 @@ export class ShareHelper {
23
23
  // on success
24
24
  })
25
25
  .catch((error) => {
26
- console.log('Error sharing:', error);
26
+ console.error('Error sharing:', error);
27
27
  });
28
28
  }
29
29
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "inviton-powerduck",
3
3
  "type": "module",
4
- "version": "0.0.137",
4
+ "version": "0.0.139",
5
5
  "files": [
6
6
  "app/",
7
7
  "common/",
@@ -49,6 +49,7 @@
49
49
  "jquery-minicolors": "2.1.10",
50
50
  "jquery-resizable": "1.0.6",
51
51
  "jspreadsheet-ce": "^4.15.0",
52
+ "jsuites": "5.11.0",
52
53
  "leaflet": "^1.9.4",
53
54
  "leaflet.markercluster": "^1.5.3",
54
55
  "mark.js": "8.11.1",