quasar-ui-danx 0.0.11 → 0.0.13
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.
- package/dist/index.css +5 -0
- package/dist/index.min.css +5 -0
- package/dist/index.rtl.css +5 -0
- package/dist/index.rtl.min.css +5 -0
- package/package.json +6 -1
- package/src/components/ActionTable/ActionTable.vue +49 -41
- package/src/components/ActionTable/BatchActionMenu.vue +20 -20
- package/src/components/ActionTable/EmptyTableState.vue +5 -5
- package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +11 -11
- package/src/components/ActionTable/Filters/FilterGroupItem.vue +7 -7
- package/src/components/ActionTable/Filters/FilterGroupList.vue +29 -29
- package/src/components/ActionTable/Filters/FilterListToggle.vue +15 -15
- package/src/components/ActionTable/Filters/FilterableField.vue +82 -80
- package/src/components/ActionTable/Filters/index.ts +5 -0
- package/src/components/ActionTable/Form/Fields/BooleanField.vue +13 -13
- package/src/components/ActionTable/Form/Fields/ConfirmPasswordField.vue +11 -11
- package/src/components/ActionTable/Form/Fields/DateField.vue +13 -13
- package/src/components/ActionTable/Form/Fields/DateRangeField.vue +25 -25
- package/src/components/ActionTable/Form/Fields/DateTimeField.vue +21 -21
- package/src/components/ActionTable/Form/Fields/DateTimePicker.vue +23 -23
- package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +31 -31
- package/src/components/ActionTable/Form/Fields/InlineDateTimeField.vue +19 -19
- package/src/components/ActionTable/Form/Fields/IntegerField.vue +7 -7
- package/src/components/ActionTable/Form/Fields/LabelValueBlock.vue +22 -0
- package/src/components/ActionTable/Form/Fields/LabeledInput.vue +19 -19
- package/src/components/ActionTable/Form/Fields/MultiFileField.vue +40 -40
- package/src/components/ActionTable/Form/Fields/MultiKeywordField.vue +23 -23
- package/src/components/ActionTable/Form/Fields/NewPasswordField.vue +10 -10
- package/src/components/ActionTable/Form/Fields/NumberField.vue +29 -29
- package/src/components/ActionTable/Form/Fields/NumberRangeField.vue +33 -33
- package/src/components/ActionTable/Form/Fields/SelectDrawer.vue +36 -36
- package/src/components/ActionTable/Form/Fields/SelectField.vue +66 -66
- package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +23 -23
- package/src/components/ActionTable/Form/Fields/SingleFileField.vue +32 -32
- package/src/components/ActionTable/Form/Fields/TextField.vue +36 -36
- package/src/components/ActionTable/Form/Fields/WysiwygField.vue +16 -16
- package/src/components/ActionTable/Form/Fields/index.ts +23 -23
- package/src/components/ActionTable/Form/RenderedForm.vue +27 -25
- package/src/components/ActionTable/Form/index.ts +2 -0
- package/src/components/ActionTable/TableSummaryRow.vue +33 -33
- package/src/components/ActionTable/index.ts +8 -13
- package/src/components/ActionTable/listActions.ts +340 -339
- package/src/components/ActionTable/listHelpers.ts +74 -0
- package/src/components/ActionTable/tableColumns.ts +56 -56
- package/src/components/DragAndDrop/HandleDraggable.vue +29 -29
- package/src/components/DragAndDrop/ListItemDraggable.vue +10 -10
- package/src/components/DragAndDrop/index.ts +0 -1
- package/src/components/DragAndDrop/listDragAndDrop.ts +1 -1
- package/src/components/Utility/CollapsableSidebar.vue +35 -35
- package/src/components/Utility/ContentDrawer.vue +20 -20
- package/src/components/Utility/Dialogs/ConfirmDialog.vue +55 -55
- package/src/components/Utility/Dialogs/FullScreenDialog.vue +18 -18
- package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +105 -0
- package/src/components/Utility/Dialogs/InfoDialog.vue +10 -10
- package/src/components/Utility/Dialogs/InputDialog.vue +13 -13
- package/src/components/Utility/ImagePreview.vue +192 -0
- package/src/components/Utility/Popover/PopoverMenu.vue +64 -0
- package/src/components/Utility/Transitions/StaggeredListTransition.vue +15 -15
- package/src/components/Utility/index.ts +11 -9
- package/src/components/index.ts +1 -1
- package/src/helpers/FileUpload.ts +274 -273
- package/src/helpers/compatibility.ts +45 -45
- package/src/helpers/date.ts +2 -2
- package/src/helpers/download.ts +166 -158
- package/src/helpers/downloadPdf.ts +48 -48
- package/src/helpers/files.ts +42 -42
- package/src/helpers/index.ts +2 -0
- package/src/helpers/multiFileUpload.ts +56 -56
- package/src/helpers/singleFileUpload.ts +49 -49
- package/src/index.esm.js +3 -4
- package/src/svg/FilterIcon.svg +7 -0
- package/src/svg/ImageIcon.svg +30 -0
- package/src/svg/PdfIcon.svg +21 -0
- package/src/svg/PercentIcon.svg +13 -0
- package/src/svg/TrashIcon.svg +15 -0
- package/src/svg/XIcon.svg +18 -0
- package/src/svg/index.ts +8 -0
- package/src/vendor/tinymce-config.ts +1 -0
- package/src/vue-plugin.js +7 -4
- package/tsconfig.json +18 -14
- package/src/components/ActionTable/tableHelpers.ts +0 -83
- package/src/components/DragAndDrop/Icons/index.ts +0 -2
- /package/src/{components/DragAndDrop/Icons → svg}/DragHandleDotsIcon.svg +0 -0
- /package/src/{components/DragAndDrop/Icons → svg}/DragHandleIcon.svg +0 -0
@@ -1,294 +1,295 @@
|
|
1
|
-
import { FlashMessages, resolveFileLocation } from "
|
1
|
+
import { FlashMessages, resolveFileLocation } from "@ui/helpers";
|
2
2
|
import { uid } from "quasar";
|
3
3
|
|
4
4
|
export type FileUploadOptions = {
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
directory: string,
|
6
|
+
presignedUploadUrl: (...params) => "",
|
7
|
+
uploadCompletedUrl: (...params) => "",
|
8
8
|
};
|
9
9
|
|
10
10
|
export class FileUpload {
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
};
|
22
|
-
|
23
|
-
constructor(files, options: FileUploadOptions) {
|
24
|
-
if (!Array.isArray(files) && !(files instanceof FileList)) {
|
25
|
-
files = [files];
|
26
|
-
}
|
27
|
-
this.files = files;
|
28
|
-
this.fileUploads = [];
|
29
|
-
this.onErrorCb = null;
|
30
|
-
this.onProgressCb = null;
|
31
|
-
this.onCompleteCb = null;
|
32
|
-
this.onAllCompleteCb = null;
|
33
|
-
|
34
|
-
this.options = {
|
35
|
-
...this.options,
|
36
|
-
...options
|
11
|
+
files: { id: string, blobUrl: string }[] = [];
|
12
|
+
fileUploads = [];
|
13
|
+
onErrorCb = null;
|
14
|
+
onProgressCb = null;
|
15
|
+
onCompleteCb = null;
|
16
|
+
onAllCompleteCb = null;
|
17
|
+
options: FileUploadOptions = {
|
18
|
+
directory: "file-upload",
|
19
|
+
presignedUploadUrl: null,
|
20
|
+
uploadCompletedUrl: null
|
37
21
|
};
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
// Prepare FormData
|
57
|
-
const formData = new FormData();
|
58
|
-
formData.append("file", file);
|
59
|
-
|
60
|
-
this.fileUploads.push({
|
61
|
-
file,
|
62
|
-
xhr: null, // NOTE: The XHR will be setup asynchronously right before sending file uploads
|
63
|
-
formData,
|
64
|
-
isComplete: false
|
65
|
-
});
|
22
|
+
|
23
|
+
constructor(files, options: FileUploadOptions) {
|
24
|
+
if (!Array.isArray(files) && !(files instanceof FileList)) {
|
25
|
+
files = [files];
|
26
|
+
}
|
27
|
+
this.files = files;
|
28
|
+
this.fileUploads = [];
|
29
|
+
this.onErrorCb = null;
|
30
|
+
this.onProgressCb = null;
|
31
|
+
this.onCompleteCb = null;
|
32
|
+
this.onAllCompleteCb = null;
|
33
|
+
|
34
|
+
this.options = {
|
35
|
+
...this.options,
|
36
|
+
...options
|
37
|
+
};
|
38
|
+
this.prepare();
|
66
39
|
}
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
onError(cb) {
|
103
|
-
this.onErrorCb = cb;
|
104
|
-
return this;
|
105
|
-
}
|
106
|
-
|
107
|
-
/**
|
108
|
-
* Handles the error events / fires the callback if it is set
|
109
|
-
* @param e
|
110
|
-
* @param file
|
111
|
-
* @param error
|
112
|
-
*/
|
113
|
-
errorHandler(e, file, error = null) {
|
114
|
-
if (this.onErrorCb) {
|
115
|
-
this.onErrorCb({ e, file, error });
|
40
|
+
|
41
|
+
/**
|
42
|
+
* Prepares all files for upload and provides an id and blobUrl for each file
|
43
|
+
*/
|
44
|
+
prepare() {
|
45
|
+
// Prepare required attributes
|
46
|
+
for (const file of this.files) {
|
47
|
+
if (!(file instanceof File)) {
|
48
|
+
throw Error(
|
49
|
+
"FileUpload constructor requires a File object or a list of File objects"
|
50
|
+
);
|
51
|
+
}
|
52
|
+
|
53
|
+
file.id = uid();
|
54
|
+
file.blobUrl = URL.createObjectURL(file);
|
55
|
+
|
56
|
+
// Prepare FormData
|
57
|
+
const formData = new FormData();
|
58
|
+
formData.append("file", file);
|
59
|
+
|
60
|
+
this.fileUploads.push({
|
61
|
+
file,
|
62
|
+
xhr: null, // NOTE: The XHR will be setup asynchronously right before sending file uploads
|
63
|
+
formData,
|
64
|
+
isComplete: false
|
65
|
+
});
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
/**
|
70
|
+
* Callback for when all files have been uploaded
|
71
|
+
*/
|
72
|
+
onAllComplete(cb) {
|
73
|
+
this.onAllCompleteCb = cb;
|
74
|
+
return this;
|
116
75
|
}
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
fileUpload.file,
|
127
|
-
waitMessage
|
128
|
-
);
|
129
|
-
fileUpload.formData.append(
|
130
|
-
"meta",
|
131
|
-
JSON.stringify(fileUpload.file.location)
|
132
|
-
);
|
76
|
+
|
77
|
+
/**
|
78
|
+
* Callback fired once for each file upon successful completion of upload
|
79
|
+
* @param cb
|
80
|
+
* @returns {FileUpload}
|
81
|
+
*/
|
82
|
+
onComplete(cb) {
|
83
|
+
this.onCompleteCb = cb;
|
84
|
+
return this;
|
133
85
|
}
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
fileUpload.file.progress = progress;
|
144
|
-
this.onProgressCb && this.onProgressCb({ file: this.wrapFile(fileUpload.file), progress });
|
145
|
-
}
|
146
|
-
|
147
|
-
/**
|
148
|
-
* Fires the complete callback
|
149
|
-
* @param fileUpload
|
150
|
-
* @param uploadedFile
|
151
|
-
*/
|
152
|
-
fireCompleteCallback(fileUpload, uploadedFile) {
|
153
|
-
fileUpload.isComplete = true;
|
154
|
-
fileUpload.file.progress = 1;
|
155
|
-
this.onCompleteCb && this.onCompleteCb({ file: this.wrapFile(fileUpload.file), uploadedFile });
|
156
|
-
}
|
157
|
-
|
158
|
-
/**
|
159
|
-
* Check if all files have been uploaded and call the callback if they have
|
160
|
-
*/
|
161
|
-
checkAllComplete() {
|
162
|
-
if (this.onAllCompleteCb) {
|
163
|
-
if (this.fileUploads.every((fileUpload) => fileUpload.isComplete)) {
|
164
|
-
this.onAllCompleteCb({ files: this.fileUploads });
|
165
|
-
}
|
86
|
+
|
87
|
+
/**
|
88
|
+
* Callback fired each time there is an upload progress update for a file
|
89
|
+
* @param cb
|
90
|
+
* @returns {FileUpload}
|
91
|
+
*/
|
92
|
+
onProgress(cb) {
|
93
|
+
this.onProgressCb = cb;
|
94
|
+
return this;
|
166
95
|
}
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
return {
|
177
|
-
id: file.id,
|
178
|
-
name: file.name,
|
179
|
-
size: file.size,
|
180
|
-
type: file.type,
|
181
|
-
progress: file.progress,
|
182
|
-
location: file.location,
|
183
|
-
blobUrl: file.blobUrl
|
184
|
-
};
|
185
|
-
}
|
186
|
-
|
187
|
-
/**
|
188
|
-
* Registers all the callbacks requested for the XHR / post-processing of file uploads
|
189
|
-
*/
|
190
|
-
setXhrCallbacks() {
|
191
|
-
// Set the error callbacks
|
192
|
-
for (const fileUpload of this.fileUploads) {
|
193
|
-
fileUpload.xhr.addEventListener(
|
194
|
-
"error",
|
195
|
-
(e) => this.errorHandler(e, fileUpload.file),
|
196
|
-
false
|
197
|
-
);
|
96
|
+
|
97
|
+
/**
|
98
|
+
* Callback fired when an error occurs during upload
|
99
|
+
* @param cb
|
100
|
+
* @returns {FileUpload}
|
101
|
+
*/
|
102
|
+
onError(cb) {
|
103
|
+
this.onErrorCb = cb;
|
104
|
+
return this;
|
198
105
|
}
|
199
106
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
this.
|
209
|
-
|
210
|
-
false
|
211
|
-
);
|
212
|
-
}
|
107
|
+
/**
|
108
|
+
* Handles the error events / fires the callback if it is set
|
109
|
+
* @param e
|
110
|
+
* @param file
|
111
|
+
* @param error
|
112
|
+
*/
|
113
|
+
errorHandler(e, file, error = null) {
|
114
|
+
if (this.onErrorCb) {
|
115
|
+
this.onErrorCb({ e, file, error });
|
116
|
+
}
|
213
117
|
}
|
214
118
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
false
|
232
|
-
);
|
119
|
+
/**
|
120
|
+
* Resolve the locations of all the files
|
121
|
+
* @returns {Promise<FileUpload>}
|
122
|
+
*/
|
123
|
+
async resolveLocation(waitMessage = null) {
|
124
|
+
for (const fileUpload of this.fileUploads) {
|
125
|
+
fileUpload.file.location = await resolveFileLocation(
|
126
|
+
fileUpload.file,
|
127
|
+
waitMessage
|
128
|
+
);
|
129
|
+
fileUpload.formData.append(
|
130
|
+
"meta",
|
131
|
+
JSON.stringify(fileUpload.file.location)
|
132
|
+
);
|
133
|
+
}
|
134
|
+
return this;
|
233
135
|
}
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
this.fireProgressCallback(fileUpload, .95);
|
244
|
-
|
245
|
-
// Let the platform know the presigned upload is complete
|
246
|
-
return await fetch(this.options.uploadCompletedUrl(fileUpload.file.resource_id), { method: "POST" }).then(r => r.json());
|
247
|
-
}
|
248
|
-
|
249
|
-
/**
|
250
|
-
* Start uploading all files
|
251
|
-
*/
|
252
|
-
async upload() {
|
253
|
-
for (const fileUpload of this.fileUploads) {
|
254
|
-
const mimeType = fileUpload.file.mimeType || fileUpload.file.type;
|
255
|
-
const presignedUrl = this.options.presignedUploadUrl(this.options.directory, fileUpload.file.name, mimeType);
|
256
|
-
|
257
|
-
// Fetch presigned upload URL
|
258
|
-
const fileResource = await fetch(presignedUrl).then(r => r.json());
|
259
|
-
|
260
|
-
if (!fileResource.url) {
|
261
|
-
FlashMessages.error("Could not fetch presigned upload URL for file " + fileUpload.file.name);
|
262
|
-
continue;
|
263
|
-
}
|
264
|
-
|
265
|
-
const isS3Upload = !fileResource.url.match("upload-presigned-url-contents");
|
266
|
-
|
267
|
-
// We need the file resource ID to complete the presigned upload
|
268
|
-
fileUpload.file.resource_id = fileResource.id;
|
269
|
-
|
270
|
-
// Prepare XHR request
|
271
|
-
const xhr = new XMLHttpRequest();
|
272
|
-
|
273
|
-
// The XHR request is different based on weather we're sending to S3 or the platform server
|
274
|
-
if (isS3Upload) {
|
275
|
-
xhr.open("PUT", fileResource.url);
|
276
|
-
xhr.setRequestHeader("Content-Type", mimeType);
|
277
|
-
fileUpload.body = fileUpload.file;
|
278
|
-
} else {
|
279
|
-
xhr.open("POST", fileResource.url);
|
280
|
-
fileUpload.body = fileUpload.formData;
|
281
|
-
}
|
282
|
-
|
283
|
-
fileUpload.xhr = xhr;
|
136
|
+
|
137
|
+
/**
|
138
|
+
* Fires the progress callback
|
139
|
+
* @param fileUpload
|
140
|
+
* @param progress
|
141
|
+
*/
|
142
|
+
fireProgressCallback(fileUpload, progress) {
|
143
|
+
fileUpload.file.progress = progress;
|
144
|
+
this.onProgressCb && this.onProgressCb({ file: this.wrapFile(fileUpload.file), progress });
|
284
145
|
}
|
285
146
|
|
286
|
-
|
287
|
-
|
147
|
+
/**
|
148
|
+
* Fires the complete callback
|
149
|
+
* @param fileUpload
|
150
|
+
* @param uploadedFile
|
151
|
+
*/
|
152
|
+
fireCompleteCallback(fileUpload, uploadedFile) {
|
153
|
+
fileUpload.isComplete = true;
|
154
|
+
fileUpload.file.progress = 1;
|
155
|
+
this.onCompleteCb && this.onCompleteCb({ file: this.wrapFile(fileUpload.file), uploadedFile });
|
156
|
+
}
|
157
|
+
|
158
|
+
/**
|
159
|
+
* Check if all files have been uploaded and call the callback if they have
|
160
|
+
*/
|
161
|
+
checkAllComplete() {
|
162
|
+
if (this.onAllCompleteCb) {
|
163
|
+
if (this.fileUploads.every((fileUpload) => fileUpload.isComplete)) {
|
164
|
+
this.onAllCompleteCb({ files: this.fileUploads });
|
165
|
+
}
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
/**
|
170
|
+
* Returns a native JS object that is easier to work with than the File objects (no weird behavior of missing
|
171
|
+
* properties, easily printable, etc.)
|
172
|
+
* @param file
|
173
|
+
* @returns {{size, name, progress, location, blobUrl: *, id, type}}
|
174
|
+
*/
|
175
|
+
wrapFile(file) {
|
176
|
+
return {
|
177
|
+
id: file.id,
|
178
|
+
name: file.name,
|
179
|
+
size: file.size,
|
180
|
+
type: file.type,
|
181
|
+
progress: file.progress,
|
182
|
+
location: file.location,
|
183
|
+
blobUrl: file.blobUrl
|
184
|
+
};
|
185
|
+
}
|
186
|
+
|
187
|
+
/**
|
188
|
+
* Registers all the callbacks requested for the XHR / post-processing of file uploads
|
189
|
+
*/
|
190
|
+
setXhrCallbacks() {
|
191
|
+
// Set the error callbacks
|
192
|
+
for (const fileUpload of this.fileUploads) {
|
193
|
+
fileUpload.xhr.addEventListener(
|
194
|
+
"error",
|
195
|
+
(e) => this.errorHandler(e, fileUpload.file),
|
196
|
+
false
|
197
|
+
);
|
198
|
+
}
|
199
|
+
|
200
|
+
// Set the progress callbacks
|
201
|
+
if (this.onProgressCb) {
|
202
|
+
for (const fileUpload of this.fileUploads) {
|
203
|
+
fileUpload.xhr.upload.addEventListener(
|
204
|
+
"progress",
|
205
|
+
(e) => {
|
206
|
+
// Max of 95%, so we can indicate we are completing the signed URL process
|
207
|
+
const progress = Math.min(.95, e.loaded / e.total);
|
208
|
+
this.fireProgressCallback(fileUpload, progress);
|
209
|
+
},
|
210
|
+
false
|
211
|
+
);
|
212
|
+
}
|
213
|
+
}
|
214
|
+
|
215
|
+
// Set the load callbacks which registers the Complete / All Complete callbacks and handles non-xhr related
|
216
|
+
// errors
|
217
|
+
for (const fileUpload of this.fileUploads) {
|
218
|
+
fileUpload.xhr.addEventListener(
|
219
|
+
"load",
|
220
|
+
async (e) => {
|
221
|
+
try {
|
222
|
+
// First complete the presigned upload to get the updated file resource data
|
223
|
+
const uploadedFile = await this.completePresignedUpload(fileUpload);
|
224
|
+
|
225
|
+
// Fire the file complete callbacks
|
226
|
+
this.fireCompleteCallback(fileUpload, uploadedFile);
|
227
|
+
this.checkAllComplete();
|
228
|
+
} catch (error) {
|
229
|
+
this.errorHandler(e, fileUpload.file, error);
|
230
|
+
}
|
231
|
+
},
|
232
|
+
false
|
233
|
+
);
|
234
|
+
}
|
235
|
+
}
|
236
|
+
|
237
|
+
/**
|
238
|
+
* Mark the presigned upload as completed and return the file resource from the platform server
|
239
|
+
* @param fileUpload
|
240
|
+
* @returns {Promise<void>}
|
241
|
+
*/
|
242
|
+
async completePresignedUpload(fileUpload) {
|
243
|
+
// Show 95% as the last 5% will be to complete the presigned upload
|
244
|
+
this.fireProgressCallback(fileUpload, .95);
|
245
|
+
|
246
|
+
// Let the platform know the presigned upload is complete
|
247
|
+
return await fetch(this.options.uploadCompletedUrl(fileUpload.file.resource_id), { method: "POST" }).then(r => r.json());
|
248
|
+
}
|
288
249
|
|
289
|
-
|
290
|
-
|
291
|
-
|
250
|
+
/**
|
251
|
+
* Start uploading all files
|
252
|
+
*/
|
253
|
+
async upload() {
|
254
|
+
for (const fileUpload of this.fileUploads) {
|
255
|
+
const mimeType = fileUpload.file.mimeType || fileUpload.file.type;
|
256
|
+
const presignedUrl = this.options.presignedUploadUrl(this.options.directory, fileUpload.file.name, mimeType);
|
257
|
+
|
258
|
+
// Fetch presigned upload URL
|
259
|
+
const fileResource = await fetch(presignedUrl).then(r => r.json());
|
260
|
+
|
261
|
+
if (!fileResource.url) {
|
262
|
+
FlashMessages.error("Could not fetch presigned upload URL for file " + fileUpload.file.name);
|
263
|
+
continue;
|
264
|
+
}
|
265
|
+
|
266
|
+
const isS3Upload = !fileResource.url.match("upload-presigned-url-contents");
|
267
|
+
|
268
|
+
// We need the file resource ID to complete the presigned upload
|
269
|
+
fileUpload.file.resource_id = fileResource.id;
|
270
|
+
|
271
|
+
// Prepare XHR request
|
272
|
+
const xhr = new XMLHttpRequest();
|
273
|
+
|
274
|
+
// The XHR request is different based on weather we're sending to S3 or the platform server
|
275
|
+
if (isS3Upload) {
|
276
|
+
xhr.open("PUT", fileResource.url);
|
277
|
+
xhr.setRequestHeader("Content-Type", mimeType);
|
278
|
+
fileUpload.body = fileUpload.file;
|
279
|
+
} else {
|
280
|
+
xhr.open("POST", fileResource.url);
|
281
|
+
fileUpload.body = fileUpload.formData;
|
282
|
+
}
|
283
|
+
|
284
|
+
fileUpload.xhr = xhr;
|
285
|
+
}
|
286
|
+
|
287
|
+
// Set all the callbacks on the XHR requests
|
288
|
+
this.setXhrCallbacks();
|
289
|
+
|
290
|
+
// Send all the XHR file uploads
|
291
|
+
for (const fileUpload of this.fileUploads) {
|
292
|
+
fileUpload.xhr.send(fileUpload.body);
|
293
|
+
}
|
292
294
|
}
|
293
|
-
}
|
294
295
|
}
|
@@ -1,5 +1,5 @@
|
|
1
|
+
import { sleep } from "@ui/helpers";
|
1
2
|
import { useGeolocation } from "@vueuse/core";
|
2
|
-
import { sleep } from "quasar-ui-danx/src/helpers";
|
3
3
|
import { computed } from "vue";
|
4
4
|
|
5
5
|
let isLoaded = false;
|
@@ -9,56 +9,56 @@ let hasLocation = null;
|
|
9
9
|
let geolocation = null;
|
10
10
|
|
11
11
|
export function useCompatibility(requestLocation = true) {
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
if (!isLoaded && requestLocation) {
|
13
|
+
const { coords, error, locatedAt } = useGeolocation();
|
14
|
+
geolocationError = error;
|
15
|
+
hasLocation = locatedAt;
|
16
|
+
geolocation = coords;
|
17
|
+
isLoaded = true;
|
18
|
+
}
|
19
19
|
|
20
|
-
|
20
|
+
const isLocationSupported = "geolocation" in navigator;
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
22
|
+
const location = computed(() => {
|
23
|
+
if (hasLocation?.value) {
|
24
|
+
return geolocation?.value;
|
25
|
+
}
|
26
|
+
return null;
|
27
|
+
});
|
28
28
|
|
29
|
-
|
29
|
+
const isCompatible = computed(() => !geolocationError?.value && !!hasLocation?.value);
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
31
|
+
/**
|
32
|
+
* Wait for location to be available and returns the location when it is or null after the wait period times out.
|
33
|
+
* @param maxWait
|
34
|
+
*/
|
35
|
+
const waitForLocation = async (maxWait = 3000) => {
|
36
|
+
// We only should wait once, if we already waited and failed, its unlikely the location will be available at a
|
37
|
+
// later time
|
38
|
+
if (hasAlreadyWaited) {
|
39
|
+
return location;
|
40
|
+
}
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
hasAlreadyWaited = true;
|
43
|
+
let waitTime = 0;
|
44
|
+
while (!location.value) {
|
45
|
+
await sleep(100);
|
46
|
+
waitTime += 100;
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
if (waitTime > maxWait) {
|
49
|
+
return null;
|
50
|
+
}
|
51
|
+
}
|
52
52
|
|
53
|
-
|
54
|
-
|
53
|
+
return location;
|
54
|
+
};
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
56
|
+
return {
|
57
|
+
isLocationSupported,
|
58
|
+
isCompatible,
|
59
|
+
geolocationError,
|
60
|
+
hasLocation,
|
61
|
+
location,
|
62
|
+
waitForLocation
|
63
|
+
};
|
64
64
|
}
|
package/src/helpers/date.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { parseDateTime } from "
|
1
|
+
import { parseDateTime } from "@ui/helpers/formats";
|
2
2
|
|
3
3
|
export function diffInDays(date1, date2) {
|
4
|
-
|
4
|
+
return parseDateTime(date2).diff(parseDateTime(date1), ["days"]).days;
|
5
5
|
}
|