inviton-powerduck 0.0.164 → 0.0.166

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/app/global-state.ts +28 -0
  2. package/app/powerduck-initializer.ts +10 -9
  3. package/app/powerduck-state.ts +3 -2
  4. package/common/ajax-xhr.ts +2 -1
  5. package/common/api-http.ts +6 -5
  6. package/common/base-component.tsx +2 -1
  7. package/common/dialog-utils.ts +2 -1
  8. package/common/external-barcode-scanner.ts +244 -242
  9. package/common/history-extended.ts +44 -6
  10. package/common/history-handler.ts +15 -15
  11. package/common/keyboard-open-tracker.ts +20 -6
  12. package/common/ladda-lite.ts +14 -1
  13. package/common/local-storage-shim.ts +66 -30
  14. package/common/resource-helper.ts +77 -71
  15. package/common/scroll-utils.ts +9 -4
  16. package/common/set-current-url.ts +8 -5
  17. package/common/utils/checkbox-utils.ts +6 -0
  18. package/common/utils/clipboard-provider.ts +37 -34
  19. package/common/utils/cookie.ts +9 -0
  20. package/common/utils/dropdown-utils.ts +5 -0
  21. package/common/utils/string-utils.ts +46 -40
  22. package/common/utils/upload-image-helper.ts +2 -1
  23. package/common/utils/utils.ts +34 -14
  24. package/components/app/navigation-guard.ts +5 -2
  25. package/components/app/root-dynamic-component-container.tsx +2 -1
  26. package/components/app/vue-plugin-jsxtransform.ts +3 -1
  27. package/components/chart-js/line-chart-flot.tsx +4 -3
  28. package/components/chart-js/line-chart.tsx +5 -0
  29. package/components/chart-js/pie-chart.tsx +4 -3
  30. package/components/collapse/index.tsx +9 -0
  31. package/components/container-with-breakpoints/ts/breakpoint-handler.ts +7 -6
  32. package/components/counter/index.tsx +2 -1
  33. package/components/counter/testall.tsx +12 -9
  34. package/components/datatable/datatable.tsx +2363 -2362
  35. package/components/dropdown/mobile/legacy_fdd.ts +10 -9
  36. package/components/dropdown/mobile/legacy_lvb.ts +3 -1
  37. package/components/file-downloader/index.tsx +6 -5
  38. package/components/google/maps.tsx +18 -8
  39. package/components/google/places-autocomplete.tsx +6 -1
  40. package/components/google/ts/google-maps-api.ts +3 -2
  41. package/components/image-crop/image-cropping-modal.tsx +11 -6
  42. package/components/image-crop/upload-and-crop.tsx +163 -162
  43. package/components/input/daterange-picker.tsx +9 -0
  44. package/components/input/datetime-picker.tsx +11 -2
  45. package/components/input/localized-url-input.tsx +2 -1
  46. package/components/input/ts/bootstrapInputSpinner.ts +7 -2
  47. package/components/input/ts/dateInputHelper.ts +8 -7
  48. package/components/memory-cache/index.ts +7 -5
  49. package/components/modal/modal-utils.ts +2 -1
  50. package/components/modal/modal.tsx +5 -4
  51. package/components/modal/ts/file-manager-dialog.ts +3 -2
  52. package/components/share/share-modal.tsx +13 -12
  53. package/components/share/share.tsx +13 -12
  54. package/components/swiper/swiper.tsx +19 -15
  55. package/package.json +1 -1
  56. package/common/cdn-webpack-shim.ts +0 -5
  57. package/components/input/plugins/daterangepicker/jquery.daterangepicker.min.js +0 -1910
@@ -1,7 +1,8 @@
1
+ import { globalState } from '../../../app/global-state';
2
+ import { latinize } from '../../../common/extensions/string-extensions';
1
3
  import TemporalUtils from '../../../common/utils/temporal-utils';
2
4
  import ListviewBuilder from './legacy_lvb';
3
5
  import './legacy_fdd.css';
4
- import { latinize } from '../../../common/extensions/string-extensions';
5
6
 
6
7
  const $ = jQuery;
7
8
 
@@ -42,11 +43,11 @@ export default function FilterableSelect(this: any, args: FilterableSelectArgs):
42
43
  let contextWindow, contextBody;
43
44
 
44
45
  if (iframeTopMode) {
45
- contextWindow = window.top;
46
+ contextWindow = globalState.top;
46
47
  contextBody = contextWindow.document.body;
47
48
  } else {
48
- contextWindow = window;
49
- contextBody = window.document.body;
49
+ contextWindow = globalState;
50
+ contextBody = globalState.document.body;
50
51
  }
51
52
 
52
53
  args = args || ({} as any);
@@ -459,7 +460,7 @@ export default function FilterableSelect(this: any, args: FilterableSelectArgs):
459
460
  $(contextBody).find('#filterableSelectOverlay').addClass('fdd-anim-shown');
460
461
  $(contextBody).find('#filterableSelectDropDown').addClass('fdd-anim-shown');
461
462
 
462
- const availableHeight = $(window).height() - 100;
463
+ const availableHeight = $(globalState).height() - 100;
463
464
  const scrollTarget = $('#filterableSelectDropDownInner');
464
465
  const selectedTop = $('.filterabledd-item.fdd-selected').offset()?.top - scrollTarget.offset().top + availableHeight;
465
466
 
@@ -472,10 +473,10 @@ export default function FilterableSelect(this: any, args: FilterableSelectArgs):
472
473
  currentContext.find('.paginator-container').attr('style', 'margin-top:20px;margin-bottom:10px;');
473
474
  openClickTime = TemporalUtils.dateNowMs();
474
475
 
475
- if (useIscroll) {
476
- contextWindow._filterableIscroll = (window as any).utils.bindIscroll($(contextBody).find('#filterableSelectDropWrap'));
477
- currentContext.find('.paginator-container').attr('style', 'margin-top:10px;margin-bottom:10px;');
478
- }
476
+ // if (useIscroll) {
477
+ // contextWindow._filterableIscroll = globalState.utils.bindIscroll($(contextBody).find('#filterableSelectDropWrap'));
478
+ // currentContext.find('.paginator-container').attr('style', 'margin-top:10px;margin-bottom:10px;');
479
+ // }
479
480
 
480
481
  $(contextBody).find('#btn_fdd_FddCancel').click(() => {
481
482
  this.hide();
@@ -1,3 +1,5 @@
1
+ import { globalState } from '../../../app/global-state';
2
+
1
3
  const $ = jQuery;
2
4
 
3
5
  export default function ListviewBuilder(this: any): void {
@@ -269,7 +271,7 @@ export default function ListviewBuilder(this: any): void {
269
271
  setTimeout(() => {
270
272
  currentContext.changePagination(newIndex);
271
273
  currentContext.buildRows();
272
- window.scrollTo(0, 0);
274
+ globalState.scrollTo(0, 0);
273
275
  self.targetElem.trigger('pagination-end', null);
274
276
  }, 200);
275
277
  };
@@ -1,4 +1,5 @@
1
1
  import { toNative } from 'vue-facing-decorator';
2
+ import { globalState } from '../../app/global-state';
2
3
  import PowerduckState from '../../app/powerduck-state';
3
4
  import TsxComponent, { Component } from '../../app/vuetsx';
4
5
  import { PortalUtils } from '../../common/utils/utils';
@@ -63,11 +64,11 @@ class FileDownloadDialogComponent extends TsxComponent<FileDownloadDialogBinding
63
64
  }
64
65
  };
65
66
 
66
- function innerDownload() {
67
+ const innerDownload = () => {
67
68
  const currentFile = fileArr.shift();
68
69
  if (currentFile != null) {
69
70
  let downloadMethod;
70
- if (window.fetch) {
71
+ if (globalState.fetch) {
71
72
  downloadMethod = mySelf.downloadUsingFetch;
72
73
  } else {
73
74
  downloadMethod = mySelf.downloadUsingXMLHTTP;
@@ -111,7 +112,7 @@ class FileDownloadDialogComponent extends TsxComponent<FileDownloadDialogBinding
111
112
  document.getElementById(randomId).click();
112
113
 
113
114
  setTimeout(() => {
114
- window.URL.revokeObjectURL(_OBJECT_URL);
115
+ globalState.URL.revokeObjectURL(_OBJECT_URL);
115
116
  document.body.removeChild(dummyLink);
116
117
  }, 5000);
117
118
 
@@ -134,7 +135,7 @@ class FileDownloadDialogComponent extends TsxComponent<FileDownloadDialogBinding
134
135
  },
135
136
  );
136
137
  }
137
- }
138
+ };
138
139
 
139
140
  mySelf.getModal().show({
140
141
  onShown() {
@@ -179,7 +180,7 @@ class FileDownloadDialogComponent extends TsxComponent<FileDownloadDialogBinding
179
180
  callback: (response: any) => void,
180
181
  error: () => void,
181
182
  ): void {
182
- const winFetch = window.fetch as any;
183
+ const winFetch = globalState.fetch as any;
183
184
  winFetch(file.url, {
184
185
  method: 'get',
185
186
  headers: {},
@@ -1,4 +1,5 @@
1
1
  import { Prop, toNative } from 'vue-facing-decorator';
2
+ import { globalState } from '../../app/global-state';
2
3
  import TsxComponent, { Component } from '../../app/vuetsx';
3
4
  import { isNullOrEmpty } from '../../common/utils/is-null-or-empty';
4
5
  import { PortalUtils } from '../../common/utils/utils';
@@ -39,8 +40,8 @@ class GoogleMapComponent extends TsxComponent<GoogleMapArgs> implements GoogleMa
39
40
  }
40
41
 
41
42
  getLocPoint() {
42
- if ((window as any).google?.maps?.LatLng) {
43
- return new (window as any).google.maps.LatLng(this.latitude as any, this.longitude as any);
43
+ if (globalState.google?.maps?.LatLng) {
44
+ return new globalState.google.maps.LatLng(this.latitude as any, this.longitude as any);
44
45
  }
45
46
 
46
47
  return {
@@ -50,8 +51,12 @@ class GoogleMapComponent extends TsxComponent<GoogleMapArgs> implements GoogleMa
50
51
  }
51
52
 
52
53
  mounted() {
54
+ // eslint-disable-next-line ts/no-this-alias
53
55
  const mySelf = this;
54
- (window as any).mapInst = mySelf;
56
+ globalState.mapInst = mySelf;
57
+ if (!globalState.windowExists) {
58
+ return;
59
+ }
55
60
 
56
61
  GoogleMapsApiLoader.load().then((result) => {
57
62
  if (result) {
@@ -65,12 +70,13 @@ class GoogleMapComponent extends TsxComponent<GoogleMapArgs> implements GoogleMa
65
70
  gmapArgs.zoom = 16;
66
71
  }
67
72
 
68
- mySelf.map = new (window as any).google.maps.Map(mySelf.$refs.mapWrapper as any, gmapArgs);
73
+ mySelf.map = new globalState.google.maps.Map(mySelf.$refs.mapWrapper as any, gmapArgs);
74
+ // eslint-disable-next-line no-useless-call
69
75
  mySelf.refreshMapItems.call(mySelf);
70
- (window as any).mapInstance = mySelf.map;
76
+ globalState.mapInstance = mySelf.map;
71
77
 
72
78
  if (this.initComplete != null) {
73
- this.initComplete(mySelf.map, (window as any).google.maps);
79
+ this.initComplete(mySelf.map, globalState.google.maps);
74
80
  }
75
81
  }
76
82
  });
@@ -89,6 +95,10 @@ class GoogleMapComponent extends TsxComponent<GoogleMapArgs> implements GoogleMa
89
95
  }
90
96
 
91
97
  refreshMapItems(args?: GoogleMapRefreshArgs) {
98
+ if (!globalState.windowExists) {
99
+ return;
100
+ }
101
+
92
102
  const map = this.map;
93
103
  const locPoint = this.getLocPoint();
94
104
  const newState = this.getCurrentState();
@@ -102,7 +112,7 @@ class GoogleMapComponent extends TsxComponent<GoogleMapArgs> implements GoogleMa
102
112
  }
103
113
 
104
114
  if (this.infoWindow == null && !isNullOrEmpty(this.name)) {
105
- this.infoWindow = new (window as any).google.maps.InfoWindow({
115
+ this.infoWindow = new globalState.google.maps.InfoWindow({
106
116
  content: '',
107
117
  });
108
118
  }
@@ -113,7 +123,7 @@ class GoogleMapComponent extends TsxComponent<GoogleMapArgs> implements GoogleMa
113
123
  }
114
124
 
115
125
  if (this.marker == null) {
116
- this.marker = new (window as any).google.maps.Marker({
126
+ this.marker = new globalState.google.maps.Marker({
117
127
  position: locPoint,
118
128
  title: PortalUtils.htmlEscape(this.name),
119
129
  visible: true,
@@ -1,6 +1,7 @@
1
1
  import type { DropdownButtonItemArgs } from '../dropdown-button/dropdown-button-item';
2
2
  import type { FormItemWrapperArgs, MarginType } from '../form/form-item-wrapper';
3
3
  import { Prop, toNative } from 'vue-facing-decorator';
4
+ import { globalState } from '../../app/global-state';
4
5
  import PowerduckState from '../../app/powerduck-state';
5
6
  import TsxComponent, { Component } from '../../app/vuetsx';
6
7
  import { AppHttpProvider } from '../../common/api-http';
@@ -304,7 +305,11 @@ class GooglePlacesAutocompleteWrapper {
304
305
  };
305
306
  }
306
307
 
307
- const autocomplete = new (window as any).google.maps.places.Autocomplete(args.element as any, acArgs);
308
+ if (!globalState.windowExists) {
309
+ return;
310
+ }
311
+
312
+ const autocomplete = new globalState.google.maps.places.Autocomplete(args.element as any, acArgs);
308
313
  autocomplete.addListener('place_changed', () => {
309
314
  const place = autocomplete.getPlace();
310
315
  const name = GooglePlacesUtils.getName(place, args.resultType);
@@ -1,3 +1,4 @@
1
+ import { globalState } from '../../../app/global-state';
1
2
  import PowerduckState from '../../../app/powerduck-state';
2
3
 
3
4
  export default class GoogleMapsApiLoader {
@@ -35,7 +36,7 @@ export default class GoogleMapsApiLoader {
35
36
  };
36
37
  script.onerror = function (e) {
37
38
  delete GoogleMapsApiLoader._loading;
38
- reject(false);
39
+ reject(e);
39
40
  };
40
41
  script.src = `https://maps.googleapis.com/maps/api/js?key=${PowerduckState.getGoogleMapsApiKey()}&libraries=places`;
41
42
  document.head.appendChild(script);
@@ -43,6 +44,6 @@ export default class GoogleMapsApiLoader {
43
44
  }
44
45
 
45
46
  private static apiLoaded(): boolean {
46
- return ((window as any).google && (window as any).google.maps && (window as any).google.maps.places && (window as any).google.maps.places.Autocomplete) != null;
47
+ return (globalState.google && globalState.google.maps && globalState.google.maps.places && globalState.google.maps.places.Autocomplete) != null;
47
48
  }
48
49
  }
@@ -1,4 +1,5 @@
1
1
  import { toNative } from 'vue-facing-decorator';
2
+ import { globalState } from '../../app/global-state';
2
3
  import PowerduckState from '../../app/powerduck-state';
3
4
  import TsxComponent, { Component } from '../../app/vuetsx';
4
5
  import { isNullOrEmpty } from '../../common/utils/is-null-or-empty';
@@ -72,8 +73,12 @@ class ImageCropModalComponent extends TsxComponent<ImageCropModalBindingArgs> im
72
73
  }
73
74
 
74
75
  public show(args: ImageCropModalShowArgs): void {
75
- if ((window as any).currentJCrop != null) {
76
- (window as any).currentJCrop.destroy();
76
+ if (!globalState.windowExists) {
77
+ return;
78
+ }
79
+
80
+ if (globalState.currentJCrop != null) {
81
+ globalState.currentJCrop.destroy();
77
82
  }
78
83
 
79
84
  this.blocked = true;
@@ -90,7 +95,7 @@ class ImageCropModalComponent extends TsxComponent<ImageCropModalBindingArgs> im
90
95
 
91
96
  (this.$refs.imageCropModal as typeof Modal.prototype).show();
92
97
 
93
- const containerWidth = $(window).width() - 62;
98
+ const containerWidth = $(globalState).width() - 62;
94
99
  let maxWidth = 780;
95
100
  if (containerWidth < maxWidth) {
96
101
  maxWidth = containerWidth;
@@ -114,7 +119,7 @@ class ImageCropModalComponent extends TsxComponent<ImageCropModalBindingArgs> im
114
119
  waitForLoad(() => {
115
120
  const height = img.height;
116
121
  const width = img.width;
117
- const maxHeight = $(window).height() - 140;
122
+ const maxHeight = $(globalState).height() - 140;
118
123
  const imgRatio = height / width;
119
124
  const possibleHeight = maxWidth * imgRatio;
120
125
 
@@ -139,7 +144,7 @@ class ImageCropModalComponent extends TsxComponent<ImageCropModalBindingArgs> im
139
144
  ($ as any).Jcrop.component.DragState.prototype.touch = false;
140
145
 
141
146
  (imgElem as any).Jcrop(cropParams, function (this: any) {
142
- (window as any).currentJCrop = this;
147
+ globalState.currentJCrop = this;
143
148
  mySelf.blocked = false;
144
149
  });
145
150
  });
@@ -326,7 +331,7 @@ class ImageCropModalComponent extends TsxComponent<ImageCropModalBindingArgs> im
326
331
  dataParam.TargetContainer = 'savethedate';
327
332
 
328
333
  try {
329
- const selectedBounds = (window as any).currentJCrop.getSelection();
334
+ const selectedBounds = globalState.currentJCrop.getSelection();
330
335
  dataParam.SelectionX = Math.round(selectedBounds.x);
331
336
  dataParam.SelectionY = Math.round(selectedBounds.y);
332
337
  dataParam.SelectionHeight = Math.round(selectedBounds.h);
@@ -2,187 +2,188 @@ 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 { globalState } from '../../app/global-state';
5
6
  import PowerduckState from '../../app/powerduck-state';
6
7
  import TsxComponent, { Component } from '../../app/vuetsx';
8
+ import { format } from '../../common/extensions/string-extensions';
7
9
  import { BrowserImageCompression } from '../../common/utils/broswer-image-compression';
8
10
  import { PortalUtils } from '../../common/utils/utils';
9
11
  import UploadButton from '../button/upload-button';
10
12
  import LoadingIndicator from '../loading-indicator';
11
13
  import NotificationProvider from '../ui/notification';
12
14
  import ImageCropModal from './image-cropping-modal';
13
- import { format } from '../../common/extensions/string-extensions';
14
15
 
15
16
  export interface UploadImageAndCropFileCompleteArgs {
16
- imageSrc: string;
17
- fileName: string;
17
+ imageSrc: string;
18
+ fileName: string;
18
19
  }
19
20
 
20
21
  export interface UploadImageAndCropUploadEventArgs extends ImageCroppedUploadArgs { }
21
22
 
22
23
  interface UploadImageAndCropButtonArgs extends _ButtonArgsBase {
23
- title?: string;
24
- aspectRatio?: number;
25
- useCropper?: boolean;
26
- uploadUrl?: string;
27
- targetBlobContainer: string;
28
- imageCropperTitleMessage?: string;
29
- uploadArgs: (e: UploadImageAndCropUploadEventArgs) => any;
30
- uploadComplete: (args: any) => void;
31
- autoCommit?: boolean;
32
- disableCompression?: boolean;
33
- allowedTypes?: string[];
34
- compressionMaxSizeMb?: number;
35
- isLoading?: boolean;
24
+ title?: string;
25
+ aspectRatio?: number;
26
+ useCropper?: boolean;
27
+ uploadUrl?: string;
28
+ targetBlobContainer: string;
29
+ imageCropperTitleMessage?: string;
30
+ uploadArgs: (e: UploadImageAndCropUploadEventArgs) => any;
31
+ uploadComplete: (args: any) => void;
32
+ autoCommit?: boolean;
33
+ disableCompression?: boolean;
34
+ allowedTypes?: string[];
35
+ compressionMaxSizeMb?: number;
36
+ isLoading?: boolean;
36
37
  }
37
38
 
38
39
  @Component
39
40
  class UploadImageAndCropButtonComponent extends TsxComponent<UploadImageAndCropButtonArgs> implements UploadImageAndCropButtonArgs {
40
- @Prop() cssClass!: string;
41
- @Prop() layout!: ButtonLayout;
42
- @Prop() text!: string;
43
- @Prop() size!: ButtonSize;
44
- @Prop() round!: boolean;
45
- @Prop() outlined!: boolean;
46
- @Prop() dismissModal!: boolean;
47
- @Prop() icon!: string;
48
- @Prop() disabled!: boolean;
49
- @Prop() useCropper!: boolean;
50
- @Prop() fullWidth!: boolean;
51
- @Prop() title!: string;
52
- @Prop() aspectRatio!: number;
53
- @Prop() uploadUrl: string;
54
- @Prop() isLoading?: boolean;
55
- @Prop() targetBlobContainer: string;
56
- @Prop() imageCropperTitleMessage!: string;
57
- @Prop() uploadArgs: (e: UploadImageAndCropUploadEventArgs) => any;
58
- @Prop() uploadComplete: (args: any) => void;
59
- @Prop() autoCommit: boolean;
60
- @Prop() disableCompression: boolean;
61
- @Prop() allowedTypes: string[];
62
- @Prop() compressionMaxSizeMb?: number;
63
- isInternalLoading: boolean = false;
64
-
65
- handleFileObtained(e: UploadButtonFileObtainedArgs): void {
66
- const urlCreator = window.URL || window.webkitURL;
67
- const imgUrl = urlCreator.createObjectURL(e.file);
68
-
69
- if (this.allowedTypes?.length > 0 && !this.allowedTypes.includes(e.file.type)) {
70
- NotificationProvider.showErrorMessage(PowerduckState.getResourceValue('uploadImageInvalidFileType')[format](PortalUtils.htmlEscape(this.allowedTypes.join(', '))));
71
- return;
72
- }
73
-
74
- if (this.useCropper != false) {
75
- (this.$refs.imgCrop as typeof ImageCropModal.prototype).show({
76
- fileName: e.file.name,
77
- imageSrc: imgUrl,
78
- aspectRatio: (this.aspectRatio || (5 / 3)),
79
- title: null,
80
- titleMessageHtml: this.imageCropperTitleMessage,
81
- handleCropUpload: (e) => {
82
- return this.getUploadPromise(e);
83
- },
84
- });
85
- } else {
86
- this.isInternalLoading = true;
87
- this.getUploadPromise({
88
- file: e.file,
89
- fileName: e.file.name,
90
- dataUrl: null,
91
- }).then(() => {
92
- this.isInternalLoading = false;
93
- }).catch((_err) => {
94
- this.isInternalLoading = false;
95
- });
96
- }
97
- }
98
-
99
- getUploadButton() {
100
- return this.$refs.uploadButton as typeof UploadButton.prototype;
101
- }
102
-
103
- async getUploadPromise(args: ImageCroppedUploadArgs): Promise<any> {
104
- if (!(this.disableCompression || args.file.type == 'image/svg+xml')) {
105
- args.file = await BrowserImageCompression.compress(args.file, {
106
- maxSizeMB: this.compressionMaxSizeMb,
107
- });
108
- }
109
-
110
- return new Promise((resolve, reject) => {
111
- const formData = new FormData();
112
- if (this.uploadArgs != null) {
113
- const argsVal = this.uploadArgs(args);
114
- if (argsVal != null) {
115
- for (const key in argsVal) {
116
- formData.append(key, argsVal[key]);
117
- }
118
- }
119
- }
120
-
121
- if (this.autoCommit === true) {
122
- const request = new XMLHttpRequest();
123
- request.open('POST', this.uploadUrl);
124
- request.onreadystatechange = () => {
125
- if (request.readyState == 4) {
126
- let response;
127
- try {
128
- response = JSON.parse(request.responseText);
129
- } catch (e) {
130
- response = request.responseText;
131
- }
132
-
133
- if (request.status == 200) {
134
- if (this.uploadComplete) {
135
- this.uploadComplete(response);
136
- }
137
-
138
- resolve(response);
139
- } else {
140
- try {
141
- if (response.responseText) {
142
- NotificationProvider.showErrorMessage(response.responseText);
143
- } else {
144
- NotificationProvider.showErrorMessage(response);
145
- }
146
- } catch (e) { }
147
-
148
- reject(response);
149
- }
150
- }
151
- };
152
-
153
- request.send(formData);
154
- } else {
155
- const argsVal = this.uploadArgs(args);
156
- this.uploadComplete(argsVal);
157
- resolve(formData);
158
- }
159
- });
160
- }
161
-
162
- render(h) {
163
- return (
164
- <span style="position:relative;">
165
- <LoadingIndicator visible={(this.isLoading || this.isInternalLoading)} />
166
- <UploadButton
167
- ref="uploadButton"
168
- fileObtained={async (e) => {
169
- await this.handleFileObtained(e);
170
- }}
171
- onlyImages={true}
172
- cssClass={this.cssClass}
173
- layout={this.layout}
174
- text={this.text}
175
- size={this.size}
176
- round={this.round}
177
- outlined={this.outlined}
178
- icon={this.icon}
179
- disabled={this.disabled}
180
- />
181
-
182
- <ImageCropModal ref="imgCrop" />
183
- </span>
184
- );
185
- }
41
+ @Prop() cssClass!: string;
42
+ @Prop() layout!: ButtonLayout;
43
+ @Prop() text!: string;
44
+ @Prop() size!: ButtonSize;
45
+ @Prop() round!: boolean;
46
+ @Prop() outlined!: boolean;
47
+ @Prop() dismissModal!: boolean;
48
+ @Prop() icon!: string;
49
+ @Prop() disabled!: boolean;
50
+ @Prop() useCropper!: boolean;
51
+ @Prop() fullWidth!: boolean;
52
+ @Prop() title!: string;
53
+ @Prop() aspectRatio!: number;
54
+ @Prop() uploadUrl: string;
55
+ @Prop() isLoading?: boolean;
56
+ @Prop() targetBlobContainer: string;
57
+ @Prop() imageCropperTitleMessage!: string;
58
+ @Prop() uploadArgs: (e: UploadImageAndCropUploadEventArgs) => any;
59
+ @Prop() uploadComplete: (args: any) => void;
60
+ @Prop() autoCommit: boolean;
61
+ @Prop() disableCompression: boolean;
62
+ @Prop() allowedTypes: string[];
63
+ @Prop() compressionMaxSizeMb?: number;
64
+ isInternalLoading: boolean = false;
65
+
66
+ handleFileObtained(e: UploadButtonFileObtainedArgs): void {
67
+ const urlCreator = globalState.URL || globalState.webkitURL;
68
+ const imgUrl = urlCreator.createObjectURL(e.file);
69
+
70
+ if (this.allowedTypes?.length > 0 && !this.allowedTypes.includes(e.file.type)) {
71
+ NotificationProvider.showErrorMessage(PowerduckState.getResourceValue('uploadImageInvalidFileType')[format](PortalUtils.htmlEscape(this.allowedTypes.join(', '))));
72
+ return;
73
+ }
74
+
75
+ if (this.useCropper != false) {
76
+ (this.$refs.imgCrop as typeof ImageCropModal.prototype).show({
77
+ fileName: e.file.name,
78
+ imageSrc: imgUrl,
79
+ aspectRatio: (this.aspectRatio || (5 / 3)),
80
+ title: null,
81
+ titleMessageHtml: this.imageCropperTitleMessage,
82
+ handleCropUpload: (e) => {
83
+ return this.getUploadPromise(e);
84
+ },
85
+ });
86
+ } else {
87
+ this.isInternalLoading = true;
88
+ this.getUploadPromise({
89
+ file: e.file,
90
+ fileName: e.file.name,
91
+ dataUrl: null,
92
+ }).then(() => {
93
+ this.isInternalLoading = false;
94
+ }).catch((_err) => {
95
+ this.isInternalLoading = false;
96
+ });
97
+ }
98
+ }
99
+
100
+ getUploadButton() {
101
+ return this.$refs.uploadButton as typeof UploadButton.prototype;
102
+ }
103
+
104
+ async getUploadPromise(args: ImageCroppedUploadArgs): Promise<any> {
105
+ if (!(this.disableCompression || args.file.type == 'image/svg+xml')) {
106
+ args.file = await BrowserImageCompression.compress(args.file, {
107
+ maxSizeMB: this.compressionMaxSizeMb,
108
+ });
109
+ }
110
+
111
+ return new Promise((resolve, reject) => {
112
+ const formData = new FormData();
113
+ if (this.uploadArgs != null) {
114
+ const argsVal = this.uploadArgs(args);
115
+ if (argsVal != null) {
116
+ for (const key in argsVal) {
117
+ formData.append(key, argsVal[key]);
118
+ }
119
+ }
120
+ }
121
+
122
+ if (this.autoCommit === true) {
123
+ const request = new XMLHttpRequest();
124
+ request.open('POST', this.uploadUrl);
125
+ request.onreadystatechange = () => {
126
+ if (request.readyState == 4) {
127
+ let response;
128
+ try {
129
+ response = JSON.parse(request.responseText);
130
+ } catch (e) {
131
+ response = request.responseText;
132
+ }
133
+
134
+ if (request.status == 200) {
135
+ if (this.uploadComplete) {
136
+ this.uploadComplete(response);
137
+ }
138
+
139
+ resolve(response);
140
+ } else {
141
+ try {
142
+ if (response.responseText) {
143
+ NotificationProvider.showErrorMessage(response.responseText);
144
+ } else {
145
+ NotificationProvider.showErrorMessage(response);
146
+ }
147
+ } catch (e) { }
148
+
149
+ reject(response);
150
+ }
151
+ }
152
+ };
153
+
154
+ request.send(formData);
155
+ } else {
156
+ const argsVal = this.uploadArgs(args);
157
+ this.uploadComplete(argsVal);
158
+ resolve(formData);
159
+ }
160
+ });
161
+ }
162
+
163
+ render(h) {
164
+ return (
165
+ <span style="position:relative;">
166
+ <LoadingIndicator visible={(this.isLoading || this.isInternalLoading)} />
167
+ <UploadButton
168
+ ref="uploadButton"
169
+ fileObtained={async (e) => {
170
+ await this.handleFileObtained(e);
171
+ }}
172
+ onlyImages={true}
173
+ cssClass={this.cssClass}
174
+ layout={this.layout}
175
+ text={this.text}
176
+ size={this.size}
177
+ round={this.round}
178
+ outlined={this.outlined}
179
+ icon={this.icon}
180
+ disabled={this.disabled}
181
+ />
182
+
183
+ <ImageCropModal ref="imgCrop" />
184
+ </span>
185
+ );
186
+ }
186
187
  }
187
188
 
188
189
  const UploadImageAndCropButton = toNative(UploadImageAndCropButtonComponent);
@@ -2,6 +2,7 @@ import type { DropdownButtonItemArgs } from '../dropdown-button/dropdown-button-
2
2
  import type { FormItemWrapperArgs, MarginType } from '../form/form-item-wrapper';
3
3
  import { Temporal } from '@js-temporal/polyfill';
4
4
  import { Prop, toNative } from 'vue-facing-decorator';
5
+ import { globalState } from '../../app/global-state';
5
6
  import PowerduckState from '../../app/powerduck-state';
6
7
  import TsxComponent, { Component } from '../../app/vuetsx';
7
8
  import { utcEpochMilliseconds } from '../../common/extensions/temporal-extensions';
@@ -159,6 +160,10 @@ class DaterangePickerComponent extends TsxComponent<DaterangePickerArgs> impleme
159
160
  }
160
161
 
161
162
  mounted() {
163
+ if (!globalState.windowExists) {
164
+ return;
165
+ }
166
+
162
167
  const self = this;
163
168
  const $elem = this.getInputElement();
164
169
  const now = Temporal.Now.plainDateTimeISO();
@@ -261,6 +266,10 @@ class DaterangePickerComponent extends TsxComponent<DaterangePickerArgs> impleme
261
266
  }
262
267
 
263
268
  beforeUpdate() {
269
+ if (!globalState.windowExists) {
270
+ return;
271
+ }
272
+
264
273
  this.handleModelValueChange();
265
274
  }
266
275