@xuda.io/xuda-widget-plugin-xuda-drive 1.0.143 → 1.0.144
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.mjs +5 -0
- package/dist/runtime.mjs +11004 -11035
- package/package.json +1 -1
- package/src/index.mjs +5 -0
- package/src/runtime.mjs +233 -353
package/src/runtime.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import 'filepond/dist/filepond.min.css';
|
|
|
3
3
|
|
|
4
4
|
window.FilePond = FilePond;
|
|
5
5
|
|
|
6
|
+
|
|
6
7
|
import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type';
|
|
7
8
|
|
|
8
9
|
import * as pintura from './vendors/pintura/pintura/pintura.js';
|
|
@@ -26,6 +27,11 @@ import * as fs from './vendors/fs-lightbox.js';
|
|
|
26
27
|
import { useDropZone } from '@vueuse/core';
|
|
27
28
|
|
|
28
29
|
export async function upload(fields, e) {
|
|
30
|
+
const container = e.container_node;
|
|
31
|
+
if (!container) {
|
|
32
|
+
e.report_error?.('xuda-drive: upload skipped — host provided no container_node (element not yet in DOM, e.g. a collapsed section)', true);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
29
35
|
return new Promise(async (resolve, reject) => {
|
|
30
36
|
var overrideAction = 'replace';
|
|
31
37
|
var existingFiles = {};
|
|
@@ -39,7 +45,7 @@ export async function upload(fields, e) {
|
|
|
39
45
|
var pond = undefined;
|
|
40
46
|
|
|
41
47
|
var init_files_fn = (files) => {
|
|
42
|
-
|
|
48
|
+
Object.entries(files || {}).forEach(function ([key, val]) {
|
|
43
49
|
if (val?.server_fileName) val = val.server_fileName;
|
|
44
50
|
val = func.utils.get_drive_url(e.SESSION_ID, val).value;
|
|
45
51
|
|
|
@@ -119,12 +125,15 @@ export async function upload(fields, e) {
|
|
|
119
125
|
}, 100);
|
|
120
126
|
};
|
|
121
127
|
|
|
122
|
-
var $pallet_wrapper = $(`<div class="image_wrapper" style="border: none !important;">`);
|
|
123
|
-
|
|
124
128
|
var input_id = new Date().valueOf();
|
|
125
|
-
var
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
var pallet_wrapper = document.createElement('div');
|
|
130
|
+
pallet_wrapper.className = 'image_wrapper';
|
|
131
|
+
pallet_wrapper.style.setProperty('border', 'none', 'important');
|
|
132
|
+
var imgInp = document.createElement('input');
|
|
133
|
+
imgInp.className = `filepond ${widget_cssClass || ''}`.trim();
|
|
134
|
+
imgInp.id = String(input_id);
|
|
135
|
+
pallet_wrapper.appendChild(imgInp);
|
|
136
|
+
container.replaceChildren(pallet_wrapper);
|
|
128
137
|
|
|
129
138
|
FilePond.registerPlugin(FilePondPluginImageEditor);
|
|
130
139
|
FilePond.registerPlugin(FilePondPluginFileValidateType);
|
|
@@ -144,15 +153,15 @@ export async function upload(fields, e) {
|
|
|
144
153
|
};
|
|
145
154
|
|
|
146
155
|
const axios_ajax = function (url, data) {
|
|
147
|
-
|
|
156
|
+
const body = new URLSearchParams();
|
|
148
157
|
|
|
149
|
-
|
|
150
|
-
|
|
158
|
+
Object.entries(data || {}).forEach(function ([key, val]) {
|
|
159
|
+
body.append(key, val);
|
|
151
160
|
});
|
|
152
161
|
|
|
153
162
|
fetch(url, {
|
|
154
163
|
method: 'POST',
|
|
155
|
-
body
|
|
164
|
+
body,
|
|
156
165
|
}).then((res) => {
|
|
157
166
|
console.log('Request complete! response:', res);
|
|
158
167
|
});
|
|
@@ -162,7 +171,7 @@ export async function upload(fields, e) {
|
|
|
162
171
|
allowProcess: true,
|
|
163
172
|
allowRemove: true,
|
|
164
173
|
allowRevert: true,
|
|
165
|
-
maxParallelUploads:
|
|
174
|
+
maxParallelUploads: 25,
|
|
166
175
|
|
|
167
176
|
imageCropAspectRatio: 1,
|
|
168
177
|
imageResizeTargetWidth: 200,
|
|
@@ -407,7 +416,7 @@ export async function upload(fields, e) {
|
|
|
407
416
|
opt.styleButtonProcessItemPosition = 'right bottom';
|
|
408
417
|
}
|
|
409
418
|
|
|
410
|
-
pond = FilePond.create(
|
|
419
|
+
pond = FilePond.create(imgInp, opt);
|
|
411
420
|
|
|
412
421
|
pond.on('processfiles', (e) => {
|
|
413
422
|
do_callback();
|
|
@@ -433,6 +442,10 @@ export async function upload(fields, e) {
|
|
|
433
442
|
|
|
434
443
|
export async function viewer(fields, e) {
|
|
435
444
|
const { app_id, gtp_token, app_token, api_key } = e._session;
|
|
445
|
+
if (!e.container_node) {
|
|
446
|
+
e.report_error?.('xuda-drive: viewer skipped — host provided no container_node (element not yet in DOM, e.g. a collapsed section)', true);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
436
449
|
|
|
437
450
|
var sepcialLoader = {
|
|
438
451
|
template: `
|
|
@@ -633,7 +646,7 @@ export async function viewer(fields, e) {
|
|
|
633
646
|
|
|
634
647
|
<div>
|
|
635
648
|
<button
|
|
636
|
-
@click="
|
|
649
|
+
@click="uploadModal = true"
|
|
637
650
|
type="button"
|
|
638
651
|
class="inline-flex items-center justify-center px-4 py-2 text-sm font-medium tracking-wide text-white transition-colors duration-200 rounded-md bg-neutral-950 hover:bg-neutral-900 focus:ring-2 focus:ring-offset-2 focus:ring-neutral-900 focus:shadow-outline focus:outline-none"
|
|
639
652
|
>
|
|
@@ -751,137 +764,129 @@ export async function viewer(fields, e) {
|
|
|
751
764
|
</div>
|
|
752
765
|
</div>
|
|
753
766
|
|
|
754
|
-
<!-- Hidden native file input -->
|
|
755
|
-
<input type="file" ref="fileInput" :multiple="options.file_upload_allow_multiple_files" :accept="acceptTypes" class="hidden" @change="onFilesSelected" />
|
|
756
|
-
|
|
757
|
-
<!-- Consent override modal -->
|
|
758
767
|
<teleport to="body">
|
|
759
|
-
<div @keydown.escape.window="
|
|
760
|
-
<div v-show="
|
|
768
|
+
<div @keydown.escape.window="uploadModal = false" v-show="uploadModal" class="fixed top-0 left-0 z-[99] w-screen h-screen" x-cloak>
|
|
769
|
+
<div v-show="uploadModal" @click="uploadModal=false" class="absolute inset-0 w-full h-full bg-white backdrop-blur-sm bg-opacity-70"></div>
|
|
770
|
+
|
|
761
771
|
<transition key="wrapper" enter-from-class="opacity-0" enter-active-class="ease-out duration-300" enter-to-class="opacity-100" leave-from-class="opacity-100" leave-active-class="ease-in duration-300" leave-to-class="opacity-0">
|
|
762
|
-
<div class="flex items-center justify-center size-full" v-show="
|
|
763
|
-
<transition
|
|
764
|
-
|
|
772
|
+
<div class="flex items-center justify-center size-full" v-show="uploadModal">
|
|
773
|
+
<transition
|
|
774
|
+
key="inner"
|
|
775
|
+
enter-active-class="ease-out duration-300"
|
|
776
|
+
enter-from-class="opacity-0 -translate-y-2 sm:scale-95"
|
|
777
|
+
enter-to-class="opacity-100 translate-y-0 sm:scale-100"
|
|
778
|
+
leave-active-class="ease-in duration-200"
|
|
779
|
+
leave-from-class="opacity-100 translate-y-0 sm:scale-100"
|
|
780
|
+
leave-to-class="opacity-0 -translate-y-2 sm:scale-95"
|
|
781
|
+
>
|
|
782
|
+
<div v-show="uploadModal" class="relative w-full py-6 bg-white border shadow-lg px-7 border-neutral-200 sm:max-w-lg sm:rounded-lg overflow-hidden">
|
|
765
783
|
<div class="flex items-center justify-between pb-3">
|
|
766
|
-
<h3 class="text-lg font-semibold"
|
|
767
|
-
<button @click="
|
|
784
|
+
<h3 class="text-lg font-semibold" v-text="'Upload file' + (options.file_upload_allow_multiple_files ? 's' : '')"></h3>
|
|
785
|
+
<button @click="uploadModal=false" class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 text-gray-600 rounded-full hover:text-gray-800 hover:bg-gray-50">
|
|
768
786
|
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
|
|
769
787
|
</button>
|
|
770
788
|
</div>
|
|
771
|
-
<div class="
|
|
772
|
-
|
|
773
|
-
<
|
|
774
|
-
<div class="mt-6 space-y-5 pl-2">
|
|
775
|
-
<div v-for="option in replaceOptions" :key="option.value">
|
|
776
|
-
<label :for="option.value" class="flex items-center cursor-pointer">
|
|
777
|
-
<input :id="option.value" name="notification-method" type="radio" :value="option.value" v-model="overrideAction" :checked="option.value === overrideAction" class="option-radio peer" />
|
|
778
|
-
<div class="ml-3 block font-medium peer-checked:text-black text-light-700 cursor-pointer">{{ option.label }}</div>
|
|
779
|
-
</label>
|
|
780
|
-
</div>
|
|
781
|
-
</div>
|
|
782
|
-
</fieldset>
|
|
783
|
-
</div>
|
|
784
|
-
<div class="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 mt-6">
|
|
785
|
-
<button @click="showExistModal=false" type="button" class="inline-flex gap-2 items-center justify-center h-10 px-4 py-2 text-sm font-medium transition-colors border rounded-md focus:outline-none focus:ring-2 focus:ring-neutral-100 focus:ring-offset-2">
|
|
789
|
+
<div id="imageUploaderComponent" class="relative w-auto pb-8 h-48 [&>div]:h-full [&>div>div]:h-full"></div>
|
|
790
|
+
<div class="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2">
|
|
791
|
+
<button @click="uploadModal=false" type="button" class="inline-flex gap-2 items-center justify-center h-10 px-4 py-2 text-sm font-medium transition-colors border rounded-md focus:outline-none focus:ring-2 focus:ring-neutral-100 focus:ring-offset-2">
|
|
786
792
|
<div>Dismiss</div>
|
|
787
793
|
</button>
|
|
788
|
-
<button
|
|
789
|
-
|
|
794
|
+
<button
|
|
795
|
+
@click="uploadFiles(false)"
|
|
796
|
+
:disabled="uploaderLoading || checkingFiles"
|
|
797
|
+
type="button"
|
|
798
|
+
class="inline-flex items-center gap-2 justify-center h-10 px-4 py-2 text-sm font-medium text-white transition-colors border border-transparent rounded-md focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:ring-offset-2 bg-neutral-950 hover:bg-neutral-900 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
799
|
+
>
|
|
800
|
+
<template v-if="checkingFiles">
|
|
801
|
+
<sepcial-loader class="size-5" />
|
|
802
|
+
Checking files...
|
|
803
|
+
</template>
|
|
804
|
+
|
|
805
|
+
<template v-else-if="uploaderLoading">
|
|
806
|
+
<sepcial-loader class="size-5" />
|
|
807
|
+
Uploading
|
|
808
|
+
</template>
|
|
809
|
+
|
|
810
|
+
<template v-else>Upload</template>
|
|
790
811
|
</button>
|
|
791
812
|
</div>
|
|
813
|
+
|
|
814
|
+
<div v-show="showExistModal" class="absolute inset-0 bg-white py-6 px-7 flex flex-col">
|
|
815
|
+
<div class="flex items-center justify-between pb-3">
|
|
816
|
+
<h3 class="text-lg font-semibold">Upload options</h3>
|
|
817
|
+
<button @click="showExistModal=false" class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 text-gray-600 rounded-full hover:text-gray-800 hover:bg-gray-50">
|
|
818
|
+
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
|
|
819
|
+
</button>
|
|
820
|
+
</div>
|
|
821
|
+
|
|
822
|
+
<div class="flex-1">
|
|
823
|
+
<p class="text-gray-800">One or more items already exists in this location. Do you want to replace the existing items with a new version or keep both items? Replacing the items won't change sharing settings.</p>
|
|
824
|
+
|
|
825
|
+
<fieldset>
|
|
826
|
+
<div class="mt-6 space-y-5 pl-2">
|
|
827
|
+
<div v-for="option in replaceOptions" :key="option.value">
|
|
828
|
+
<label :for="option.value" class="flex items-center cursor-pointer">
|
|
829
|
+
<input :id="option.value" name="notification-method" type="radio" :value="option.value" v-model="overrideAction" :checked="option.value === overrideAction" class="option-radio peer" />
|
|
830
|
+
<div class="ml-3 block font-medium peer-checked:text-black text-light-700 cursor-pointer">{{ option.label }}</div>
|
|
831
|
+
</label>
|
|
832
|
+
</div>
|
|
833
|
+
</div>
|
|
834
|
+
</fieldset>
|
|
835
|
+
</div>
|
|
836
|
+
|
|
837
|
+
<div class="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2">
|
|
838
|
+
<button @click="showExistModal=false" type="button" class="inline-flex gap-2 items-center justify-center h-10 px-4 py-2 text-sm font-medium transition-colors border rounded-md focus:outline-none focus:ring-2 focus:ring-neutral-100 focus:ring-offset-2">
|
|
839
|
+
<div>Dismiss</div>
|
|
840
|
+
</button>
|
|
841
|
+
<button
|
|
842
|
+
@click="uploadFiles(true)"
|
|
843
|
+
:disabled="uploaderLoading"
|
|
844
|
+
type="button"
|
|
845
|
+
class="inline-flex items-center gap-2 justify-center h-10 px-4 py-2 text-sm font-medium text-white transition-colors border border-transparent rounded-md focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:ring-offset-2 bg-neutral-950 hover:bg-neutral-900 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
846
|
+
>
|
|
847
|
+
Upload
|
|
848
|
+
</button>
|
|
849
|
+
</div>
|
|
850
|
+
</div>
|
|
851
|
+
|
|
852
|
+
<div v-show="checkerFailedModal" class="absolute inset-0 bg-white py-6 px-7 flex flex-col">
|
|
853
|
+
<div class="flex items-center justify-between pb-3">
|
|
854
|
+
<h3 class="text-lg font-semibold">Checker failed</h3>
|
|
855
|
+
<button @click="checkerFailedModal=false" class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 text-gray-600 rounded-full hover:text-gray-800 hover:bg-gray-50">
|
|
856
|
+
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
|
|
857
|
+
</button>
|
|
858
|
+
</div>
|
|
859
|
+
|
|
860
|
+
<div class="flex-1">
|
|
861
|
+
<p class="text-gray-800">We couldn't complete your file upload due to a network error. This might be due to a temporary connection issue.
|
|
862
|
+
|
|
863
|
+
</p>
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
</div>
|
|
867
|
+
|
|
868
|
+
<div class="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2">
|
|
869
|
+
<button @click="checkerFailedModal=false" type="button" class="inline-flex gap-2 items-center justify-center h-10 px-4 py-2 text-sm font-medium transition-colors border rounded-md focus:outline-none focus:ring-2 focus:ring-neutral-100 focus:ring-offset-2">
|
|
870
|
+
<div>Dismiss</div>
|
|
871
|
+
</button>
|
|
872
|
+
<button
|
|
873
|
+
@click="uploadFiles(false), (checkerFailedModal = false)"
|
|
874
|
+
:disabled="uploaderLoading"
|
|
875
|
+
type="button"
|
|
876
|
+
class="inline-flex items-center gap-2 justify-center h-10 px-4 py-2 text-sm font-medium text-white transition-colors border border-transparent rounded-md focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:ring-offset-2 bg-neutral-950 hover:bg-neutral-900 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
877
|
+
>
|
|
878
|
+
Retry
|
|
879
|
+
</button>
|
|
880
|
+
</div>
|
|
881
|
+
</div>
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
|
|
792
885
|
</div>
|
|
793
886
|
</transition>
|
|
794
887
|
</div>
|
|
795
888
|
</transition>
|
|
796
889
|
</div>
|
|
797
|
-
|
|
798
|
-
<!-- Upload job window (Google Drive style) -->
|
|
799
|
-
<div v-if="uploadJobs.length || checkingFiles" class="fixed bottom-4 right-4 z-[100] w-96 bg-white rounded-lg shadow-2xl border border-neutral-200 overflow-hidden" style="font-family: system-ui, -apple-system, sans-serif;">
|
|
800
|
-
<!-- Header -->
|
|
801
|
-
<div class="flex items-center justify-between px-4 py-3 bg-neutral-900 text-white cursor-pointer" @click="jobWindowCollapsed = !jobWindowCollapsed">
|
|
802
|
-
<div class="flex items-center gap-2.5 font-medium text-sm">
|
|
803
|
-
<!-- Circular progress -->
|
|
804
|
-
<svg v-if="uploadJobs.length && !checkingFiles && uploadsInProgress > 0" class="w-5 h-5 -rotate-90 shrink-0" viewBox="0 0 20 20">
|
|
805
|
-
<circle cx="10" cy="10" r="8" fill="none" stroke="currentColor" stroke-width="2.5" opacity="0.2" />
|
|
806
|
-
<circle cx="10" cy="10" r="8" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" :stroke-dasharray="50.265" :stroke-dashoffset="50.265 - (50.265 * overallProgress / 100)" class="transition-all duration-300" />
|
|
807
|
-
</svg>
|
|
808
|
-
<div>
|
|
809
|
-
<template v-if="checkingFiles">Checking {{ checkingFilesCount }} file{{ checkingFilesCount > 1 ? 's' : '' }}</template>
|
|
810
|
-
<template v-else-if="uploadsInProgress > 0">Uploading {{ uploadsInProgress }} item{{ uploadsInProgress > 1 ? 's' : '' }}<template v-if="uploadsFailed">, {{ uploadsFailed }} failed</template><span v-if="estimatedTimeLeft" class="ml-1 text-white/50 font-normal">· {{ estimatedTimeLeft }}</span></template>
|
|
811
|
-
<template v-else>
|
|
812
|
-
{{ uploadsDone }} uploaded<template v-if="uploadsFailed">, {{ uploadsFailed }} failed</template>
|
|
813
|
-
</template>
|
|
814
|
-
</div>
|
|
815
|
-
</div>
|
|
816
|
-
<div class="flex items-center gap-2">
|
|
817
|
-
<button @click.stop="jobWindowCollapsed = !jobWindowCollapsed" class="text-white/70 hover:text-white">
|
|
818
|
-
<svg :class="{ 'rotate-180': jobWindowCollapsed }" class="w-5 h-5 transition-transform" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" /></svg>
|
|
819
|
-
</button>
|
|
820
|
-
<button v-if="uploadsInProgress === 0 && !checkingFiles" @click.stop="uploadJobs = []" class="text-white/70 hover:text-white">
|
|
821
|
-
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
|
|
822
|
-
</button>
|
|
823
|
-
</div>
|
|
824
|
-
</div>
|
|
825
|
-
<!-- Failed summary bar -->
|
|
826
|
-
<div v-if="uploadsFailed > 0 && uploadsInProgress === 0 && !jobWindowCollapsed" class="flex items-center justify-between px-4 py-2 bg-red-50 border-b border-red-100">
|
|
827
|
-
<div class="flex items-center gap-2 text-red-600 text-sm font-medium">
|
|
828
|
-
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path fill-rule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zm-1.72 6.97a.75.75 0 10-1.06 1.06L10.94 12l-1.72 1.72a.75.75 0 101.06 1.06L12 13.06l1.72 1.72a.75.75 0 101.06-1.06L13.06 12l1.72-1.72a.75.75 0 10-1.06-1.06L12 10.94l-1.72-1.72z" clip-rule="evenodd" /></svg>
|
|
829
|
-
{{ uploadsFailed }} failed
|
|
830
|
-
</div>
|
|
831
|
-
<div class="flex items-center gap-1.5">
|
|
832
|
-
<!-- Navigate up/down between failed items -->
|
|
833
|
-
<button @click.stop="navigateFailed(-1)" class="p-0.5 rounded hover:bg-red-100 text-red-500 transition-colors">
|
|
834
|
-
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 15.75l7.5-7.5 7.5 7.5" /></svg>
|
|
835
|
-
</button>
|
|
836
|
-
<button @click.stop="navigateFailed(1)" class="p-0.5 rounded hover:bg-red-100 text-red-500 transition-colors">
|
|
837
|
-
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" /></svg>
|
|
838
|
-
</button>
|
|
839
|
-
<!-- Retry all failed -->
|
|
840
|
-
<button @click.stop="retryAllFailed" class="text-xs text-red-600 hover:text-red-800 font-medium px-2 py-1 rounded hover:bg-red-100 transition-colors">
|
|
841
|
-
Retry all
|
|
842
|
-
</button>
|
|
843
|
-
</div>
|
|
844
|
-
</div>
|
|
845
|
-
<!-- File list -->
|
|
846
|
-
<div v-show="!jobWindowCollapsed" ref="jobListRef" class="max-h-64 overflow-y-auto divide-y divide-neutral-100">
|
|
847
|
-
<!-- Checking files row -->
|
|
848
|
-
<div v-if="checkingFiles" class="flex items-center gap-3 px-4 py-2.5">
|
|
849
|
-
<div class="shrink-0">
|
|
850
|
-
<sepcial-loader class="size-5 text-neutral-500" />
|
|
851
|
-
</div>
|
|
852
|
-
<div class="flex-1 min-w-0">
|
|
853
|
-
<div class="text-sm">Checking {{ checkingFilesCount }} file{{ checkingFilesCount > 1 ? 's' : '' }} for duplicates...</div>
|
|
854
|
-
</div>
|
|
855
|
-
</div>
|
|
856
|
-
<!-- Upload jobs -->
|
|
857
|
-
<div v-for="job in uploadJobs" :key="job.id" :ref="'job-' + job.id" class="flex items-center gap-3 px-4 py-2.5">
|
|
858
|
-
<div class="shrink-0">
|
|
859
|
-
<!-- Pending (queued) -->
|
|
860
|
-
<svg v-if="job.status === 'pending'" class="w-5 h-5 text-neutral-300" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" /></svg>
|
|
861
|
-
<!-- Uploading spinner -->
|
|
862
|
-
<svg v-else-if="job.status === 'uploading'" class="w-5 h-5 text-neutral-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" /></svg>
|
|
863
|
-
<!-- Success icon -->
|
|
864
|
-
<svg v-else-if="job.status === 'done'" class="w-5 h-5 text-green-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm13.36-1.814a.75.75 0 10-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 00-1.06 1.06l2.25 2.25a.75.75 0 001.14-.094l3.75-5.25z" clip-rule="evenodd" /></svg>
|
|
865
|
-
<!-- Error icon -->
|
|
866
|
-
<svg v-else-if="job.status === 'error'" class="w-5 h-5 text-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path fill-rule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zm-1.72 6.97a.75.75 0 10-1.06 1.06L10.94 12l-1.72 1.72a.75.75 0 101.06 1.06L12 13.06l1.72 1.72a.75.75 0 101.06-1.06L13.06 12l1.72-1.72a.75.75 0 10-1.06-1.06L12 10.94l-1.72-1.72z" clip-rule="evenodd" /></svg>
|
|
867
|
-
</div>
|
|
868
|
-
<div class="flex-1 min-w-0">
|
|
869
|
-
<div class="text-sm truncate" v-text="job.name"></div>
|
|
870
|
-
<!-- Progress bar -->
|
|
871
|
-
<div v-if="job.status === 'uploading'" class="mt-1 h-1 bg-neutral-100 rounded-full overflow-hidden">
|
|
872
|
-
<div class="h-full bg-blue-500 rounded-full transition-all duration-300" :style="{ width: job.progress + '%' }"></div>
|
|
873
|
-
</div>
|
|
874
|
-
</div>
|
|
875
|
-
<div class="shrink-0 flex items-center gap-1">
|
|
876
|
-
<span v-if="job.status === 'uploading'" class="text-xs text-neutral-400">{{ Math.round(job.progress) }}%</span>
|
|
877
|
-
<!-- Retry button for failed uploads -->
|
|
878
|
-
<button v-if="job.status === 'error'" @click.stop="retryUpload(job)" class="text-xs text-blue-500 hover:text-blue-700 font-medium px-1.5 py-0.5 rounded hover:bg-blue-50 transition-colors">
|
|
879
|
-
Retry
|
|
880
|
-
</button>
|
|
881
|
-
</div>
|
|
882
|
-
</div>
|
|
883
|
-
</div>
|
|
884
|
-
</div>
|
|
885
890
|
</teleport>
|
|
886
891
|
</div>
|
|
887
892
|
|
|
@@ -929,7 +934,7 @@ export async function viewer(fields, e) {
|
|
|
929
934
|
};
|
|
930
935
|
|
|
931
936
|
var containerId = crypto.randomUUID();
|
|
932
|
-
e
|
|
937
|
+
e.container_node.setAttribute('widget-id', containerId);
|
|
933
938
|
|
|
934
939
|
var app = createApp({
|
|
935
940
|
template: html,
|
|
@@ -1000,22 +1005,18 @@ export async function viewer(fields, e) {
|
|
|
1000
1005
|
folders: [],
|
|
1001
1006
|
files: [],
|
|
1002
1007
|
view: fields.default_view_type || 'gallery',
|
|
1008
|
+
uploadModal: false,
|
|
1009
|
+
uploaderLoading: false,
|
|
1010
|
+
checkingFiles: false,
|
|
1003
1011
|
loadingSearch: false,
|
|
1004
1012
|
fetcher,
|
|
1005
1013
|
formatBytes,
|
|
1014
|
+
initUploader: upload,
|
|
1006
1015
|
swiper: null,
|
|
1007
1016
|
existingTags: '',
|
|
1008
1017
|
showSearchPopper: false,
|
|
1009
1018
|
isOverDropZone: false,
|
|
1010
1019
|
removeChipCount: 0,
|
|
1011
|
-
// Upload job window state
|
|
1012
|
-
uploadJobs: [],
|
|
1013
|
-
jobWindowCollapsed: false,
|
|
1014
|
-
pendingFiles: [],
|
|
1015
|
-
checkingFiles: false,
|
|
1016
|
-
checkingFilesCount: 0,
|
|
1017
|
-
failedNavIndex: -1,
|
|
1018
|
-
uploadStartTime: null,
|
|
1019
1020
|
searchOptions: [
|
|
1020
1021
|
{
|
|
1021
1022
|
label: 'Filename',
|
|
@@ -1047,7 +1048,10 @@ export async function viewer(fields, e) {
|
|
|
1047
1048
|
existingFiles: {},
|
|
1048
1049
|
allLoading: false,
|
|
1049
1050
|
showExistModal: false,
|
|
1051
|
+
checkerFailedModal: false,
|
|
1050
1052
|
overrideAction: 'replace',
|
|
1053
|
+
pond: null,
|
|
1054
|
+
processFiles: null,
|
|
1051
1055
|
};
|
|
1052
1056
|
},
|
|
1053
1057
|
methods: {
|
|
@@ -1094,190 +1098,67 @@ export async function viewer(fields, e) {
|
|
|
1094
1098
|
refreshFsLightbox();
|
|
1095
1099
|
});
|
|
1096
1100
|
},
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
if (!files.length) return;
|
|
1100
|
-
this.pendingFiles = files;
|
|
1101
|
-
// Reset the input so re-selecting the same files works
|
|
1102
|
-
event.target.value = '';
|
|
1103
|
-
this.checkAndUpload();
|
|
1104
|
-
},
|
|
1105
|
-
checkAndUpload() {
|
|
1106
|
-
this.existingFiles = {};
|
|
1107
|
-
this.checkingFiles = true;
|
|
1108
|
-
this.checkingFilesCount = this.pendingFiles.length;
|
|
1109
|
-
this.jobWindowCollapsed = false;
|
|
1110
|
-
var file_paths = this.pendingFiles.map((f) => '/' + f.name);
|
|
1111
|
-
|
|
1112
|
-
this.fetcher('check_drive_files_workspace', {
|
|
1113
|
-
file_paths,
|
|
1114
|
-
type: 'file',
|
|
1115
|
-
})
|
|
1116
|
-
.then(() => {
|
|
1117
|
-
this.checkingFiles = false;
|
|
1118
|
-
// No conflicts — upload all
|
|
1119
|
-
this.startUpload(false);
|
|
1120
|
-
})
|
|
1121
|
-
.catch((err) => {
|
|
1122
|
-
this.checkingFiles = false;
|
|
1123
|
-
if (err.code === -764) {
|
|
1124
|
-
(err.files || []).forEach((file, index) => {
|
|
1125
|
-
if (file.code === -764) {
|
|
1126
|
-
this.existingFiles[this.pendingFiles[index].name] = file.duplicates;
|
|
1127
|
-
}
|
|
1128
|
-
});
|
|
1101
|
+
uploadFiles(is_after_confirm) {
|
|
1102
|
+
this.uploaderLoading = true;
|
|
1129
1103
|
|
|
1130
|
-
|
|
1131
|
-
this.showExistModal = true;
|
|
1132
|
-
} else {
|
|
1133
|
-
this.startUpload(false);
|
|
1134
|
-
}
|
|
1135
|
-
} else {
|
|
1136
|
-
// Network error — upload anyway, server will handle it
|
|
1137
|
-
this.startUpload(false);
|
|
1138
|
-
}
|
|
1139
|
-
});
|
|
1140
|
-
},
|
|
1141
|
-
startUpload(isAfterConsent) {
|
|
1142
|
-
if (isAfterConsent) {
|
|
1104
|
+
if (is_after_confirm) {
|
|
1143
1105
|
this.showExistModal = false;
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
this.jobWindowCollapsed = false;
|
|
1149
|
-
this.uploadStartTime = Date.now();
|
|
1150
|
-
|
|
1151
|
-
filesToUpload.forEach((file) => {
|
|
1152
|
-
const jobId = crypto.randomUUID();
|
|
1153
|
-
const isUpdate = this.overrideAction === 'replace' && file.name in this.existingFiles;
|
|
1154
|
-
this.uploadJobs.push({
|
|
1155
|
-
id: jobId,
|
|
1156
|
-
name: file.name,
|
|
1157
|
-
progress: 0,
|
|
1158
|
-
status: 'pending',
|
|
1159
|
-
file: file,
|
|
1160
|
-
isUpdate: isUpdate,
|
|
1106
|
+
// this.processCb();
|
|
1107
|
+
this.processFiles(null, {
|
|
1108
|
+
overrideAction: this.overrideAction,
|
|
1109
|
+
existingFiles: this.existingFiles,
|
|
1161
1110
|
});
|
|
1162
|
-
});
|
|
1163
1111
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
const active = this.uploadJobs.filter(j => j.status === 'uploading').length;
|
|
1169
|
-
const pending = this.uploadJobs.filter(j => j.status === 'pending');
|
|
1170
|
-
const slotsAvailable = MAX_CONCURRENT - active;
|
|
1171
|
-
|
|
1172
|
-
for (let i = 0; i < Math.min(slotsAvailable, pending.length); i++) {
|
|
1173
|
-
const job = pending[i];
|
|
1174
|
-
job.status = 'uploading';
|
|
1175
|
-
this.uploadSingleFile(job.file, job.id, job.isUpdate);
|
|
1176
|
-
}
|
|
1177
|
-
},
|
|
1178
|
-
uploadSingleFile(file, jobId, isUpdate) {
|
|
1179
|
-
let formData = new FormData();
|
|
1180
|
-
formData.append('file', file, file.name);
|
|
1181
|
-
|
|
1182
|
-
const { app_id: appId, gtp_token, app_token, api_key } = e._session;
|
|
1183
|
-
formData.append('app_id_reference', APP_OBJ?.[appId]?.app_id_reference);
|
|
1184
|
-
formData.append('app_id', appId);
|
|
1185
|
-
formData.append('gtp_token', gtp_token);
|
|
1186
|
-
formData.append('app_token', app_token);
|
|
1187
|
-
formData.append('drive_type', 'workspace');
|
|
1188
|
-
if (api_key) formData.append('api_key', api_key);
|
|
1189
|
-
|
|
1190
|
-
if (fields.file_upload_folder) formData.append('folder', fields.file_upload_folder);
|
|
1191
|
-
if (fields.public_file) formData.append('make_public', fields.public_file);
|
|
1192
|
-
|
|
1193
|
-
let tags = fields.assign_file_tags || [];
|
|
1194
|
-
let file_tags = [];
|
|
1195
|
-
if (fields.auto_tag_generator) {
|
|
1196
|
-
let identifier = fields.auto_tag_identifier || ' ';
|
|
1197
|
-
const filename = file.name.split('.').slice(0, -1).join('.');
|
|
1198
|
-
file_tags = filename.split(identifier);
|
|
1199
|
-
}
|
|
1200
|
-
formData.append('tags', [...tags, ...file_tags]);
|
|
1201
|
-
|
|
1202
|
-
const request = new XMLHttpRequest();
|
|
1203
|
-
|
|
1204
|
-
let _domain = e._session.domain;
|
|
1205
|
-
if (e._session.is_deployment) {
|
|
1206
|
-
_domain = e._session.opt.regional_server === 'dev.xuda.ai' ? 'dev.xuda.ai' : 'xuda.ai';
|
|
1207
|
-
}
|
|
1112
|
+
// this.pond.on("processfile", (e) => {
|
|
1113
|
+
// this.uploaderLoading = false;
|
|
1114
|
+
// this.uploadModal = false;
|
|
1115
|
+
// this.pond.removeFiles();
|
|
1208
1116
|
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
formData.append('file_name', file.name);
|
|
1212
|
-
request.open('POST', 'https://' + _domain + '/cpi/update_drive_file_workspace');
|
|
1117
|
+
// this.refreshDirectory();
|
|
1118
|
+
// });
|
|
1213
1119
|
} else {
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
request.setRequestHeader('xu-gtp-token', gtp_token);
|
|
1218
|
-
request.setRequestHeader('xu-app-token', app_token);
|
|
1120
|
+
this.existingFiles = {};
|
|
1121
|
+
this.checkingFiles = true;
|
|
1219
1122
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
const job = this.uploadJobs.find((j) => j.id === jobId);
|
|
1223
|
-
if (job) job.progress = (ev.loaded / ev.total) * 100;
|
|
1224
|
-
}
|
|
1225
|
-
};
|
|
1226
|
-
|
|
1227
|
-
const onComplete = () => {
|
|
1228
|
-
this.processQueue();
|
|
1229
|
-
const hasPending = this.uploadJobs.some(j => j.status === 'pending');
|
|
1230
|
-
if (this.uploadsInProgress === 0 && !hasPending) {
|
|
1231
|
-
this.refreshDirectory(false, true);
|
|
1232
|
-
}
|
|
1233
|
-
};
|
|
1123
|
+
var filesMapped = this.pond.getFiles().map((e) => ({ id: e.id, filename: e.file.name }));
|
|
1124
|
+
var file_paths = filesMapped.map(({ filename }) => '/' + filename);
|
|
1234
1125
|
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
} else {
|
|
1242
|
-
if (job) job.status = 'error';
|
|
1243
|
-
}
|
|
1244
|
-
} catch (_e) {
|
|
1245
|
-
if (job) job.status = 'error';
|
|
1246
|
-
}
|
|
1247
|
-
onComplete();
|
|
1248
|
-
};
|
|
1126
|
+
this.fetcher(`check_drive_files_workspace`, {
|
|
1127
|
+
file_paths,
|
|
1128
|
+
type: 'file',
|
|
1129
|
+
})
|
|
1130
|
+
.then(() => {
|
|
1131
|
+
this.checkingFiles = false;
|
|
1249
1132
|
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1133
|
+
// No conflicts — process all files
|
|
1134
|
+
filesMapped.forEach((e) => {
|
|
1135
|
+
this.processFiles(e.id);
|
|
1136
|
+
});
|
|
1137
|
+
})
|
|
1138
|
+
.catch((err) => {
|
|
1139
|
+
this.checkingFiles = false;
|
|
1140
|
+
|
|
1141
|
+
if (err.code === -764) {
|
|
1142
|
+
(err.files || []).forEach((file, index) => {
|
|
1143
|
+
if (file.code === -764) {
|
|
1144
|
+
this.existingFiles[filesMapped[index].filename] = file.duplicates;
|
|
1145
|
+
}
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
if (Object.keys(this.existingFiles).length) {
|
|
1149
|
+
this.showExistModal = true;
|
|
1150
|
+
}
|
|
1255
1151
|
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
job.progress = 0;
|
|
1267
|
-
});
|
|
1268
|
-
this.processQueue();
|
|
1269
|
-
},
|
|
1270
|
-
navigateFailed(direction) {
|
|
1271
|
-
const failedJobs = this.uploadJobs.filter(j => j.status === 'error');
|
|
1272
|
-
if (!failedJobs.length) return;
|
|
1273
|
-
this.failedNavIndex = (this.failedNavIndex + direction + failedJobs.length) % failedJobs.length;
|
|
1274
|
-
const targetJob = failedJobs[this.failedNavIndex];
|
|
1275
|
-
const refEl = this.$refs['job-' + targetJob.id];
|
|
1276
|
-
const el = Array.isArray(refEl) ? refEl[0] : refEl;
|
|
1277
|
-
if (el && this.$refs.jobListRef) {
|
|
1278
|
-
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
1279
|
-
el.classList.add('bg-red-50');
|
|
1280
|
-
setTimeout(() => el.classList.remove('bg-red-50'), 1500);
|
|
1152
|
+
// Process non-conflicting files
|
|
1153
|
+
filesMapped
|
|
1154
|
+
.filter((e) => !Object.keys(this.existingFiles).includes(e.filename))
|
|
1155
|
+
.forEach((e) => {
|
|
1156
|
+
this.processFiles(e.id);
|
|
1157
|
+
});
|
|
1158
|
+
} else {
|
|
1159
|
+
this.checkerFailedModal = true;
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
1281
1162
|
}
|
|
1282
1163
|
},
|
|
1283
1164
|
|
|
@@ -1310,10 +1191,10 @@ export async function viewer(fields, e) {
|
|
|
1310
1191
|
moment,
|
|
1311
1192
|
},
|
|
1312
1193
|
mounted() {
|
|
1313
|
-
var onDrop = (
|
|
1194
|
+
var onDrop = (files) => {
|
|
1314
1195
|
if (this.view === 'slider') return;
|
|
1315
|
-
this.
|
|
1316
|
-
this.
|
|
1196
|
+
this.uploadModal = true;
|
|
1197
|
+
this.pond.addFiles(files);
|
|
1317
1198
|
};
|
|
1318
1199
|
|
|
1319
1200
|
const { isOverDropZone } = useDropZone(this.$refs.dropZoneRef, {
|
|
@@ -1323,44 +1204,6 @@ export async function viewer(fields, e) {
|
|
|
1323
1204
|
this.isOverDropZone = isOverDropZone;
|
|
1324
1205
|
},
|
|
1325
1206
|
computed: {
|
|
1326
|
-
uploadsInProgress() {
|
|
1327
|
-
return this.uploadJobs.filter((j) => j.status === 'uploading' || j.status === 'pending').length;
|
|
1328
|
-
},
|
|
1329
|
-
uploadsDone() {
|
|
1330
|
-
return this.uploadJobs.filter(j => j.status === 'done').length;
|
|
1331
|
-
},
|
|
1332
|
-
uploadsFailed() {
|
|
1333
|
-
return this.uploadJobs.filter(j => j.status === 'error').length;
|
|
1334
|
-
},
|
|
1335
|
-
overallProgress() {
|
|
1336
|
-
if (!this.uploadJobs.length) return 0;
|
|
1337
|
-
const total = this.uploadJobs.reduce((sum, j) => sum + (j.file?.size || 0), 0);
|
|
1338
|
-
if (!total) return this.uploadJobs.reduce((sum, j) => sum + j.progress, 0) / this.uploadJobs.length;
|
|
1339
|
-
const uploaded = this.uploadJobs.reduce((sum, j) => sum + (j.file?.size || 0) * (j.progress / 100), 0);
|
|
1340
|
-
return (uploaded / total) * 100;
|
|
1341
|
-
},
|
|
1342
|
-
estimatedTimeLeft() {
|
|
1343
|
-
if (!this.uploadStartTime || this.overallProgress <= 0 || this.uploadsInProgress === 0) return '';
|
|
1344
|
-
const elapsed = (Date.now() - this.uploadStartTime) / 1000;
|
|
1345
|
-
const remaining = (elapsed / this.overallProgress) * (100 - this.overallProgress);
|
|
1346
|
-
if (remaining < 60) return Math.ceil(remaining) + 's left';
|
|
1347
|
-
if (remaining < 3600) return Math.ceil(remaining / 60) + 'm left';
|
|
1348
|
-
const h = Math.floor(remaining / 3600);
|
|
1349
|
-
const m = Math.ceil((remaining % 3600) / 60);
|
|
1350
|
-
return h + 'h ' + m + 'm left';
|
|
1351
|
-
},
|
|
1352
|
-
acceptTypes() {
|
|
1353
|
-
if (fields.file_upload_mime_type_preset) {
|
|
1354
|
-
switch (fields.file_upload_mime_type_preset) {
|
|
1355
|
-
case 'image': return 'image/*';
|
|
1356
|
-
case 'video': return 'video/*';
|
|
1357
|
-
case 'audio': return 'audio/*';
|
|
1358
|
-
default: return 'image/*';
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
if (fields.file_upload_custom_mime_types) return fields.file_upload_custom_mime_types;
|
|
1362
|
-
return null;
|
|
1363
|
-
},
|
|
1364
1207
|
searchTags() {
|
|
1365
1208
|
return this.opts.search_string.split(' ').filter((e) => e.includes(':'));
|
|
1366
1209
|
},
|
|
@@ -1439,6 +1282,43 @@ export async function viewer(fields, e) {
|
|
|
1439
1282
|
async created() {
|
|
1440
1283
|
this.refreshDirectory();
|
|
1441
1284
|
|
|
1285
|
+
this.$nextTick(async () => {
|
|
1286
|
+
// debugger;
|
|
1287
|
+
const uploaderContainer = e.container_node.querySelector('#imageUploaderComponent');
|
|
1288
|
+
if (!uploaderContainer) return; // viewer-only (e.g. a read-only diagram) — no uploader element to init
|
|
1289
|
+
var { pond, processFiles } = await this.initUploader(
|
|
1290
|
+
{ ...fields, instant_file_upload: 'N' },
|
|
1291
|
+
{
|
|
1292
|
+
...e,
|
|
1293
|
+
container_node: uploaderContainer,
|
|
1294
|
+
container_data: uploaderContainer ? func.runtime.ui.get_data(uploaderContainer) : {},
|
|
1295
|
+
},
|
|
1296
|
+
);
|
|
1297
|
+
|
|
1298
|
+
this.pond = pond;
|
|
1299
|
+
this.processFiles = processFiles;
|
|
1300
|
+
|
|
1301
|
+
// Avoid per-file DOM removal — let processfiles handle cleanup in bulk
|
|
1302
|
+
this.pond.on('processfile', (e, { id }) => {
|
|
1303
|
+
// if (!e?.type !== 'error') {
|
|
1304
|
+
// this.pond.removeFiles(id);
|
|
1305
|
+
// }
|
|
1306
|
+
|
|
1307
|
+
// if (!this.pond.getFiles().length) {
|
|
1308
|
+
// this.uploaderLoading = false;
|
|
1309
|
+
// this.uploadModal = false;
|
|
1310
|
+
// this.pond.removeFiles();
|
|
1311
|
+
// }
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
this.pond.on('processfiles', () => {
|
|
1315
|
+
this.uploaderLoading = false;
|
|
1316
|
+
this.uploadModal = false;
|
|
1317
|
+
this.pond.removeFiles();
|
|
1318
|
+
this.refreshDirectory(false, true);
|
|
1319
|
+
});
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1442
1322
|
this.$watch('opts', (value) => {
|
|
1443
1323
|
this.refreshDirectory();
|
|
1444
1324
|
});
|