@eslamdevui/ui 3.3.2-beta.2 → 3.3.2-beta.3

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.
@@ -50,13 +50,7 @@ export default {
50
50
  "fileWrapper": "flex flex-col min-w-0",
51
51
  "fileName": "text-default truncate",
52
52
  "fileSize": "text-muted truncate",
53
- "fileProgress": "flex items-center gap-2 mt-1",
54
- "fileProgressText": "text-xs text-muted whitespace-nowrap",
55
- "fileError": "text-xs text-red-500 mt-1",
56
- "fileActions": "flex items-center gap-1",
57
- "fileTrailingButton": "",
58
- "fileUploadButton": "",
59
- "fileCancelButton": ""
53
+ "fileTrailingButton": ""
60
54
  },
61
55
  "variants": {
62
56
  "color": {
@@ -110,15 +104,12 @@ export default {
110
104
  "root": "gap-2 items-start",
111
105
  "files": "flex flex-col w-full gap-2",
112
106
  "file": "min-w-0 flex items-center border border-default rounded-md w-full",
113
- "fileActions": "ms-auto"
107
+ "fileTrailingButton": "ms-auto"
114
108
  },
115
109
  "grid": {
116
110
  "fileWrapper": "hidden",
117
111
  "fileLeadingAvatar": "size-full rounded-lg",
118
- "fileActions": "absolute -top-1.5 -end-1.5",
119
- "fileTrailingButton": "p-0 rounded-full border-2 border-bg",
120
- "fileUploadButton": "p-0 rounded-full border-2 border-bg",
121
- "fileCancelButton": "p-0 rounded-full border-2 border-bg"
112
+ "fileTrailingButton": "absolute -top-1.5 -end-1.5 p-0 rounded-full border-2 border-bg"
122
113
  }
123
114
  },
124
115
  "position": {
@@ -139,9 +130,6 @@ export default {
139
130
  },
140
131
  "disabled": {
141
132
  "true": "cursor-not-allowed opacity-75"
142
- },
143
- "showProgress": {
144
- "true": ""
145
133
  }
146
134
  },
147
135
  "compoundVariants": [
@@ -212,35 +200,35 @@ export default {
212
200
  "size": "xs" as typeof size[number],
213
201
  "layout": "list" as typeof layout[number],
214
202
  "class": {
215
- "fileActions": "-me-1"
203
+ "fileTrailingButton": "-me-1"
216
204
  }
217
205
  },
218
206
  {
219
207
  "size": "sm" as typeof size[number],
220
208
  "layout": "list" as typeof layout[number],
221
209
  "class": {
222
- "fileActions": "-me-1.5"
210
+ "fileTrailingButton": "-me-1.5"
223
211
  }
224
212
  },
225
213
  {
226
214
  "size": "md" as typeof size[number],
227
215
  "layout": "list" as typeof layout[number],
228
216
  "class": {
229
- "fileActions": "-me-1.5"
217
+ "fileTrailingButton": "-me-1.5"
230
218
  }
231
219
  },
232
220
  {
233
221
  "size": "lg" as typeof size[number],
234
222
  "layout": "list" as typeof layout[number],
235
223
  "class": {
236
- "fileActions": "-me-2"
224
+ "fileTrailingButton": "-me-2"
237
225
  }
238
226
  },
239
227
  {
240
228
  "size": "xl" as typeof size[number],
241
229
  "layout": "list" as typeof layout[number],
242
230
  "class": {
243
- "fileActions": "-me-2"
231
+ "fileTrailingButton": "-me-2"
244
232
  }
245
233
  },
246
234
  {
@@ -297,23 +285,6 @@ export default {
297
285
  "interactive": true,
298
286
  "disabled": false,
299
287
  "class": "hover:bg-elevated/25"
300
- },
301
- {
302
- "showProgress": true,
303
- "layout": "list" as typeof layout[number],
304
- "class": {
305
- "fileWrapper": "flex-col",
306
- "fileProgress": "w-full"
307
- }
308
- },
309
- {
310
- "showProgress": true,
311
- "layout": "grid" as typeof layout[number],
312
- "class": {
313
- "fileWrapper": "absolute inset-0 bg-black/50 flex flex-col items-center justify-center rounded-lg",
314
- "fileProgress": "w-3/4",
315
- "fileProgressText": "text-white font-medium"
316
- }
317
288
  }
318
289
  ],
319
290
  "defaultVariants": {
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eslamdevui/ui",
3
- "version": "3.3.2-beta.2",
3
+ "version": "3.3.2-beta.3",
4
4
  "docs": "https://ui.nuxt.com/getting-started/installation/nuxt",
5
5
  "configKey": "ui",
6
6
  "compatibility": {
package/dist/module.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { defineNuxtModule, createResolver, addVitePlugin, addPlugin, hasNuxtModule, addComponentsDir, addImportsDir, installModule } from '@nuxt/kit';
2
2
  import { defu } from 'defu';
3
- import { d as defaultOptions, r as resolveColors, a as getDefaultUiConfig, b as addTemplates } from './shared/ui.DdcmBFzX.mjs';
3
+ import { d as defaultOptions, r as resolveColors, a as getDefaultUiConfig, b as addTemplates } from './shared/ui.CgUuvRXi.mjs';
4
4
  import '../dist/runtime/utils/index.js';
5
5
  import 'node:url';
6
6
  import 'scule';
@@ -8,7 +8,7 @@ import 'tailwindcss/colors';
8
8
  import 'knitwork';
9
9
 
10
10
  const name = "@eslamdevui/ui";
11
- const version = "3.3.2-beta.2";
11
+ const version = "3.3.2-beta.3";
12
12
 
13
13
  function generateProseComponentMap(components) {
14
14
  return components.reduce((map, component) => {
@@ -3,7 +3,7 @@ import theme from "#build/ui/file-upload";
3
3
  </script>
4
4
 
5
5
  <script setup>
6
- import { computed, watch, ref, nextTick, readonly } from "vue";
6
+ import { computed, watch } from "vue";
7
7
  import { Primitive } from "reka-ui";
8
8
  import { createReusableTemplate } from "@vueuse/core";
9
9
  import { useAppConfig } from "#imports";
@@ -13,7 +13,6 @@ import { tv } from "../utils/tv";
13
13
  import UAvatar from "./Avatar.vue";
14
14
  import UButton from "./Button.vue";
15
15
  import UIcon from "./Icon.vue";
16
- import UProgress from "./Progress.vue";
17
16
  defineOptions({ inheritAttrs: false });
18
17
  const props = defineProps({
19
18
  as: { type: null, required: false },
@@ -38,28 +37,20 @@ const props = defineProps({
38
37
  fileIcon: { type: String, required: false },
39
38
  fileDelete: { type: [Boolean, Object], required: false },
40
39
  fileDeleteIcon: { type: String, required: false },
41
- showProgress: { type: Boolean, required: false, default: false },
42
- allowCancel: { type: Boolean, required: false, default: true },
43
- uploadFn: { type: Function, required: false },
44
- autoUpload: { type: Boolean, required: false, default: false },
45
- uploadTimeout: { type: Number, required: false, default: 3e4 },
46
- maxConcurrentUploads: { type: Number, required: false, default: 3 },
47
40
  class: { type: null, required: false },
48
41
  ui: { type: null, required: false }
49
42
  });
50
- const emits = defineEmits(["update:modelValue", "change", "upload:start", "upload:progress", "upload:success", "upload:error", "upload:cancel", "upload:complete"]);
43
+ const emits = defineEmits(["update:modelValue", "change"]);
51
44
  const slots = defineSlots();
52
45
  const modelValue = defineModel({ type: null });
53
46
  const appConfig = useAppConfig();
54
- const activeUploads = ref(0);
55
- const uploadQueue = ref([]);
56
47
  const [DefineFilesTemplate, ReuseFilesTemplate] = createReusableTemplate();
57
48
  const { isDragging, open, inputRef, dropzoneRef } = useFileUpload({
58
49
  accept: props.accept,
59
50
  reset: props.reset,
60
51
  multiple: props.multiple,
61
52
  dropzone: props.dropzone,
62
- onUpdate: onFileUpdate
53
+ onUpdate
63
54
  });
64
55
  const { emitFormInput, emitFormChange, id, name, disabled, ariaAttrs } = useFormField(props);
65
56
  const variant = computed(() => props.multiple ? "area" : props.variant);
@@ -73,9 +64,6 @@ const position = computed(() => {
73
64
  }
74
65
  return props.position;
75
66
  });
76
- const shouldShowProgress = computed(() => {
77
- return props.showProgress || props.autoUpload;
78
- });
79
67
  const ui = computed(() => tv({ extend: tv(theme), ...appConfig.ui?.fileUpload || {} })({
80
68
  dropzone: props.dropzone,
81
69
  interactive: props.interactive,
@@ -86,8 +74,7 @@ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.ui?.fileUpload ||
86
74
  position: position.value,
87
75
  multiple: props.multiple,
88
76
  highlight: props.highlight,
89
- disabled: props.disabled,
90
- showProgress: shouldShowProgress.value
77
+ disabled: props.disabled
91
78
  }));
92
79
  function createObjectUrl(file) {
93
80
  return URL.createObjectURL(file);
@@ -103,265 +90,77 @@ function formatFileSize(bytes) {
103
90
  const formattedSize = i === 0 ? size.toString() : size.toFixed(0);
104
91
  return `${formattedSize}${sizes[i]}`;
105
92
  }
106
- function createFileUploadObject(file) {
107
- return {
108
- file,
109
- progress: 0,
110
- uploading: false,
111
- error: void 0,
112
- abortController: void 0
113
- };
114
- }
115
- async function onFileUpdate(files, reset = false) {
116
- const fileUploads = files.map(createFileUploadObject);
93
+ function onUpdate(files, reset = false) {
117
94
  if (props.multiple) {
118
95
  if (reset) {
119
- modelValue.value = fileUploads;
96
+ modelValue.value = files;
120
97
  } else {
121
98
  const existingFiles = modelValue.value || [];
122
- modelValue.value = [...existingFiles, ...fileUploads];
99
+ modelValue.value = [...existingFiles, ...files || []];
123
100
  }
124
101
  } else {
125
- modelValue.value = fileUploads?.[0];
102
+ modelValue.value = files?.[0];
126
103
  }
127
104
  const event = new Event("change", { target: { value: modelValue.value } });
128
105
  emits("change", event);
129
106
  emitFormChange();
130
107
  emitFormInput();
131
- if (props.autoUpload && props.uploadFn) {
132
- await nextTick();
133
- const newFiles = props.multiple ? fileUploads : fileUploads[0] ? [fileUploads[0]] : [];
134
- for (let i = 0; i < newFiles.length; i++) {
135
- const actualIndex = props.multiple && !reset ? (modelValue.value?.length || 0) - newFiles.length + i : i;
136
- if (props.multiple) {
137
- if (newFiles[i]) {
138
- queueUpload(actualIndex, newFiles[i]);
139
- }
140
- } else {
141
- uploadFile(actualIndex);
142
- }
143
- }
144
- }
145
- }
146
- function queueUpload(index, fileUpload) {
147
- if (!props.multiple) {
148
- uploadFile(index);
149
- return;
150
- }
151
- if (activeUploads.value < props.maxConcurrentUploads) {
152
- uploadFile(index);
153
- } else {
154
- uploadQueue.value.push({ index, fileUpload });
155
- }
156
- }
157
- function processUploadQueue() {
158
- while (uploadQueue.value.length > 0 && activeUploads.value < props.maxConcurrentUploads) {
159
- const queueItem = uploadQueue.value.shift();
160
- if (queueItem) {
161
- uploadFile(queueItem.index);
162
- }
163
- }
164
108
  }
165
109
  function removeFile(index) {
166
110
  if (!modelValue.value) {
167
111
  return;
168
112
  }
169
113
  if (!props.multiple || index === void 0) {
170
- const singleFile = modelValue.value;
171
- if (singleFile.abortController) {
172
- singleFile.abortController.abort();
173
- }
174
- onFileUpdate([], true);
114
+ onUpdate([], true);
175
115
  return;
176
116
  }
177
117
  const files = [...modelValue.value];
178
- const file = files[index];
179
- if (file?.abortController) {
180
- file.abortController.abort();
181
- }
182
- uploadQueue.value = uploadQueue.value.filter((item) => item.index !== index);
183
118
  files.splice(index, 1);
184
- onFileUpdate(files.map((f) => f.file), true);
185
- }
186
- async function uploadFile(index) {
187
- if (!props.uploadFn || !modelValue.value) return;
188
- const files = props.multiple ? modelValue.value : [modelValue.value];
189
- const fileUpload = files[index];
190
- if (!fileUpload || fileUpload.uploading) return;
191
- activeUploads.value++;
192
- const abortController = new AbortController();
193
- fileUpload.abortController = abortController;
194
- fileUpload.uploading = true;
195
- fileUpload.progress = 0;
196
- fileUpload.error = void 0;
197
- emits("upload:start", fileUpload, index);
198
- const timeoutId = setTimeout(() => {
199
- if (!abortController.signal.aborted) {
200
- abortController.abort();
201
- }
202
- }, props.uploadTimeout);
203
- try {
204
- const result = await props.uploadFn(
205
- fileUpload.file,
206
- (progress) => {
207
- fileUpload.progress = Math.min(100, Math.max(0, progress));
208
- emits("upload:progress", fileUpload, fileUpload.progress, index);
209
- },
210
- abortController.signal
211
- );
212
- clearTimeout(timeoutId);
213
- fileUpload.uploading = false;
214
- fileUpload.progress = 100;
215
- emits("upload:success", fileUpload, result, index);
216
- } catch (error) {
217
- clearTimeout(timeoutId);
218
- fileUpload.uploading = false;
219
- if (abortController.signal.aborted) {
220
- emits("upload:cancel", fileUpload, index);
221
- } else {
222
- fileUpload.error = error instanceof Error ? error.message : "Upload failed";
223
- emits("upload:error", fileUpload, fileUpload.error, index);
224
- }
225
- } finally {
226
- fileUpload.abortController = void 0;
227
- activeUploads.value--;
228
- if (props.multiple) {
229
- processUploadQueue();
230
- }
231
- if (props.multiple && activeUploads.value === 0 && uploadQueue.value.length === 0) {
232
- emits("upload:complete", files);
233
- }
234
- }
235
- }
236
- function cancelUpload(index) {
237
- if (!modelValue.value) return;
238
- const files = props.multiple ? modelValue.value : [modelValue.value];
239
- const fileUpload = files[index];
240
- if (fileUpload?.abortController) {
241
- fileUpload.abortController.abort();
242
- }
243
- uploadQueue.value = uploadQueue.value.filter((item) => item.index !== index);
244
- }
245
- function shouldShowFileProgress(fileUpload) {
246
- return shouldShowProgress.value && (fileUpload.uploading || (fileUpload.progress ?? 0) > 0 || !!fileUpload.error);
119
+ onUpdate(files, true);
247
120
  }
248
121
  watch(modelValue, (newValue) => {
249
122
  const hasModelReset = !Array.isArray(newValue) || !newValue.length;
250
123
  if (hasModelReset && inputRef.value) {
251
124
  inputRef.value.value = "";
252
- uploadQueue.value = [];
253
- activeUploads.value = 0;
254
125
  }
255
126
  });
256
127
  defineExpose({
257
128
  inputRef,
258
- dropzoneRef,
259
- uploadFile,
260
- cancelUpload,
261
- activeUploads: readonly(activeUploads),
262
- uploadQueue: readonly(uploadQueue)
129
+ dropzoneRef
263
130
  });
264
131
  </script>
265
132
 
266
133
  <template>
267
134
  <DefineFilesTemplate>
268
135
  <template v-if="modelValue && (Array.isArray(modelValue) ? modelValue.length : true)">
269
- <slot
270
- name="files-top"
271
- :files="modelValue"
272
- :open="open"
273
- :remove-file="removeFile"
274
- :upload-file="uploadFile"
275
- :cancel-upload="cancelUpload"
276
- />
136
+ <slot name="files-top" :files="modelValue" :open="open" :remove-file="removeFile" />
277
137
 
278
138
  <div :class="ui.files({ class: props.ui?.files })">
279
139
  <slot name="files" :files="modelValue">
280
- <div
281
- v-for="(fileUpload, index) in Array.isArray(modelValue) ? modelValue : [modelValue]"
282
- :key="fileUpload.file.name + index"
283
- :class="ui.file({ class: props.ui?.file })"
284
- >
285
- <slot name="file" :file-upload="fileUpload" :index="index">
286
- <slot name="file-leading" :file-upload="fileUpload" :index="index">
287
- <UAvatar
288
- :src="createObjectUrl(fileUpload.file)"
289
- :icon="fileIcon || appConfig.ui.icons.file"
290
- :size="props.size"
291
- :class="ui.fileLeadingAvatar({ class: props.ui?.fileLeadingAvatar })"
292
- />
140
+ <div v-for="(file, index) in Array.isArray(modelValue) ? modelValue : [modelValue]" :key="file.name" :class="ui.file({ class: props.ui?.file })">
141
+ <slot name="file" :file="file" :index="index">
142
+ <slot name="file-leading" :file="file" :index="index">
143
+ <UAvatar :src="createObjectUrl(file)" :icon="fileIcon || appConfig.ui.icons.file" :size="props.size" :class="ui.fileLeadingAvatar({ class: props.ui?.fileLeadingAvatar })" />
293
144
  </slot>
294
145
 
295
146
  <div :class="ui.fileWrapper({ class: props.ui?.fileWrapper })">
296
147
  <span :class="ui.fileName({ class: props.ui?.fileName })">
297
- <slot name="file-name" :file-upload="fileUpload" :index="index">
298
- {{ fileUpload.file.name }}
148
+ <slot name="file-name" :file="file" :index="index">
149
+ {{ file.name }}
299
150
  </slot>
300
151
  </span>
301
152
 
302
153
  <span :class="ui.fileSize({ class: props.ui?.fileSize })">
303
- <slot name="file-size" :file-upload="fileUpload" :index="index">
304
- {{ formatFileSize(fileUpload.file.size) }}
154
+ <slot name="file-size" :file="file" :index="index">
155
+ {{ formatFileSize(file.size) }}
305
156
  </slot>
306
157
  </span>
307
-
308
- <!-- Progress bar - Now shows automatically when autoUpload is true -->
309
- <div
310
- v-if="shouldShowFileProgress(fileUpload)"
311
- :class="ui.fileProgress({ class: props.ui?.fileProgress })"
312
- >
313
- <slot name="file-progress" :file-upload="fileUpload" :index="index">
314
- <UProgress
315
- :model-value="fileUpload.progress || 0"
316
- :color="fileUpload.error ? 'error' : color"
317
- :size="size === 'xs' ? 'xs' : 'sm'"
318
- />
319
- <span :class="ui.fileProgressText({ class: props.ui?.fileProgressText })">
320
- {{ Math.round(fileUpload.progress || 0) }}%
321
- </span>
322
- </slot>
323
- </div>
324
-
325
- <!-- Error message -->
326
- <div v-if="fileUpload.error" :class="ui.fileError({ class: props.ui?.fileError })">
327
- {{ fileUpload.error }}
328
- </div>
329
-
330
- <!-- Upload status indicator -->
331
- <div v-if="autoUpload && fileUpload.uploading" class="text-xs text-gray-500 mt-1">
332
- Uploading...
333
- </div>
334
158
  </div>
335
159
 
336
- <slot name="file-trailing" :file-upload="fileUpload" :index="index">
337
- <div :class="ui.fileActions({ class: props.ui?.fileActions })">
338
- <!-- Upload button (if not auto-upload and uploadFn provided) -->
339
- <UButton
340
- v-if="uploadFn && !autoUpload && !fileUpload.uploading && !(fileUpload.progress ?? 0)"
341
- color="primary"
342
- variant="ghost"
343
- :size="layout === 'grid' ? 'xs' : size"
344
- :trailing-icon="appConfig.ui.icons.upload || 'i-lucide-upload'"
345
- :class="ui.fileUploadButton({ class: props.ui?.fileUploadButton })"
346
- @click.stop.prevent="uploadFile(index)"
347
- />
348
-
349
- <!-- Cancel button (during upload) -->
350
- <UButton
351
- v-if="allowCancel && fileUpload.uploading"
352
- color="neutral"
353
- variant="ghost"
354
- :size="layout === 'grid' ? 'xs' : size"
355
- :trailing-icon="appConfig.ui.icons.close || 'i-lucide-x'"
356
- :class="ui.fileCancelButton({ class: props.ui?.fileCancelButton })"
357
- @click.stop.prevent="cancelUpload(index)"
358
- />
359
-
360
- <!-- Delete button -->
361
- <UButton
362
- v-if="!fileUpload.uploading"
363
- color="neutral"
364
- v-bind="{
160
+ <slot name="file-trailing" :file="file" :index="index">
161
+ <UButton
162
+ color="neutral"
163
+ v-bind="{
365
164
  ...layout === 'grid' ? {
366
165
  variant: 'solid',
367
166
  size: 'xs'
@@ -371,30 +170,22 @@ defineExpose({
371
170
  },
372
171
  ...typeof fileDelete === 'object' ? fileDelete : void 0
373
172
  }"
374
- :trailing-icon="fileDeleteIcon || appConfig.ui.icons.close"
375
- :class="ui.fileTrailingButton({ class: props.ui?.fileTrailingButton })"
376
- @click.stop.prevent="removeFile(index)"
377
- />
378
- </div>
173
+ :trailing-icon="fileDeleteIcon || appConfig.ui.icons.close"
174
+ :class="ui.fileTrailingButton({ class: props.ui?.fileTrailingButton })"
175
+ @click.stop.prevent="removeFile(index)"
176
+ />
379
177
  </slot>
380
178
  </slot>
381
179
  </div>
382
180
  </slot>
383
181
  </div>
384
182
 
385
- <slot
386
- name="files-bottom"
387
- :files="modelValue"
388
- :open="open"
389
- :remove-file="removeFile"
390
- :upload-file="uploadFile"
391
- :cancel-upload="cancelUpload"
392
- />
183
+ <slot name="files-bottom" :files="modelValue" :open="open" :remove-file="removeFile" />
393
184
  </template>
394
185
  </DefineFilesTemplate>
395
186
 
396
187
  <Primitive :as="as" :class="ui.root({ class: [props.ui?.root, props.class] })">
397
- <slot :open="open" :remove-file="removeFile" :upload-file="uploadFile" :cancel-upload="cancelUpload">
188
+ <slot :open="open" :remove-file="removeFile">
398
189
  <component
399
190
  :is="variant === 'button' ? 'button' : 'div'"
400
191
  ref="dropzoneRef"
@@ -406,22 +197,10 @@ defineExpose({
406
197
  >
407
198
  <ReuseFilesTemplate v-if="position === 'inside'" />
408
199
 
409
- <div
410
- v-if="position === 'inside' ? multiple ? !modelValue?.length : !modelValue : true"
411
- :class="ui.wrapper({ class: props.ui?.wrapper })"
412
- >
200
+ <div v-if="position === 'inside' ? multiple ? !modelValue?.length : !modelValue : true" :class="ui.wrapper({ class: props.ui?.wrapper })">
413
201
  <slot name="leading">
414
- <UIcon
415
- v-if="variant === 'button'"
416
- :name="icon || appConfig.ui.icons.upload"
417
- :class="ui.icon({ class: props.ui?.icon })"
418
- />
419
- <UAvatar
420
- v-else
421
- :icon="icon || appConfig.ui.icons.upload"
422
- :size="props.size"
423
- :class="ui.avatar({ class: props.ui?.avatar })"
424
- />
202
+ <UIcon v-if="variant === 'button'" :name="icon || appConfig.ui.icons.upload" :class="ui.icon({ class: props.ui?.icon })" />
203
+ <UAvatar v-else :icon="icon || appConfig.ui.icons.upload" :size="props.size" :class="ui.avatar({ class: props.ui?.avatar })" />
425
204
  </slot>
426
205
 
427
206
  <template v-if="variant !== 'button'">
@@ -437,14 +216,7 @@ defineExpose({
437
216
  </div>
438
217
 
439
218
  <div v-if="!!slots.actions" :class="ui.actions({ class: props.ui?.actions })">
440
- <slot
441
- name="actions"
442
- :files="modelValue"
443
- :open="open"
444
- :remove-file="removeFile"
445
- :upload-file="uploadFile"
446
- :cancel-upload="cancelUpload"
447
- />
219
+ <slot name="actions" :files="modelValue" :open="open" :remove-file="removeFile" />
448
220
  </div>
449
221
  </template>
450
222
  </div>
@@ -4,13 +4,6 @@ import theme from '#build/ui/file-upload';
4
4
  import type { ButtonProps } from '../types';
5
5
  import type { ComponentConfig } from '../types/utils';
6
6
  type FileUpload = ComponentConfig<typeof theme, AppConfig, 'fileUpload'>;
7
- export interface FileUploadFile {
8
- file: File;
9
- progress?: number;
10
- uploading?: boolean;
11
- error?: string;
12
- abortController?: AbortController;
13
- }
14
7
  export interface FileUploadProps<M extends boolean = false> {
15
8
  /**
16
9
  * The element or component this component should render as.
@@ -96,55 +89,18 @@ export interface FileUploadProps<M extends boolean = false> {
96
89
  * @IconifyIcon
97
90
  */
98
91
  fileDeleteIcon?: string;
99
- /**
100
- * Enable upload progress tracking
101
- * @defaultValue false
102
- */
103
- showProgress?: boolean;
104
- /**
105
- * Allow cancelling uploads in progress
106
- * @defaultValue true
107
- */
108
- allowCancel?: boolean;
109
- /**
110
- * Upload function that returns a promise with progress callback
111
- */
112
- uploadFn?: (file: File, onProgress: (progress: number) => void, signal?: AbortSignal) => Promise<any>;
113
- /**
114
- * Auto-start upload when files are selected
115
- * @defaultValue false
116
- */
117
- autoUpload?: boolean;
118
- /**
119
- * Upload timeout in milliseconds
120
- * @defaultValue 30000
121
- */
122
- uploadTimeout?: number;
123
- /**
124
- * Max concurrent uploads when multiple is true
125
- * @defaultValue 3
126
- */
127
- maxConcurrentUploads?: number;
128
92
  class?: any;
129
93
  ui?: FileUpload['slots'];
130
94
  }
131
95
  export interface FileUploadEmits<M extends boolean = false> {
132
- 'update:modelValue': [payload: M extends true ? FileUploadFile[] : FileUploadFile | null];
96
+ 'update:modelValue': [payload: M extends true ? File[] : File | null];
133
97
  'change': [event: Event];
134
- 'upload:start': [file: FileUploadFile, index: number];
135
- 'upload:progress': [file: FileUploadFile, progress: number, index: number];
136
- 'upload:success': [file: FileUploadFile, result: any, index: number];
137
- 'upload:error': [file: FileUploadFile, error: string, index: number];
138
- 'upload:cancel': [file: FileUploadFile, index: number];
139
- 'upload:complete': [files: FileUploadFile[]];
140
98
  }
141
- type FileUploadFiles<M> = (M extends true ? FileUploadFile[] : FileUploadFile) | null;
99
+ type FileUploadFiles<M> = (M extends true ? File[] : File) | null;
142
100
  export interface FileUploadSlots<M extends boolean = false> {
143
101
  'default'(props: {
144
102
  open: UseFileDialogReturn['open'];
145
103
  removeFile: (index?: number) => void;
146
- uploadFile: (index: number) => void;
147
- cancelUpload: (index: number) => void;
148
104
  }): any;
149
105
  'leading'(props?: {}): any;
150
106
  'label'(props?: {}): any;
@@ -153,8 +109,6 @@ export interface FileUploadSlots<M extends boolean = false> {
153
109
  files?: FileUploadFiles<M>;
154
110
  open: UseFileDialogReturn['open'];
155
111
  removeFile: (index?: number) => void;
156
- uploadFile: (index: number) => void;
157
- cancelUpload: (index: number) => void;
158
112
  }): any;
159
113
  'files'(props: {
160
114
  files?: FileUploadFiles<M>;
@@ -163,38 +117,30 @@ export interface FileUploadSlots<M extends boolean = false> {
163
117
  files?: FileUploadFiles<M>;
164
118
  open: UseFileDialogReturn['open'];
165
119
  removeFile: (index?: number) => void;
166
- uploadFile: (index: number) => void;
167
- cancelUpload: (index: number) => void;
168
120
  }): any;
169
121
  'files-bottom'(props: {
170
122
  files?: FileUploadFiles<M>;
171
123
  open: UseFileDialogReturn['open'];
172
124
  removeFile: (index?: number) => void;
173
- uploadFile: (index: number) => void;
174
- cancelUpload: (index: number) => void;
175
125
  }): any;
176
126
  'file'(props: {
177
- fileUpload: FileUploadFile;
127
+ file: File;
178
128
  index: number;
179
129
  }): any;
180
130
  'file-leading'(props: {
181
- fileUpload: FileUploadFile;
131
+ file: File;
182
132
  index: number;
183
133
  }): any;
184
134
  'file-name'(props: {
185
- fileUpload: FileUploadFile;
135
+ file: File;
186
136
  index: number;
187
137
  }): any;
188
138
  'file-size'(props: {
189
- fileUpload: FileUploadFile;
190
- index: number;
191
- }): any;
192
- 'file-progress'(props: {
193
- fileUpload: FileUploadFile;
139
+ file: File;
194
140
  index: number;
195
141
  }): any;
196
142
  'file-trailing'(props: {
197
- fileUpload: FileUploadFile;
143
+ file: File;
198
144
  index: number;
199
145
  }): any;
200
146
  }
@@ -202,160 +148,16 @@ declare const _default: <M extends boolean = false>(__VLS_props: NonNullable<Awa
202
148
  props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
203
149
  readonly onChange?: ((event: Event) => any) | undefined;
204
150
  readonly "onUpdate:modelValue"?: ((...args: unknown[]) => any) | undefined;
205
- readonly "onUpload:start"?: ((file: FileUploadFile, index: number) => any) | undefined;
206
- readonly "onUpload:progress"?: ((file: FileUploadFile, progress: number, index: number) => any) | undefined;
207
- readonly "onUpload:success"?: ((file: FileUploadFile, result: any, index: number) => any) | undefined;
208
- readonly "onUpload:error"?: ((file: FileUploadFile, error: string, index: number) => any) | undefined;
209
- readonly "onUpload:cancel"?: ((file: FileUploadFile, index: number) => any) | undefined;
210
- readonly "onUpload:complete"?: ((files: FileUploadFile[]) => any) | undefined;
211
- } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onChange" | "onUpdate:modelValue" | "onUpload:start" | "onUpload:progress" | "onUpload:success" | "onUpload:error" | "onUpload:cancel" | "onUpload:complete"> & (FileUploadProps<M> & {
212
- modelValue?: (M extends true ? FileUploadFile[] : FileUploadFile) | null;
151
+ } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onChange" | "onUpdate:modelValue"> & (FileUploadProps<M> & {
152
+ modelValue?: (M extends true ? File[] : File) | null;
213
153
  }) & Partial<{}>> & import("vue").PublicProps;
214
154
  expose(exposed: import("vue").ShallowUnwrapRef<{
215
155
  inputRef: import("vue").Ref<HTMLInputElement | undefined, HTMLInputElement | undefined>;
216
156
  dropzoneRef: import("vue").Ref<HTMLDivElement | undefined, HTMLDivElement | undefined>;
217
- uploadFile: (index: number) => Promise<void>;
218
- cancelUpload: (index: number) => void;
219
- activeUploads: Readonly<import("vue").Ref<number, number>>;
220
- uploadQueue: Readonly<import("vue").Ref<readonly {
221
- readonly index: number;
222
- readonly fileUpload: {
223
- readonly file: {
224
- readonly lastModified: number;
225
- readonly name: string;
226
- readonly webkitRelativePath: string;
227
- readonly size: number;
228
- readonly type: string;
229
- readonly arrayBuffer: {
230
- (): Promise<ArrayBuffer>;
231
- (): Promise<ArrayBuffer>;
232
- };
233
- readonly bytes: {
234
- (): Promise<Uint8Array>;
235
- (): Promise<Uint8Array>;
236
- };
237
- readonly slice: {
238
- (start?: number, end?: number, contentType?: string): Blob;
239
- (start?: number, end?: number, contentType?: string): Blob;
240
- };
241
- readonly stream: {
242
- (): ReadableStream<Uint8Array>;
243
- (): ReadableStream<Uint8Array>;
244
- };
245
- readonly text: {
246
- (): Promise<string>;
247
- (): Promise<string>;
248
- };
249
- };
250
- readonly progress?: number | undefined;
251
- readonly uploading?: boolean | undefined;
252
- readonly error?: string | undefined;
253
- readonly abortController?: {
254
- readonly signal: {
255
- readonly aborted: boolean;
256
- readonly onabort: ((this: AbortSignal, ev: Event) => any) | null;
257
- readonly reason: any;
258
- readonly throwIfAborted: {
259
- (): void;
260
- (): void;
261
- (): void;
262
- };
263
- readonly addEventListener: {
264
- <K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
265
- (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
266
- <K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
267
- (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
268
- };
269
- readonly removeEventListener: {
270
- <K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
271
- (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
272
- <K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
273
- (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
274
- };
275
- readonly dispatchEvent: {
276
- (event: Event): boolean;
277
- (event: Event): boolean;
278
- };
279
- };
280
- readonly abort: {
281
- (reason?: any): void;
282
- (reason?: any): void;
283
- (reason?: any): void;
284
- };
285
- } | undefined;
286
- };
287
- }[], readonly {
288
- readonly index: number;
289
- readonly fileUpload: {
290
- readonly file: {
291
- readonly lastModified: number;
292
- readonly name: string;
293
- readonly webkitRelativePath: string;
294
- readonly size: number;
295
- readonly type: string;
296
- readonly arrayBuffer: {
297
- (): Promise<ArrayBuffer>;
298
- (): Promise<ArrayBuffer>;
299
- };
300
- readonly bytes: {
301
- (): Promise<Uint8Array>;
302
- (): Promise<Uint8Array>;
303
- };
304
- readonly slice: {
305
- (start?: number, end?: number, contentType?: string): Blob;
306
- (start?: number, end?: number, contentType?: string): Blob;
307
- };
308
- readonly stream: {
309
- (): ReadableStream<Uint8Array>;
310
- (): ReadableStream<Uint8Array>;
311
- };
312
- readonly text: {
313
- (): Promise<string>;
314
- (): Promise<string>;
315
- };
316
- };
317
- readonly progress?: number | undefined;
318
- readonly uploading?: boolean | undefined;
319
- readonly error?: string | undefined;
320
- readonly abortController?: {
321
- readonly signal: {
322
- readonly aborted: boolean;
323
- readonly onabort: ((this: AbortSignal, ev: Event) => any) | null;
324
- readonly reason: any;
325
- readonly throwIfAborted: {
326
- (): void;
327
- (): void;
328
- (): void;
329
- };
330
- readonly addEventListener: {
331
- <K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
332
- (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
333
- <K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
334
- (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
335
- };
336
- readonly removeEventListener: {
337
- <K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
338
- (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
339
- <K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
340
- (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
341
- };
342
- readonly dispatchEvent: {
343
- (event: Event): boolean;
344
- (event: Event): boolean;
345
- };
346
- };
347
- readonly abort: {
348
- (reason?: any): void;
349
- (reason?: any): void;
350
- (reason?: any): void;
351
- };
352
- } | undefined;
353
- };
354
- }[]>>;
355
157
  }>): void;
356
158
  attrs: any;
357
159
  slots: FileUploadSlots<M>;
358
- emit: (((evt: "change", event: Event) => void) & ((evt: "update:modelValue", payload: M extends true ? FileUploadFile[] : FileUploadFile | null) => void) & ((evt: "upload:start", file: FileUploadFile, index: number) => void) & ((evt: "upload:progress", file: FileUploadFile, progress: number, index: number) => void) & ((evt: "upload:success", file: FileUploadFile, result: any, index: number) => void) & ((evt: "upload:error", file: FileUploadFile, error: string, index: number) => void) & ((evt: "upload:cancel", file: FileUploadFile, index: number) => void) & ((evt: "upload:complete", files: FileUploadFile[]) => void)) & ((evt: "update:modelValue", value: (M extends true ? FileUploadFile[] : FileUploadFile) | null | undefined) => void);
160
+ emit: (((evt: "change", event: Event) => void) & ((evt: "update:modelValue", payload: M extends true ? File[] : File | null) => void)) & ((evt: "update:modelValue", value: (M extends true ? File[] : File) | null | undefined) => void);
359
161
  }>) => import("vue").VNode & {
360
162
  __ctx?: Awaited<typeof __VLS_setup>;
361
163
  };
@@ -2367,13 +2367,7 @@ const fileUpload = (options) => ({
2367
2367
  fileWrapper: "flex flex-col min-w-0",
2368
2368
  fileName: "text-default truncate",
2369
2369
  fileSize: "text-muted truncate",
2370
- fileProgress: "flex items-center gap-2 mt-1",
2371
- fileProgressText: "text-xs text-muted whitespace-nowrap",
2372
- fileError: "text-xs text-red-500 mt-1",
2373
- fileActions: "flex items-center gap-1",
2374
- fileTrailingButton: "",
2375
- fileUploadButton: "",
2376
- fileCancelButton: ""
2370
+ fileTrailingButton: ""
2377
2371
  },
2378
2372
  variants: {
2379
2373
  color: {
@@ -2422,15 +2416,12 @@ const fileUpload = (options) => ({
2422
2416
  root: "gap-2 items-start",
2423
2417
  files: "flex flex-col w-full gap-2",
2424
2418
  file: "min-w-0 flex items-center border border-default rounded-md w-full",
2425
- fileActions: "ms-auto"
2419
+ fileTrailingButton: "ms-auto"
2426
2420
  },
2427
2421
  grid: {
2428
2422
  fileWrapper: "hidden",
2429
2423
  fileLeadingAvatar: "size-full rounded-lg",
2430
- fileActions: "absolute -top-1.5 -end-1.5",
2431
- fileTrailingButton: "p-0 rounded-full border-2 border-bg",
2432
- fileUploadButton: "p-0 rounded-full border-2 border-bg",
2433
- fileCancelButton: "p-0 rounded-full border-2 border-bg"
2424
+ fileTrailingButton: "absolute -top-1.5 -end-1.5 p-0 rounded-full border-2 border-bg"
2434
2425
  }
2435
2426
  },
2436
2427
  position: {
@@ -2451,9 +2442,6 @@ const fileUpload = (options) => ({
2451
2442
  },
2452
2443
  disabled: {
2453
2444
  true: "cursor-not-allowed opacity-75"
2454
- },
2455
- showProgress: {
2456
- true: ""
2457
2445
  }
2458
2446
  },
2459
2447
  compoundVariants: [...(options.theme.colors || []).map((color) => ({
@@ -2474,31 +2462,31 @@ const fileUpload = (options) => ({
2474
2462
  size: "xs",
2475
2463
  layout: "list",
2476
2464
  class: {
2477
- fileActions: "-me-1"
2465
+ fileTrailingButton: "-me-1"
2478
2466
  }
2479
2467
  }, {
2480
2468
  size: "sm",
2481
2469
  layout: "list",
2482
2470
  class: {
2483
- fileActions: "-me-1.5"
2471
+ fileTrailingButton: "-me-1.5"
2484
2472
  }
2485
2473
  }, {
2486
2474
  size: "md",
2487
2475
  layout: "list",
2488
2476
  class: {
2489
- fileActions: "-me-1.5"
2477
+ fileTrailingButton: "-me-1.5"
2490
2478
  }
2491
2479
  }, {
2492
2480
  size: "lg",
2493
2481
  layout: "list",
2494
2482
  class: {
2495
- fileActions: "-me-2"
2483
+ fileTrailingButton: "-me-2"
2496
2484
  }
2497
2485
  }, {
2498
2486
  size: "xl",
2499
2487
  layout: "list",
2500
2488
  class: {
2501
- fileActions: "-me-2"
2489
+ fileTrailingButton: "-me-2"
2502
2490
  }
2503
2491
  }, {
2504
2492
  variant: "button",
@@ -2547,21 +2535,6 @@ const fileUpload = (options) => ({
2547
2535
  interactive: true,
2548
2536
  disabled: false,
2549
2537
  class: "hover:bg-elevated/25"
2550
- }, {
2551
- showProgress: true,
2552
- layout: "list",
2553
- class: {
2554
- fileWrapper: "flex-col",
2555
- fileProgress: "w-full"
2556
- }
2557
- }, {
2558
- showProgress: true,
2559
- layout: "grid",
2560
- class: {
2561
- fileWrapper: "absolute inset-0 bg-black/50 flex flex-col items-center justify-center rounded-lg",
2562
- fileProgress: "w-3/4",
2563
- fileProgressText: "text-white font-medium"
2564
- }
2565
2538
  }],
2566
2539
  defaultVariants: {
2567
2540
  color: "primary",
package/dist/unplugin.mjs CHANGED
@@ -3,7 +3,7 @@ import { join, normalize } from 'pathe';
3
3
  import { createUnplugin } from 'unplugin';
4
4
  import { defu } from 'defu';
5
5
  import tailwind from '@tailwindcss/vite';
6
- import { g as getTemplates, d as defaultOptions, r as resolveColors, a as getDefaultUiConfig } from './shared/ui.DdcmBFzX.mjs';
6
+ import { g as getTemplates, d as defaultOptions, r as resolveColors, a as getDefaultUiConfig } from './shared/ui.CgUuvRXi.mjs';
7
7
  import { globSync } from 'tinyglobby';
8
8
  import { genSafeVariableName } from 'knitwork';
9
9
  import MagicString from 'magic-string';
package/dist/vite.mjs CHANGED
@@ -4,7 +4,7 @@ import 'pathe';
4
4
  import 'unplugin';
5
5
  import 'defu';
6
6
  import '@tailwindcss/vite';
7
- import './shared/ui.DdcmBFzX.mjs';
7
+ import './shared/ui.CgUuvRXi.mjs';
8
8
  import '../dist/runtime/utils/index.js';
9
9
  import 'scule';
10
10
  import '@nuxt/kit';
package/package.json CHANGED
@@ -1,18 +1,15 @@
1
1
  {
2
2
  "name": "@eslamdevui/ui",
3
3
  "description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
4
- "version": "3.3.2-beta.2",
4
+ "version": "3.3.2-beta.3",
5
5
  "packageManager": "pnpm@10.13.1",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/MohamedEslam04/ui.git"
9
9
  },
10
- "homepage": "https://ui.nuxt.com",
10
+ "homepage": "https://ui.nuxt.com/pro",
11
11
  "type": "module",
12
12
  "license": "MIT",
13
- "publishConfig": {
14
- "access": "public"
15
- },
16
13
  "exports": {
17
14
  ".": {
18
15
  "types": "./dist/module.d.mts",
@@ -135,7 +132,7 @@
135
132
  "colortranslator": "^5.0.0",
136
133
  "consola": "^3.4.2",
137
134
  "defu": "^6.1.4",
138
- "dotenv": "^16.5.0",
135
+ "dotenv": "^16.6.1",
139
136
  "embla-carousel-auto-height": "^8.6.0",
140
137
  "embla-carousel-auto-scroll": "^8.6.0",
141
138
  "embla-carousel-autoplay": "^8.6.0",
@@ -145,6 +142,7 @@
145
142
  "embla-carousel-wheel-gestures": "^8.0.2",
146
143
  "fuse.js": "^7.1.0",
147
144
  "git-url-parse": "^16.1.0",
145
+ "motion-v": "^1.5.0",
148
146
  "hookable": "^5.5.3",
149
147
  "knitwork": "^1.2.0",
150
148
  "magic-string": "^0.30.17",
@@ -152,26 +150,27 @@
152
150
  "ofetch": "^1.4.1",
153
151
  "ohash": "^2.0.11",
154
152
  "pathe": "^2.0.3",
155
- "pkg-types": "^2.1.0",
153
+ "pkg-types": "^2.2.0",
156
154
  "reka-ui": "2.3.2",
157
155
  "scule": "^1.3.0",
158
156
  "tailwind-variants": "^1.0.0",
159
157
  "tailwindcss": "^4.1.11",
160
158
  "tinyglobby": "^0.2.14",
161
159
  "unplugin": "^2.3.5",
162
- "unplugin-auto-import": "^19.3.0",
163
- "unplugin-vue-components": "^28.8.0",
160
+ "unplugin-auto-import": "^20.0.0",
161
+ "unplugin-vue-components": "^29.0.0",
164
162
  "vaul-vue": "0.4.1",
165
- "vue-component-type-helpers": "^2.2.10"
163
+ "vue-component-type-helpers": "^3.0.5"
166
164
  },
167
165
  "devDependencies": {
166
+ "@nuxt/content": "^3.6.3",
168
167
  "@nuxt/eslint-config": "^1.6.0",
169
168
  "@nuxt/module-builder": "^1.0.1",
170
169
  "@nuxt/test-utils": "^3.19.2",
171
170
  "@release-it/conventional-changelog": "^10.0.1",
172
171
  "@vue/compiler-sfc": "3.5.17",
173
172
  "@vue/test-utils": "^2.4.6",
174
- "better-sqlite3": "^12.0.0",
173
+ "better-sqlite3": "^12.2.0",
175
174
  "embla-carousel": "^8.6.0",
176
175
  "eslint": "^9.31.0",
177
176
  "happy-dom": "^18.0.1",