inertia-bootstrap-forms 1.0.80 → 1.0.82

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inertia-bootstrap-forms",
3
- "version": "1.0.80",
3
+ "version": "1.0.82",
4
4
  "description": "Create bootstrap forms with inertia and twitter bootstrap",
5
5
  "main": "dist/inertia-bootstrap-forms.cjs.js",
6
6
  "module": "dist/inertia-bootstrap-forms.es.js",
package/src/UppyInput.vue CHANGED
@@ -11,7 +11,7 @@
11
11
  </template>
12
12
 
13
13
  <script setup>
14
- import {computed, inject, onBeforeUnmount, onMounted, shallowRef, ref} from 'vue'
14
+ import {computed, inject, onBeforeUnmount, onMounted, shallowRef, ref, watch} from 'vue'
15
15
  import {
16
16
  Dropzone,
17
17
  FilesList,
@@ -43,6 +43,9 @@ const emits = defineEmits([
43
43
  const restrictionCaption = ref(null);
44
44
  const inputEl = ref(null);
45
45
  const uppy = shallowRef(null);
46
+ const isResetting = ref(false);
47
+ const isUnmounting = ref(false);
48
+ const uploadingFiles = ref(0);
46
49
 
47
50
  // Inject form and group contexts
48
51
  const form = inject("form", {value: {}, errors: {}, getID: n => n});
@@ -64,9 +67,8 @@ const modelValue = computed({
64
67
  }
65
68
  });
66
69
 
67
- // ایجاد اینستنس اختصاصی برای هر کامپوننت
68
70
  uppy.value = new Uppy({
69
- id: props.name, // جلوگیری از تداخل با استفاده از نام پروپ
71
+ id: props.name,
70
72
  autoProceed: true,
71
73
  ...props.config,
72
74
  restrictions: {
@@ -75,11 +77,18 @@ uppy.value = new Uppy({
75
77
  },
76
78
  });
77
79
 
80
+ uppy.value.on('upload', () => {
81
+ uploadingFiles.value = uppy.value.getFiles().filter(f => !f.progress.uploadComplete).length;
82
+ emits('upload');
83
+ });
84
+
78
85
  uppy.value.on('before-upload', (files) => {
79
86
  emits('beforeUpload', files);
80
87
  });
81
88
 
82
89
  uppy.value.on('upload-success', (file, response) => {
90
+ uploadingFiles.value = Math.max(0, uploadingFiles.value - 1);
91
+
83
92
  const result = response.body ?? response;
84
93
  if (props.multiple) {
85
94
  const currentValues = Array.isArray(modelValue.value) ? modelValue.value : [];
@@ -91,21 +100,19 @@ uppy.value.on('upload-success', (file, response) => {
91
100
  });
92
101
 
93
102
  uppy.value.on('file-added', (file) => emits('file-added', file));
103
+
94
104
  uppy.value.on('file-removed', (file) => {
95
105
  const serverResponse = file.response?.body ?? file.response;
96
106
 
97
107
  if (props.multiple && Array.isArray(modelValue.value)) {
98
108
  modelValue.value = modelValue.value.filter(item => {
99
- // مقایسه بر اساس شناسه یا کل آبجکت (بسته به ساختار ارسالی سرور شما)
100
- // اگر سرور ID برمی‌گرداند: return item.id !== serverResponse.id;
101
- // اگر مستقیماً خود آبجکت است:
102
109
  return JSON.stringify(item) !== JSON.stringify(serverResponse);
103
110
  });
104
111
  } else {
105
112
  modelValue.value = null;
106
113
  }
107
114
 
108
- if (serverResponse) {
115
+ if (serverResponse && !isResetting.value && !isUnmounting.value) {
109
116
  fetch(props.url, {
110
117
  method: 'DELETE',
111
118
  headers: {
@@ -120,23 +127,37 @@ uppy.value.on('file-removed', (file) => {
120
127
  });
121
128
 
122
129
  uppy.value.on('progress', (progress) => {
123
- form.value['uploading'] = (progress >= 100 || progress <=0) ? null : progress;
130
+ if (progress <= 0) {
131
+ form.value['uploading'] = null;
132
+ } else if (progress >= 100) {
133
+ // هنوز منتظر جواب سرور - uploading رو null نکن
134
+ form.value['uploading'] = uploadingFiles.value > 0 ? 99 : null;
135
+ } else {
136
+ form.value['uploading'] = progress;
137
+ }
138
+
124
139
  emits('progress', progress)
125
140
  });
126
141
  uppy.value.on('upload-progress', (file, progress) => emits('upload-progress', file, progress));
127
142
  uppy.value.on('upload-pause', (file, progress) => emits('upload-pause', file, progress));
128
143
  uppy.value.on('cancel-all', () => emits('cancel-all'));
129
- uppy.value.on('retry-all', () => emits('retry-all', ));
144
+ uppy.value.on('retry-all', () => emits('retry-all'));
130
145
  uppy.value.on('upload-stalled', (error, files) => emits('upload-stalled', error, files));
131
146
  uppy.value.on('upload-retry', (file) => emits('upload-retry', file));
132
147
 
133
- uppy.value.on('complete', (result) => emits('complete', result));
148
+ uppy.value.on('complete', (result) => {
149
+ uploadingFiles.value = 0;
150
+ form.value['uploading'] = null;
151
+ emits('complete', result);
152
+ });
134
153
 
135
154
  uppy.value.on('error', (error) => {
136
155
  emits('error', error)
137
156
  });
138
157
 
139
158
  uppy.value.on('upload-error', (file, error, response) => {
159
+ uploadingFiles.value = Math.max(0, uploadingFiles.value - 1);
160
+
140
161
  const errorMessage = JSON.parse(response.response)?.message ?? error;
141
162
  handleError(errorMessage);
142
163
  emits('upload-error', file, error, response, errorMessage)
@@ -147,11 +168,16 @@ uppy.value.on('restriction-failed', (file, error) => {
147
168
  emits('restriction-failed', file, error);
148
169
  });
149
170
 
171
+ watch(() => form.value?.wasSuccessful, (newVal, oldVal) => {
172
+ if (newVal === true && oldVal === false) {
173
+ resetUppy();
174
+ }
175
+ });
176
+
150
177
  onMounted(() => {
151
- let XHR;
152
178
  if (props.useXHR) {
153
179
  import('@uppy/xhr-upload').then(module => {
154
- XHR = module.default; // چون اکثر پکیج‌ها default export دارند
180
+ const XHR = module.default;
155
181
  uppy.value.use(XHR, {
156
182
  method: 'POST',
157
183
  endpoint: props.url,
@@ -163,17 +189,27 @@ onMounted(() => {
163
189
  });
164
190
  }
165
191
 
166
- if(uppy.value?.opts?.restrictions){
192
+ if (uppy.value?.opts?.restrictions) {
167
193
  restrictionCaption.value = buildRestrictionsCaption(uppy.value.opts.restrictions)
168
194
  }
169
195
  });
170
196
 
171
197
  onBeforeUnmount(() => {
198
+ isUnmounting.value = true;
172
199
  if (uppy.value) {
173
200
  uppy.value.destroy();
174
201
  }
175
202
  });
176
203
 
204
+ function resetUppy() {
205
+ if (uppy.value) {
206
+ isResetting.value = true;
207
+ uppy.value.cancelAll();
208
+ isResetting.value = false;
209
+ modelValue.value = props.multiple ? [] : null;
210
+ }
211
+ }
212
+
177
213
  function handleError(error) {
178
214
  if (props.errorHandler) {
179
215
  props.errorHandler(error);
@@ -193,20 +229,13 @@ function showError(message) {
193
229
  }
194
230
  }
195
231
 
196
- function formatBytesToKB(size) {
197
- if (!size) return null;
198
- return Math.round(size / 1024);
199
- }
200
-
201
232
  const niceBytesUnits = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
202
- function niceBytes(x){
233
+ function niceBytes(x) {
203
234
  let l = 0, n = parseInt(x, 10) || 0;
204
-
205
- while(n >= 1024 && ++l){
206
- n = n/1024;
235
+ while (n >= 1024 && ++l) {
236
+ n = n / 1024;
207
237
  }
208
-
209
- return(n.toFixed(n < 10 && l > 0 ? 1 : 0) + niceBytesUnits[l]);
238
+ return (n.toFixed(n < 10 && l > 0 ? 1 : 0) + niceBytesUnits[l]);
210
239
  }
211
240
 
212
241
  function buildRestrictionsCaption(restrictions) {
@@ -223,7 +252,6 @@ function buildRestrictionsCaption(restrictions) {
223
252
 
224
253
  const parts = [];
225
254
 
226
- // پسوندها
227
255
  if (allowedFileTypes && allowedFileTypes.length) {
228
256
  const types = allowedFileTypes
229
257
  .map(type => type.replace('.', ''))
@@ -231,8 +259,7 @@ function buildRestrictionsCaption(restrictions) {
231
259
  parts.push(`فقط فایل‌های ${types}`);
232
260
  }
233
261
 
234
- // تعداد فایل
235
- if (maxNumberOfFiles) {
262
+ if (maxNumberOfFiles && maxNumberOfFiles > 1) {
236
263
  parts.push(`امکان انتخاب حداکثر ${maxNumberOfFiles} فایل`);
237
264
  }
238
265
 
@@ -240,7 +267,6 @@ function buildRestrictionsCaption(restrictions) {
240
267
  parts.push(`حداقل ${minNumberOfFiles} فایل نیاز است`);
241
268
  }
242
269
 
243
- // حجم هر فایل
244
270
  if (maxFileSize) {
245
271
  parts.push(`با حداکثر حجم ${niceBytes(maxFileSize)} برای هر فایل`);
246
272
  }
@@ -249,7 +275,6 @@ function buildRestrictionsCaption(restrictions) {
249
275
  parts.push(`با حداقل حجم ${niceBytes(minFileSize)} برای هر فایل`);
250
276
  }
251
277
 
252
- // مجموع حجم
253
278
  if (maxTotalFileSize) {
254
279
  parts.push(`و مجموع حجم کل حداکثر ${niceBytes(maxTotalFileSize)}`);
255
280
  }
@@ -258,7 +283,6 @@ function buildRestrictionsCaption(restrictions) {
258
283
 
259
284
  return parts.join('، ') + ' مجاز است.';
260
285
  }
261
-
262
286
  </script>
263
287
 
264
288
  <style>
@@ -283,9 +307,8 @@ function buildRestrictionsCaption(restrictions) {
283
307
  margin-bottom: 5px;
284
308
  }
285
309
 
286
- .uppy-input-area .uppy-input-area--caption{
310
+ .uppy-input-area .uppy-input-area--caption {
287
311
  text-align: center;
288
312
  margin: 5px 0;
289
313
  }
290
-
291
314
  </style>