@cloudron/pankow 4.1.9 → 4.1.10

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.
@@ -1,8 +1,8 @@
1
1
  <template>
2
2
  <div class="pankow-file-uploader" v-show="uploadInProgress">
3
3
  <div class="pankow-file-uploader-label-and-speed-container"><div class="pankow-file-uploader-label">{{ tr('filemanager.uploader.uploading') }} {{ activeFile ? activeFile.name : '' }}</div><div class="pankow-file-uploader-speed">{{ prettyUploadSpeed }}</div></div>
4
- <div style="display: flex;">
5
- <ProgressBar :value="parseInt((this.uploadedSize * 100) / this.size) || 0" style="flex-grow: 1"></ProgressBar>
4
+ <div class="pankow-file-uploader-progress-container">
5
+ <ProgressBar :value="parseInt((uploadedSize * 100) / size) || 0" style="flex-grow: 1"></ProgressBar>
6
6
  <Button danger small tool @click="onCancelUpload" icon="fa-solid fa-xmark"></Button>
7
7
  </div>
8
8
 
@@ -13,175 +13,181 @@
13
13
  </div>
14
14
  </template>
15
15
 
16
- <script>
16
+ <script setup>
17
+
18
+ import { ref, computed, onMounted, useTemplateRef } from 'vue';
17
19
 
18
20
  import ProgressBar from './ProgressBar.vue';
19
21
  import Button from './Button.vue';
20
- import Icon from './Icon.vue';
21
22
  import InputDialog from './InputDialog.vue';
22
23
 
23
24
  import { translation } from '../utils.js';
24
25
 
25
- export default {
26
- name: 'FileUploader',
27
- components: {
28
- ProgressBar,
29
- Button,
30
- Icon,
31
- InputDialog
26
+ const emit = defineEmits([ 'finished' ]);
27
+
28
+ const props = defineProps({
29
+ uploadHandler: {
30
+ type: Function,
31
+ default() { console.warn('Missing uploadHandler for FileUploader'); }
32
32
  },
33
- emits: [ 'finished' ],
34
- props: {
35
- uploadHandler: {
36
- type: Function,
37
- default() { console.warn('Missing uploadHandler for FileUploader'); }
38
- },
39
- cancelHandler: {
40
- type: Function,
41
- default() { console.warn('Missing cancelHandler for FileUploader'); }
42
- },
43
- jobPreFlightCheckHandler: {
44
- type: Function,
45
- default(job) { console.warn('Missing jobPreFlightCheckHandler for FileUploader'); return true; }
46
- },
47
- tr: {
48
- type: Function,
49
- default(id) { console.warn('Missing tr for FileUploader, using fallback.'); return translation(id); }
50
- },
33
+ cancelHandler: {
34
+ type: Function,
35
+ default() { console.warn('Missing cancelHandler for FileUploader'); }
51
36
  },
52
- data() {
53
- return {
54
- activeTargetFolder: '/', // keep folder state for upload dialogs
55
- uploadInProgress: false,
56
- activeJob: null,
57
- activeFile: {},
58
- pendingJobs: [],
59
- size: 0,
60
- uploadedSize: 0,
61
- uploadSpeed: 0
62
- };
37
+ jobPreFlightCheckHandler: {
38
+ type: Function,
39
+ default(job) { console.warn('Missing jobPreFlightCheckHandler for FileUploader'); return true; }
63
40
  },
64
- computed: {
65
- prettyUploadSpeed() {
66
- if (this.uploadSpeed < 1024) return this.uploadSpeed + ' Kb/s';
67
- else return (this.uploadSpeed/1024).toFixed(2) + ' Mb/s';
68
- }
41
+ tr: {
42
+ type: Function,
43
+ default(id) { console.warn('Missing tr for FileUploader, using fallback.'); return translation(id); }
69
44
  },
70
- methods: {
71
- beforeUnloadListener(event) {
72
- event.preventDefault();
73
- return window.confirm(this.tr('filemanager.uploader.exitWarning'));
74
- },
75
- onUploadFile(targetFolder) {
76
- this.activeTargetFolder = targetFolder;
77
- this.$refs.uploadFileInput.click();
78
- },
79
- onUploadFolder(targetFolder) {
80
- this.activeTargetFolder = targetFolder;
81
- this.$refs.uploadFolderInput.click();
82
- },
83
- async onCancelUpload() {
84
- const confirmed = await this.$refs.inputDialog.confirm({
85
- message: `Really cancel upload?`,
86
- confirmStyle: 'danger',
87
- confirmLabel: 'Yes',
88
- rejectLabel: 'No'
89
- });
90
-
91
- if (!confirmed) return;
92
-
93
- this.cancelHandler();
94
- this.uploadFinished();
95
- },
96
- uploadFinished() {
97
- this.progess = 0;
98
- this.size = 0;
99
- this.uploadedSize = 0;
100
- this.activeFile = {};
101
- this.activeJob = null;
102
- this.uploadInProgress = false;
103
-
104
- window.removeEventListener('beforeunload', this.beforeUnloadListener, { capture: true });
105
-
106
- this.$emit('finished');
107
- },
108
- async uploadNextJob() {
109
- if (this.pendingJobs.length === 0) return this.uploadFinished();
110
-
111
- this.activeJob = this.pendingJobs.pop();
112
-
113
- if (!(await this.jobPreFlightCheckHandler(this.activeJob))) return this.uploadNextJob();
114
-
115
- this.uploadNextFile();
116
- },
117
- async uploadNextFile() {
118
- if (!this.activeJob || this.activeJob.pendingFiles.length === 0) return this.uploadNextJob();
119
-
120
- this.activeFile = this.activeJob.pendingFiles.pop();
121
-
122
- try {
123
- let uploadedSoFar = 0;
124
- let lastTimestamp = Date.now();
125
-
126
- await this.uploadHandler(this.activeJob.targetFolder, this.activeFile, (event) => {
127
- if (event.direction === 'upload') {
128
- const timeDiff = Date.now() - lastTimestamp;
129
- const loadedDiff = event.loaded - uploadedSoFar;
130
-
131
- this.uploadedSize += loadedDiff;
132
- this.uploadSpeed = parseInt(loadedDiff / (timeDiff / 1024) / 1024);
133
-
134
- uploadedSoFar = event.loaded;
135
- lastTimestamp = Date.now();
136
- }
137
- });
138
- } catch (e) {
139
- console.error(`Failed to upload ${this.activeFile.name}`, e);
140
- }
45
+ });
46
+
47
+ const uploadFileInput = useTemplateRef('uploadFileInput');
48
+ const uploadFolderInput = useTemplateRef('uploadFolderInput');
49
+ const inputDialog = useTemplateRef('inputDialog');
50
+
51
+ const activeTargetFolder = ref('/');
52
+ const uploadInProgress = ref(false);
53
+ const activeJob = ref(null);
54
+ const activeFile = ref({});
55
+ const pendingJobs = ref([]);
56
+ const size = ref(0);
57
+ const uploadedSize = ref(0);
58
+ const uploadSpeed = ref(0);
59
+
60
+ const prettyUploadSpeed = computed(() => {
61
+ if (uploadSpeed.value < 1024) return uploadSpeed.value + ' Kb/s';
62
+ else return (uploadSpeed.value / 1024).toFixed(2) + ' Mb/s';
63
+ });
64
+
65
+ function beforeUnloadListener(event) {
66
+ event.preventDefault();
67
+ return window.confirm(props.tr('filemanager.uploader.exitWarning'));
68
+ }
141
69
 
142
- this.uploadNextFile();
143
- },
144
- startUpload() {
145
- if (this.uploadInProgress) return;
146
- this.uploadInProgress = true;
70
+ function onUploadFile(targetFolder) {
71
+ activeTargetFolder.value = targetFolder;
72
+ uploadFileInput.value?.click();
73
+ }
147
74
 
148
- window.addEventListener('beforeunload', this.beforeUnloadListener, { capture: true });
75
+ function onUploadFolder(targetFolder) {
76
+ activeTargetFolder.value = targetFolder;
77
+ uploadFolderInput.value?.click();
78
+ }
149
79
 
150
- this.uploadNextJob();
151
- },
152
- addFiles(files, targetFolder) {
153
- if (!files || !files.length) return;
80
+ async function onCancelUpload() {
81
+ const confirmed = await inputDialog.value.confirm({
82
+ message: `Really cancel upload?`,
83
+ confirmStyle: 'danger',
84
+ confirmLabel: 'Yes',
85
+ rejectLabel: 'No'
86
+ });
154
87
 
155
- let folder = files[0].webkitRelativePath ? files[0].webkitRelativePath : null;
156
- if (folder && folder.indexOf('/') !== -1) folder = folder.split('/')[0];
88
+ if (!confirmed) return;
157
89
 
158
- const job = {
159
- folder,
160
- targetFolder,
161
- pendingFiles: []
162
- };
90
+ props.cancelHandler();
91
+ uploadFinished();
92
+ }
163
93
 
164
- // update overall size and convert to real Array
165
- for (let i = 0; i < files.length; ++i) {
166
- this.size += files[i].size;
167
- job.pendingFiles.push(files[i]);
168
- }
94
+ function uploadFinished() {
95
+ size.value = 0;
96
+ uploadedSize.value = 0;
97
+ activeFile.value = {};
98
+ activeJob.value = null;
99
+ uploadInProgress.value = false;
169
100
 
170
- this.pendingJobs.push(job);
101
+ window.removeEventListener('beforeunload', beforeUnloadListener, { capture: true });
171
102
 
172
- this.startUpload();
173
- }
174
- },
175
- mounted() {
176
- this.$refs.uploadFileInput.addEventListener('change', (e) => {
177
- this.addFiles(e.target.files || [], this.activeTargetFolder);
178
- });
103
+ emit('finished');
104
+ }
179
105
 
180
- this.$refs.uploadFolderInput.addEventListener('change', (e) => {
181
- this.addFiles(e.target.files || [], this.activeTargetFolder);
106
+ async function uploadNextJob() {
107
+ if (pendingJobs.value.length === 0) return uploadFinished();
108
+
109
+ activeJob.value = pendingJobs.value.pop();
110
+
111
+ if (!(await props.jobPreFlightCheckHandler(activeJob.value))) return uploadNextJob();
112
+
113
+ uploadNextFile();
114
+ }
115
+
116
+ async function uploadNextFile() {
117
+ console.log('next file')
118
+ if (!activeJob.value || activeJob.value.pendingFiles.length === 0) return uploadNextJob();
119
+
120
+ activeFile.value = activeJob.value.pendingFiles.pop();
121
+
122
+ try {
123
+ let uploadedSoFar = 0;
124
+ let lastTimestamp = Date.now();
125
+
126
+ await props.uploadHandler(activeJob.value.targetFolder, activeFile.value, (event) => {
127
+ if (event.direction === 'upload') {
128
+ const timeDiff = Date.now() - lastTimestamp;
129
+ const loadedDiff = event.loaded - uploadedSoFar;
130
+
131
+ uploadedSize.value += loadedDiff;
132
+ uploadSpeed.value = parseInt(loadedDiff / (timeDiff / 1024) / 1024);
133
+
134
+ uploadedSoFar = event.loaded;
135
+ lastTimestamp = Date.now();
136
+ }
182
137
  });
138
+ } catch (e) {
139
+ console.error(`Failed to upload ${activeFile.value.name}`, e);
183
140
  }
184
- };
141
+
142
+ uploadNextFile();
143
+ }
144
+
145
+ function startUpload() {
146
+ if (uploadInProgress.value) return;
147
+ uploadInProgress.value = true;
148
+
149
+ window.addEventListener('beforeunload', beforeUnloadListener, { capture: true });
150
+
151
+ uploadNextJob();
152
+ }
153
+
154
+ function addFiles(files, targetFolder) {
155
+ if (!files || !files.length) return;
156
+
157
+ let folder = files[0].webkitRelativePath ? files[0].webkitRelativePath : null;
158
+ if (folder && folder.indexOf('/') !== -1) folder = folder.split('/')[0];
159
+
160
+ const job = {
161
+ folder,
162
+ targetFolder,
163
+ pendingFiles: []
164
+ };
165
+
166
+ for (let i = 0; i < files.length; ++i) {
167
+ size.value += files[i].size;
168
+ job.pendingFiles.push(files[i]);
169
+ }
170
+
171
+ pendingJobs.value.push(job);
172
+
173
+ startUpload();
174
+ }
175
+
176
+ onMounted(() => {
177
+ uploadFileInput.value?.addEventListener('change', (e) => {
178
+ addFiles(e.target.files || [], activeTargetFolder.value);
179
+ });
180
+
181
+ uploadFolderInput.value?.addEventListener('change', (e) => {
182
+ addFiles(e.target.files || [], activeTargetFolder.value);
183
+ });
184
+ });
185
+
186
+ defineExpose({
187
+ addFiles,
188
+ onUploadFile,
189
+ onUploadFolder
190
+ });
185
191
 
186
192
  </script>
187
193
 
@@ -218,4 +224,9 @@ export default {
218
224
  white-space: nowrap;
219
225
  }
220
226
 
221
- </style>
227
+ .pankow-file-uploader-progress-container {
228
+ display: flex;
229
+ gap: 10px;
230
+ }
231
+
232
+ </style>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudron/pankow",
3
3
  "private": false,
4
- "version": "4.1.9",
4
+ "version": "4.1.10",
5
5
  "description": "",
6
6
  "main": "index.js",
7
7
  "types": "types/index.d.ts",
@@ -33,7 +33,7 @@
33
33
  "@vitejs/plugin-vue": "^6.0.5",
34
34
  "highlight.js": "^11.11.1",
35
35
  "typescript": "^6.0.2",
36
- "vite": "^8.0.6",
36
+ "vite": "^8.0.8",
37
37
  "vue": "^3.5.32",
38
38
  "vue-router": "^5.0.4"
39
39
  }