@uploadista/vue 0.0.20-beta.8 → 0.0.20
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/components/index.d.mts +3 -3
- package/dist/components/index.mjs +1 -1
- package/dist/components-DoBB6sqm.mjs +2 -0
- package/dist/components-DoBB6sqm.mjs.map +1 -0
- package/dist/composables/index.d.mts +1 -1
- package/dist/{index-DiRR_Ua6.d.mts → index-BSlqFF1H.d.mts} +2 -3
- package/dist/index-BSlqFF1H.d.mts.map +1 -0
- package/dist/{index-B2fUTjNP.d.mts → index-CDJUpsAf.d.mts} +5 -5
- package/dist/{index-B2fUTjNP.d.mts.map → index-CDJUpsAf.d.mts.map} +1 -1
- package/dist/index-CLOy812P.d.mts +1285 -0
- package/dist/index-CLOy812P.d.mts.map +1 -0
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +1 -1
- package/dist/providers/index.d.mts +1 -1
- package/package.json +5 -5
- package/src/components/index.ts +3 -0
- package/src/components/upload/Upload.vue +135 -0
- package/src/components/upload/UploadCancel.vue +24 -0
- package/src/components/upload/UploadClearCompleted.vue +24 -0
- package/src/components/upload/UploadDropZone.vue +96 -0
- package/src/components/upload/UploadError.vue +44 -0
- package/src/components/upload/UploadItem.vue +52 -0
- package/src/components/upload/UploadItems.vue +35 -0
- package/src/components/upload/UploadProgress.vue +37 -0
- package/src/components/upload/UploadReset.vue +22 -0
- package/src/components/upload/UploadRetry.vue +24 -0
- package/src/components/upload/UploadStartAll.vue +30 -0
- package/src/components/upload/UploadStatus.vue +67 -0
- package/src/components/upload/index.ts +102 -0
- package/src/components/upload/useUploadContext.ts +67 -0
- package/src/index.ts +67 -4
- package/dist/components-BxBz_7tS.mjs +0 -2
- package/dist/components-BxBz_7tS.mjs.map +0 -1
- package/dist/index-D3PNaPGh.d.mts +0 -787
- package/dist/index-D3PNaPGh.d.mts.map +0 -1
- package/dist/index-DiRR_Ua6.d.mts.map +0 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, inject } from "vue";
|
|
3
|
+
import { UPLOAD_CONTEXT_KEY } from "./useUploadContext";
|
|
4
|
+
import type { UploadContextValue } from "./Upload.vue";
|
|
5
|
+
|
|
6
|
+
const uploadContext = inject<{ value: UploadContextValue }>(UPLOAD_CONTEXT_KEY);
|
|
7
|
+
if (!uploadContext) {
|
|
8
|
+
throw new Error(
|
|
9
|
+
"UploadRetry must be used within an <Upload> component.",
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const isDisabled = computed(() => uploadContext.value.state.failed === 0);
|
|
14
|
+
|
|
15
|
+
const handleClick = () => {
|
|
16
|
+
uploadContext.value.retryFailed();
|
|
17
|
+
};
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<button type="button" :disabled="isDisabled" @click="handleClick">
|
|
22
|
+
<slot />
|
|
23
|
+
</button>
|
|
24
|
+
</template>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, inject } from "vue";
|
|
3
|
+
import { UPLOAD_CONTEXT_KEY } from "./useUploadContext";
|
|
4
|
+
import type { UploadContextValue } from "./Upload.vue";
|
|
5
|
+
|
|
6
|
+
const uploadContext = inject<{ value: UploadContextValue }>(UPLOAD_CONTEXT_KEY);
|
|
7
|
+
if (!uploadContext) {
|
|
8
|
+
throw new Error(
|
|
9
|
+
"UploadStartAll must be used within an <Upload> component.",
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const idleCount = computed(() =>
|
|
14
|
+
uploadContext.value.items.filter((item) => item.state.status === "idle").length,
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const isDisabled = computed(
|
|
18
|
+
() => uploadContext.value.state.isUploading || idleCount.value === 0,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const handleClick = () => {
|
|
22
|
+
uploadContext.value.startAll();
|
|
23
|
+
};
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<template>
|
|
27
|
+
<button type="button" :disabled="isDisabled" @click="handleClick">
|
|
28
|
+
<slot />
|
|
29
|
+
</button>
|
|
30
|
+
</template>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, inject } from "vue";
|
|
3
|
+
import { UPLOAD_CONTEXT_KEY } from "./useUploadContext";
|
|
4
|
+
import type { UploadContextValue } from "./Upload.vue";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Slot props for UploadStatus component.
|
|
8
|
+
*/
|
|
9
|
+
export interface UploadStatusSlotProps {
|
|
10
|
+
/** Overall status */
|
|
11
|
+
status: "idle" | "uploading" | "success" | "error";
|
|
12
|
+
/** Whether idle (no uploads active or completed) */
|
|
13
|
+
isIdle: boolean;
|
|
14
|
+
/** Whether uploading */
|
|
15
|
+
isUploading: boolean;
|
|
16
|
+
/** Whether all uploads succeeded */
|
|
17
|
+
isSuccess: boolean;
|
|
18
|
+
/** Whether any upload failed */
|
|
19
|
+
isError: boolean;
|
|
20
|
+
/** Whether all uploads completed (success or failure) */
|
|
21
|
+
isComplete: boolean;
|
|
22
|
+
/** Number of total items */
|
|
23
|
+
total: number;
|
|
24
|
+
/** Number of successful uploads */
|
|
25
|
+
successful: number;
|
|
26
|
+
/** Number of failed uploads */
|
|
27
|
+
failed: number;
|
|
28
|
+
/** Number of currently uploading */
|
|
29
|
+
uploading: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const uploadContext = inject<{ value: UploadContextValue }>(UPLOAD_CONTEXT_KEY);
|
|
33
|
+
if (!uploadContext) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
"UploadStatus must be used within an <Upload> component.",
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const slotProps = computed<UploadStatusSlotProps>(() => {
|
|
40
|
+
const state = uploadContext.value.state;
|
|
41
|
+
|
|
42
|
+
// Derive overall status
|
|
43
|
+
let status: "idle" | "uploading" | "success" | "error" = "idle";
|
|
44
|
+
if (state.isUploading) {
|
|
45
|
+
status = "uploading";
|
|
46
|
+
} else if (state.isComplete) {
|
|
47
|
+
status = state.failed > 0 ? "error" : "success";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
status,
|
|
52
|
+
isIdle: status === "idle",
|
|
53
|
+
isUploading: state.isUploading,
|
|
54
|
+
isSuccess: state.isComplete && state.failed === 0,
|
|
55
|
+
isError: state.failed > 0,
|
|
56
|
+
isComplete: state.isComplete,
|
|
57
|
+
total: state.total,
|
|
58
|
+
successful: state.successful,
|
|
59
|
+
failed: state.failed,
|
|
60
|
+
uploading: state.uploading,
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<template>
|
|
66
|
+
<slot v-bind="slotProps" />
|
|
67
|
+
</template>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vue 3 Upload Compound Components for Uploadista
|
|
3
|
+
*
|
|
4
|
+
* These components provide a composable, headless API for building upload interfaces.
|
|
5
|
+
* They use Vue's provide/inject for context and scoped slots for complete UI control.
|
|
6
|
+
*
|
|
7
|
+
* @example Simple Drop Zone (Single File)
|
|
8
|
+
* ```vue
|
|
9
|
+
* <template>
|
|
10
|
+
* <Upload @success="handleSuccess">
|
|
11
|
+
* <UploadDropZone accept="image/*" v-slot="{ isDragging, dragHandlers, inputProps, onInputChange, openFilePicker }">
|
|
12
|
+
* <div v-bind="dragHandlers" @click="openFilePicker">
|
|
13
|
+
* <input type="file" v-bind="inputProps" @change="onInputChange" style="display: none" />
|
|
14
|
+
* {{ isDragging ? 'Drop here' : 'Drag or click' }}
|
|
15
|
+
* </div>
|
|
16
|
+
* </UploadDropZone>
|
|
17
|
+
* <UploadProgress v-slot="{ progress, isUploading }">
|
|
18
|
+
* <progress v-if="isUploading" :value="progress" max="100" />
|
|
19
|
+
* </UploadProgress>
|
|
20
|
+
* </Upload>
|
|
21
|
+
* </template>
|
|
22
|
+
*
|
|
23
|
+
* <script setup>
|
|
24
|
+
* import { Upload, UploadDropZone, UploadProgress } from '@uploadista/vue'
|
|
25
|
+
*
|
|
26
|
+
* const handleSuccess = (result) => {
|
|
27
|
+
* console.log('Upload complete:', result)
|
|
28
|
+
* }
|
|
29
|
+
* </script>
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @example Multi-File Upload
|
|
33
|
+
* ```vue
|
|
34
|
+
* <template>
|
|
35
|
+
* <Upload multiple :max-concurrent="3" @complete="handleComplete">
|
|
36
|
+
* <UploadDropZone v-slot="{ dragHandlers, inputProps, onInputChange, openFilePicker }">
|
|
37
|
+
* <div v-bind="dragHandlers" @click="openFilePicker">
|
|
38
|
+
* <input type="file" v-bind="inputProps" @change="onInputChange" style="display: none" />
|
|
39
|
+
* Drop files here
|
|
40
|
+
* </div>
|
|
41
|
+
* </UploadDropZone>
|
|
42
|
+
* <UploadItems v-slot="{ items, isEmpty }">
|
|
43
|
+
* <p v-if="isEmpty">No files</p>
|
|
44
|
+
* <div v-else v-for="item in items" :key="item.id">
|
|
45
|
+
* <UploadItem :id="item.id" v-slot="{ file, state, abort, remove }">
|
|
46
|
+
* {{ file.name }}: {{ state.progress }}%
|
|
47
|
+
* <button @click="abort">Cancel</button>
|
|
48
|
+
* <button @click="remove">Remove</button>
|
|
49
|
+
* </UploadItem>
|
|
50
|
+
* </div>
|
|
51
|
+
* </UploadItems>
|
|
52
|
+
* <UploadStartAll>Upload All</UploadStartAll>
|
|
53
|
+
* <UploadCancel>Cancel All</UploadCancel>
|
|
54
|
+
* <UploadClearCompleted>Clear Completed</UploadClearCompleted>
|
|
55
|
+
* </Upload>
|
|
56
|
+
* </template>
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
// Root component
|
|
61
|
+
export { default as Upload } from "./Upload.vue";
|
|
62
|
+
export type { UploadProps, UploadContextValue } from "./Upload.vue";
|
|
63
|
+
|
|
64
|
+
// Drop zone
|
|
65
|
+
export { default as UploadDropZone } from "./UploadDropZone.vue";
|
|
66
|
+
export type { UploadDropZoneProps, UploadDropZoneSlotProps } from "./UploadDropZone.vue";
|
|
67
|
+
|
|
68
|
+
// Items
|
|
69
|
+
export { default as UploadItems } from "./UploadItems.vue";
|
|
70
|
+
export type { UploadItemsSlotProps } from "./UploadItems.vue";
|
|
71
|
+
|
|
72
|
+
// Item
|
|
73
|
+
export { default as UploadItem } from "./UploadItem.vue";
|
|
74
|
+
export type { UploadItemProps, UploadItemSlotProps } from "./UploadItem.vue";
|
|
75
|
+
|
|
76
|
+
// Progress
|
|
77
|
+
export { default as UploadProgress } from "./UploadProgress.vue";
|
|
78
|
+
export type { UploadProgressSlotProps } from "./UploadProgress.vue";
|
|
79
|
+
|
|
80
|
+
// Status
|
|
81
|
+
export { default as UploadStatus } from "./UploadStatus.vue";
|
|
82
|
+
export type { UploadStatusSlotProps } from "./UploadStatus.vue";
|
|
83
|
+
|
|
84
|
+
// Error
|
|
85
|
+
export { default as UploadError } from "./UploadError.vue";
|
|
86
|
+
export type { UploadErrorSlotProps } from "./UploadError.vue";
|
|
87
|
+
|
|
88
|
+
// Action components
|
|
89
|
+
export { default as UploadCancel } from "./UploadCancel.vue";
|
|
90
|
+
export { default as UploadRetry } from "./UploadRetry.vue";
|
|
91
|
+
export { default as UploadReset } from "./UploadReset.vue";
|
|
92
|
+
export { default as UploadStartAll } from "./UploadStartAll.vue";
|
|
93
|
+
export { default as UploadClearCompleted } from "./UploadClearCompleted.vue";
|
|
94
|
+
|
|
95
|
+
// Context hooks
|
|
96
|
+
export {
|
|
97
|
+
UPLOAD_CONTEXT_KEY,
|
|
98
|
+
UPLOAD_ITEM_CONTEXT_KEY,
|
|
99
|
+
useUploadContext,
|
|
100
|
+
useUploadItemContext,
|
|
101
|
+
type UploadItemContextValue,
|
|
102
|
+
} from "./useUploadContext";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { inject } from "vue";
|
|
2
|
+
import type { UploadContextValue } from "./Upload.vue";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Injection key for the Upload context
|
|
6
|
+
*/
|
|
7
|
+
export const UPLOAD_CONTEXT_KEY = "uploadContext";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Injection key for the UploadItem context
|
|
11
|
+
*/
|
|
12
|
+
export const UPLOAD_ITEM_CONTEXT_KEY = "uploadItemContext";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Context value for a specific upload item within an Upload.
|
|
16
|
+
*/
|
|
17
|
+
export interface UploadItemContextValue {
|
|
18
|
+
/** Item ID */
|
|
19
|
+
id: string;
|
|
20
|
+
/** The file being uploaded */
|
|
21
|
+
file: File | Blob;
|
|
22
|
+
/** Current upload state */
|
|
23
|
+
state: {
|
|
24
|
+
status: string;
|
|
25
|
+
progress: number;
|
|
26
|
+
bytesUploaded: number;
|
|
27
|
+
totalBytes: number | null;
|
|
28
|
+
error: Error | null;
|
|
29
|
+
result: unknown;
|
|
30
|
+
};
|
|
31
|
+
/** Abort this upload */
|
|
32
|
+
abort: () => void;
|
|
33
|
+
/** Retry this upload */
|
|
34
|
+
retry: () => void;
|
|
35
|
+
/** Remove this item from the queue */
|
|
36
|
+
remove: () => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Composable to access upload context from within an Upload component.
|
|
41
|
+
* @throws Error if used outside of an Upload component
|
|
42
|
+
*/
|
|
43
|
+
export function useUploadContext(): UploadContextValue {
|
|
44
|
+
const context = inject<UploadContextValue>(UPLOAD_CONTEXT_KEY);
|
|
45
|
+
if (!context) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"useUploadContext must be used within an <Upload> component. " +
|
|
48
|
+
"Wrap your component tree with <Upload>",
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
return context;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Composable to access upload item context from within an UploadItem component.
|
|
56
|
+
* @throws Error if used outside of an UploadItem component
|
|
57
|
+
*/
|
|
58
|
+
export function useUploadItemContext(): UploadItemContextValue {
|
|
59
|
+
const context = inject<UploadItemContextValue>(UPLOAD_ITEM_CONTEXT_KEY);
|
|
60
|
+
if (!context) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
"useUploadItemContext must be used within an <UploadItem> component. " +
|
|
63
|
+
'Wrap your component with <UploadItem id="...">',
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
return context;
|
|
67
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -18,10 +18,73 @@
|
|
|
18
18
|
|
|
19
19
|
// Re-export all components
|
|
20
20
|
export * from "./components";
|
|
21
|
-
|
|
22
|
-
export
|
|
23
|
-
//
|
|
24
|
-
export
|
|
21
|
+
|
|
22
|
+
// Re-export composables with explicit types to avoid conflicts
|
|
23
|
+
// Types with potential conflicts are renamed for clarity
|
|
24
|
+
export {
|
|
25
|
+
// Event composables
|
|
26
|
+
isFlowEvent,
|
|
27
|
+
isUploadEvent,
|
|
28
|
+
useUploadistaEvents,
|
|
29
|
+
useFlowEvents,
|
|
30
|
+
useUploadEvents,
|
|
31
|
+
// Plugin
|
|
32
|
+
createUploadistaPlugin,
|
|
33
|
+
UPLOADISTA_CLIENT_KEY,
|
|
34
|
+
// Drag and drop
|
|
35
|
+
useDragDrop,
|
|
36
|
+
// Flow composables
|
|
37
|
+
useFlow,
|
|
38
|
+
// Multi-upload
|
|
39
|
+
useMultiFlowUpload,
|
|
40
|
+
useMultiUpload,
|
|
41
|
+
// Upload composables
|
|
42
|
+
useUpload,
|
|
43
|
+
// Client
|
|
44
|
+
useUploadistaClient,
|
|
45
|
+
// Metrics
|
|
46
|
+
useUploadMetrics,
|
|
47
|
+
} from "./composables";
|
|
48
|
+
|
|
49
|
+
// Re-export types from composables
|
|
50
|
+
export type {
|
|
51
|
+
// Event types
|
|
52
|
+
UseFlowEventsOptions,
|
|
53
|
+
UploadFailedEventData,
|
|
54
|
+
UploadFileEventData,
|
|
55
|
+
UploadProgressEventData,
|
|
56
|
+
UploadValidationFailedEventData,
|
|
57
|
+
UploadValidationSuccessEventData,
|
|
58
|
+
UploadValidationWarningEventData,
|
|
59
|
+
UseUploadEventsOptions,
|
|
60
|
+
// Plugin types
|
|
61
|
+
UploadistaPluginOptions,
|
|
62
|
+
// Drag and drop types
|
|
63
|
+
DragDropOptions,
|
|
64
|
+
DragDropState,
|
|
65
|
+
// Flow types
|
|
66
|
+
FlowInputMetadata,
|
|
67
|
+
FlowUploadState,
|
|
68
|
+
FlowUploadStatus,
|
|
69
|
+
InputExecutionState,
|
|
70
|
+
UseFlowReturn,
|
|
71
|
+
// Multi-upload types - rename to avoid conflict
|
|
72
|
+
MultiUploadOptions,
|
|
73
|
+
MultiUploadState,
|
|
74
|
+
UploadItem as MultiUploadItem,
|
|
75
|
+
// Upload types - rename to avoid conflict
|
|
76
|
+
ChunkMetrics,
|
|
77
|
+
PerformanceInsights,
|
|
78
|
+
UploadInput,
|
|
79
|
+
UploadSessionMetrics,
|
|
80
|
+
UploadState,
|
|
81
|
+
UploadStatus as UploadStatusType,
|
|
82
|
+
// Client types
|
|
83
|
+
UseUploadistaClientReturn,
|
|
84
|
+
// Metrics types
|
|
85
|
+
FileUploadMetrics,
|
|
86
|
+
UseUploadMetricsOptions,
|
|
87
|
+
} from "./composables";
|
|
25
88
|
|
|
26
89
|
export * from "./providers";
|
|
27
90
|
// Re-export utilities
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{m as e}from"./utils-B_r6xppE.mjs";import{a as t,n,o as r,r as i}from"./composables-BZ2c_WgI.mjs";import{Fragment as a,computed as o,createCommentVNode as s,createElementBlock as c,createElementVNode as l,createTextVNode as u,defineComponent as d,guardReactiveProps as f,inject as p,mergeProps as m,normalizeClass as h,normalizeProps as g,normalizeStyle as _,openBlock as v,provide as y,ref as b,renderList as x,renderSlot as S,toDisplayString as C,unref as w,withKeys as T,withModifiers as ee}from"vue";var E=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n};const te={class:`flow-upload-list`},ne={class:`flow-upload-list__item-header`},re={class:`flow-upload-list__item-icon`},ie={class:`flow-upload-list__item-name`},ae={class:`flow-upload-list__item-details`},D={class:`flow-upload-list__item-size`},O={key:0,class:`flow-upload-list__item-job`},k={key:0,class:`flow-upload-list__item-progress`},A={class:`flow-upload-list__progress-bar`},j={class:`flow-upload-list__progress-text`},M={key:1,class:`flow-upload-list__item-error`},N={key:2,class:`flow-upload-list__item-success`};var P=E(d({__name:`FlowUploadList`,props:{uploads:{},filter:{type:Function},sortBy:{type:Function}},setup(t){let n=t,r=o(()=>{let e=n.uploads;return n.filter&&(e=e.filter(n.filter)),n.sortBy&&(e=[...e].sort(n.sortBy)),e}),i=o(()=>({pending:r.value.filter(e=>e.status===`pending`),uploading:r.value.filter(e=>e.status===`uploading`),success:r.value.filter(e=>e.status===`success`),error:r.value.filter(e=>e.status===`error`),aborted:r.value.filter(e=>e.status===`aborted`)})),u=e=>{if(e===0)return`0 Bytes`;let t=1024,n=[`Bytes`,`KB`,`MB`,`GB`],r=Math.floor(Math.log(e)/Math.log(t));return`${parseFloat((e/t**r).toFixed(2))} ${n[r]}`},d=e=>{switch(e){case`pending`:return`⏳`;case`uploading`:return`📤`;case`success`:return`✅`;case`error`:return`❌`;case`aborted`:return`⏹️`;default:return`❓`}},f=e=>{switch(e){case`pending`:return`#6c757d`;case`uploading`:return`#007bff`;case`success`:return`#28a745`;case`error`:return`#dc3545`;case`aborted`:return`#6c757d`;default:return`#6c757d`}};return(t,n)=>(v(),c(`div`,te,[S(t.$slots,`default`,{items:r.value,itemsByStatus:i.value},()=>[s(` Default rendering: simple list of flow upload items `),(v(!0),c(a,null,x(r.value,(n,r)=>(v(),c(`div`,{key:n.id,class:h([`flow-upload-list__item`,`flow-upload-list__item--${n.status}`])},[S(t.$slots,`item`,{item:n,index:r,isPending:n.status===`pending`,isUploading:n.status===`uploading`,isSuccess:n.status===`success`,isError:n.status===`error`,isAborted:n.status===`aborted`,formatFileSize:u},()=>[s(` Default item template `),l(`div`,ne,[l(`span`,re,C(d(n.status)),1),l(`span`,ie,C(w(e)(n.file)?n.file.name:`File`),1),l(`span`,{class:`flow-upload-list__item-status`,style:_({color:f(n.status)})},C(n.status.toUpperCase()),5)]),l(`div`,ae,[l(`span`,D,C(u(n.totalBytes)),1),n.jobId?(v(),c(`span`,O,` Job: `+C(n.jobId.slice(0,8))+`... `,1)):s(`v-if`,!0)]),n.status===`uploading`?(v(),c(`div`,k,[l(`div`,A,[l(`div`,{class:`flow-upload-list__progress-fill`,style:_({width:`${n.progress}%`})},null,4)]),l(`span`,j,C(n.progress)+`% • `+C(u(n.bytesUploaded))+` / `+C(u(n.totalBytes)),1)])):s(`v-if`,!0),n.status===`error`&&n.error?(v(),c(`div`,M,C(n.error.message),1)):s(`v-if`,!0),n.status===`success`?(v(),c(`div`,N,` Upload complete `)):s(`v-if`,!0)],!0)],2))),128))],!0)]))}}),[[`__scopeId`,`data-v-eabb787d`]]);const F={class:`upload-list`},I={class:`upload-list__item-header`},L={class:`upload-list__item-icon`},R={class:`upload-list__item-name`},z={key:0,class:`upload-list__item-size`},B={key:1,class:`upload-list__item-progress`},V={class:`upload-list__progress-bar`},H={class:`upload-list__progress-text`},U={key:2,class:`upload-list__item-error`};var W=E(d({__name:`UploadList`,props:{uploads:{},filter:{type:Function},sortBy:{type:Function}},setup(t){let n=t,r=o(()=>{let e=n.uploads;return n.filter&&(e=e.filter(n.filter)),n.sortBy&&(e=[...e].sort(n.sortBy)),e}),i=o(()=>({idle:r.value.filter(e=>e.state.status===`idle`),uploading:r.value.filter(e=>e.state.status===`uploading`),success:r.value.filter(e=>e.state.status===`success`),error:r.value.filter(e=>e.state.status===`error`),aborted:r.value.filter(e=>e.state.status===`aborted`)})),u=e=>{if(e===0)return`0 Bytes`;let t=1024,n=[`Bytes`,`KB`,`MB`,`GB`],r=Math.floor(Math.log(e)/Math.log(t));return`${parseFloat((e/t**r).toFixed(2))} ${n[r]}`},d=e=>{switch(e){case`idle`:return`⏳`;case`uploading`:return`📤`;case`success`:return`✅`;case`error`:return`❌`;case`aborted`:return`⏹️`;default:return`❓`}},f=e=>{switch(e){case`idle`:return`#6c757d`;case`uploading`:return`#007bff`;case`success`:return`#28a745`;case`error`:return`#dc3545`;case`aborted`:return`#6c757d`;default:return`#6c757d`}};return(t,n)=>(v(),c(`div`,F,[S(t.$slots,`default`,{items:r.value,itemsByStatus:i.value},()=>[s(` Default rendering: simple list of upload items `),(v(!0),c(a,null,x(r.value,(n,r)=>(v(),c(`div`,{key:n.id,class:h([`upload-list__item`,`upload-list__item--${n.state.status}`])},[S(t.$slots,`item`,{item:n,index:r,isUploading:n.state.status===`uploading`,isSuccess:n.state.status===`success`,isError:n.state.status===`error`,formatFileSize:u},()=>[s(` Default item template `),l(`div`,I,[l(`span`,L,C(d(n.state.status)),1),l(`span`,R,C(w(e)(n.file)?n.file.name:`File`),1),l(`span`,{class:`upload-list__item-status`,style:_({color:f(n.state.status)})},C(n.state.status.toUpperCase()),5)]),n.state.totalBytes?(v(),c(`div`,z,C(u(n.state.totalBytes)),1)):s(`v-if`,!0),n.state.status===`uploading`?(v(),c(`div`,B,[l(`div`,V,[l(`div`,{class:`upload-list__progress-fill`,style:_({width:`${n.state.progress}%`})},null,4)]),l(`span`,H,C(n.state.progress)+`%`,1)])):s(`v-if`,!0),n.state.status===`error`&&n.state.error?(v(),c(`div`,U,C(n.state.error.message),1)):s(`v-if`,!0)],!0)],2))),128))],!0)]))}}),[[`__scopeId`,`data-v-70c8fe1a`]]);const G=[`tabindex`,`aria-disabled`,`aria-label`,`onKeydown`],K={class:`upload-zone__content`},q={key:0},J={key:1},oe={key:2,class:`upload-zone__errors`},se=[`multiple`,`accept`,`disabled`];var ce=E(d({__name:`UploadZone`,props:{accept:{},multiple:{type:Boolean,default:!0},disabled:{type:Boolean,default:!1},maxFileSize:{},validator:{},multiUploadOptions:{},uploadOptions:{}},emits:[`file-select`,`upload-start`,`validation-error`],setup(e,{emit:t}){let u=e,d=t,f=u.multiple?null:n(u.uploadOptions||{}),p=u.multiple?i(u.multiUploadOptions||{}):null,m=r({accept:u.accept,multiple:u.multiple,maxFileSize:u.maxFileSize,validator:u.validator,onFilesReceived:e=>{d(`file-select`,e),d(`upload-start`,e),u.multiple&&p?(p.addFiles(e),setTimeout(()=>p.startAll(),0)):!u.multiple&&f&&e[0]&&f.upload(e[0])},onValidationError:e=>{d(`validation-error`,e)}}),g=b(),_=()=>{u.disabled||g.value?.click()},y=o(()=>m.state.value.isDragging||m.state.value.isOver),E=o(()=>u.multiple&&p?p.state.value.isUploading:!u.multiple&&f?f.state.value.status===`uploading`:!1);return(t,n)=>(v(),c(`div`,{class:h([`upload-zone`,{"upload-zone--active":y.value,"upload-zone--disabled":e.disabled}]),onDragenter:n[1]||=t=>!e.disabled&&w(m).onDragEnter,onDragover:n[2]||=t=>!e.disabled&&w(m).onDragOver,onDragleave:n[3]||=t=>!e.disabled&&w(m).onDragLeave,onDrop:n[4]||=t=>!e.disabled&&w(m).onDrop,onClick:_,role:`button`,tabindex:e.disabled?-1:0,"aria-disabled":e.disabled,"aria-label":e.multiple?`Upload multiple files`:`Upload a file`,onKeydown:[T(_,[`enter`]),T(ee(_,[`prevent`]),[`space`])]},[S(t.$slots,`default`,{isDragging:w(m).state.value.isDragging,isOver:w(m).state.value.isOver,isUploading:E.value,errors:[...w(m).state.value.errors],openFilePicker:_},()=>[s(` Default slot content `),l(`div`,K,[w(m).state.value.isDragging?(v(),c(`p`,q,C(e.multiple?`Drop files here...`:`Drop file here...`),1)):(v(),c(`p`,J,C(e.multiple?`Drag files here or click to select`:`Drag a file here or click to select`),1)),w(m).state.value.errors.length>0?(v(),c(`div`,oe,[(v(!0),c(a,null,x(w(m).state.value.errors,(e,t)=>(v(),c(`p`,{key:t},C(e),1))),128))])):s(`v-if`,!0)])],!0),l(`input`,{ref_key:`fileInputRef`,ref:g,type:`file`,multiple:w(m).inputProps.value.multiple,accept:w(m).inputProps.value.accept,disabled:e.disabled,onChange:n[0]||=(...e)=>w(m).onInputChange&&w(m).onInputChange(...e),style:{display:`none`},"aria-hidden":`true`},null,40,se)],42,G))}}),[[`__scopeId`,`data-v-8b709bef`]]),le=d({__name:`Flow`,props:{flowId:{},storageId:{},outputNodeId:{},metadata:{}},emits:[`success`,`error`,`progress`,`flowComplete`,`abort`],setup(e,{expose:n,emit:r}){let i=e,a=r,o=t({flowConfig:{flowId:i.flowId,storageId:i.storageId,outputNodeId:i.outputNodeId,metadata:i.metadata},onSuccess:e=>a(`success`,e),onError:e=>a(`error`,e),onProgress:(e,t,n)=>a(`progress`,e,t,n),onFlowComplete:e=>a(`flowComplete`,e),onAbort:()=>a(`abort`)}),s={state:o.state,inputMetadata:o.inputMetadata,inputs:o.inputs,inputStates:o.inputStates,setInput:o.setInput,execute:o.execute,upload:o.upload,abort:o.abort,pause:o.pause,reset:o.reset,isUploading:o.isUploading,isUploadingFile:o.isUploadingFile,isProcessing:o.isProcessing,isDiscoveringInputs:o.isDiscoveringInputs};return y(`flowContext`,s),n(s),(e,t)=>S(e.$slots,`default`)}});const Y=`flowContext`,X=`flowInputContext`;function Z(){let e=p(Y);if(!e)throw Error(`useFlowContext must be used within a <Flow> component. Wrap your component tree with <Flow flowId="..." storageId="...">`);return e}function Q(){let e=p(X);if(!e)throw Error(`useFlowInputContext must be used within a <FlowInput> component. Wrap your component with <FlowInput nodeId="...">`);return e}var ue=d({inheritAttrs:!1,__name:`FlowCancel`,setup(e){let t=Z(),n=()=>{t.abort()};return(e,t)=>(v(),c(`button`,m({type:`button`,onClick:n},e.$attrs),[S(e.$slots,`default`,{},()=>[t[0]||=u(`Cancel`,-1)])],16))}});const de={key:0},fe={key:1},pe={key:2},me=[`multiple`,`accept`];var he=d({__name:`FlowDropZone`,props:{accept:{default:void 0},maxFileSize:{default:void 0}},setup(e){let t=e,n=Z(),i=b(null),a=r({onFilesReceived:e=>{let t=e[0];t&&n.upload(t)},accept:t.accept?t.accept.split(`,`).map(e=>e.trim()):void 0,maxFileSize:t.maxFileSize,multiple:!1}),u=()=>{i.value?.click()},d=o(()=>({isDragging:a.state.value.isDragging,isOver:a.state.value.isOver,progress:n.state.value.progress,status:n.state.value.status,dragDropState:a.state.value,openFilePicker:u,dragHandlers:{onDragenter:a.onDragEnter,onDragover:a.onDragOver,onDragleave:a.onDragLeave,onDrop:a.onDrop},inputProps:a.inputProps.value,onInputChange:a.onInputChange,inputRef:i}));return(e,t)=>S(e.$slots,`default`,g(f(d.value)),()=>[s(` Default content if no slot provided `),l(`div`,m(d.value.dragHandlers,{onClick:u,style:{border:d.value.isDragging?`2px dashed #3b82f6`:`2px dashed #d1d5db`,borderRadius:`0.5rem`,padding:`2rem`,textAlign:`center`,cursor:w(n).isUploading.value?`not-allowed`:`pointer`,opacity:w(n).isUploading.value?.5:1,backgroundColor:d.value.isOver?`#eff6ff`:`transparent`,transition:`all 0.2s ease`}}),[d.value.isDragging?(v(),c(`p`,de,`Drop file here...`)):w(n).isUploading.value?(v(),c(`p`,fe,`Uploading... `+C(d.value.progress)+`%`,1)):(v(),c(`p`,pe,`Drag and drop a file here, or click to select`))],16),l(`input`,{ref_key:`inputRef`,ref:i,type:`file`,multiple:d.value.inputProps.multiple,accept:d.value.inputProps.accept,onChange:t[0]||=(...e)=>d.value.onInputChange&&d.value.onInputChange(...e),style:{display:`none`}},null,40,me)])}});const ge={key:0,style:{padding:`1rem`,background:`#fef2f2`,border:`1px solid #fecaca`,"border-radius":`0.5rem`,color:`#dc2626`}},_e={style:{margin:`0.25rem 0 0`,"font-size":`0.875rem`}};var ve=d({__name:`FlowError`,setup(e){let t=Z(),n=o(()=>({error:t.state.value.error,hasError:t.state.value.status===`error`,message:t.state.value.error?.message??null,reset:t.reset}));return(e,t)=>S(e.$slots,`default`,g(f(n.value)),()=>[s(` Default error display `),n.value.hasError?(v(),c(`div`,ge,[t[1]||=l(`p`,{style:{margin:`0`,"font-weight":`600`}},`Error`,-1),l(`p`,_e,C(n.value.message),1),l(`button`,{type:`button`,onClick:t[0]||=(...e)=>n.value.reset&&n.value.reset(...e),style:{"margin-top":`0.75rem`,padding:`0.5rem 1rem`,background:`#dc2626`,color:`white`,border:`none`,"border-radius":`0.375rem`,cursor:`pointer`}},` Try Again `)])):s(`v-if`,!0)])}}),ye=d({__name:`FlowInput`,props:{nodeId:{}},setup(e){let t=e,n=Z(),r=o(()=>n.inputMetadata.value?.find(e=>e.nodeId===t.nodeId)),i=o(()=>n.inputs.value[t.nodeId]),l=o(()=>n.inputStates.value.get(t.nodeId)),u=e=>{n.setInput(t.nodeId,e)};y(X,{get nodeId(){return t.nodeId},get metadata(){return r.value??{nodeId:t.nodeId,nodeName:``,nodeDescription:``,required:!1}},get value(){return i.value},setValue:u,get state(){return l.value}});let d=o(()=>({nodeId:t.nodeId,metadata:r.value,value:i.value,setValue:u,state:l.value}));return(e,t)=>(v(),c(a,null,[s(` Only render if metadata is found `),r.value?S(e.$slots,`default`,g(m({key:0},d.value))):s(`v-if`,!0)],2112))}});const be={key:0},xe={key:1},Se={key:2},Ce=[`multiple`,`accept`];var we=d({__name:`FlowInputDropZone`,props:{accept:{default:void 0},maxFileSize:{default:void 0}},setup(e){let t=e=>e instanceof File,n=e,i=Q(),a=b(null),u=r({onFilesReceived:e=>{let t=e[0];t&&i.setValue(t)},accept:n.accept?n.accept.split(`,`).map(e=>e.trim()):void 0,maxFileSize:n.maxFileSize,multiple:!1}),d=()=>{a.value?.click()},p=o(()=>({isDragging:u.state.value.isDragging,isOver:u.state.value.isOver,value:i.value,progress:i.state?.progress??0,status:i.state?.status??`idle`,dragDropState:u.state.value,openFilePicker:d,dragHandlers:{onDragenter:u.onDragEnter,onDragover:u.onDragOver,onDragleave:u.onDragLeave,onDrop:u.onDrop},inputProps:u.inputProps.value,onInputChange:u.onInputChange,inputRef:a}));return(e,n)=>S(e.$slots,`default`,g(f(p.value)),()=>[s(` Default content if no slot provided `),l(`div`,m(p.value.dragHandlers,{onClick:d,style:{border:p.value.isDragging?`2px dashed #3b82f6`:`2px dashed #d1d5db`,borderRadius:`0.5rem`,padding:`2rem`,textAlign:`center`,cursor:`pointer`,backgroundColor:p.value.isOver?`#eff6ff`:`transparent`,transition:`all 0.2s ease`}}),[p.value.isDragging?(v(),c(`p`,be,`Drop file here...`)):t(p.value.value)?(v(),c(`p`,xe,` Selected: `+C(p.value.value.name),1)):(v(),c(`p`,Se,`Drag and drop a file here, or click to select`))],16),l(`input`,{ref_key:`inputRef`,ref:a,type:`file`,multiple:p.value.inputProps.multiple,accept:p.value.inputProps.accept,onChange:n[0]||=(...e)=>p.value.onInputChange&&p.value.onInputChange(...e),style:{display:`none`}},null,40,Ce)])}});const Te={key:0,style:{display:`flex`,"align-items":`center`,gap:`0.5rem`,padding:`0.5rem`,background:`#f3f4f6`,"border-radius":`0.375rem`}},Ee={style:{flex:`1`,"min-width":`0`}},De={key:0,style:{margin:`0`,"font-size":`0.875rem`,overflow:`hidden`,"text-overflow":`ellipsis`,"white-space":`nowrap`}},Oe={key:0,style:{color:`#6b7280`,"margin-left":`0.25rem`}},ke={key:1,style:{margin:`0`,"font-size":`0.875rem`,overflow:`hidden`,"text-overflow":`ellipsis`,"white-space":`nowrap`}};var Ae=d({__name:`FlowInputPreview`,setup(e){let t=Q(),n=o(()=>t.value instanceof File),r=o(()=>typeof t.value==`string`&&t.value.length>0),i=()=>{t.setValue(void 0)},a=o(()=>({value:t.value,isFile:n.value,isUrl:r.value,fileName:n.value?t.value.name:null,fileSize:n.value?t.value.size:null,clear:i}));return(e,t)=>S(e.$slots,`default`,g(f(a.value)),()=>[s(` Default preview content - only render for File or URL values `),a.value.isFile||a.value.isUrl?(v(),c(`div`,Te,[l(`div`,Ee,[a.value.isFile?(v(),c(`p`,De,[u(C(a.value.fileName)+` `,1),a.value.fileSize?(v(),c(`span`,Oe,` (`+C((a.value.fileSize/1024).toFixed(1))+` KB) `,1)):s(`v-if`,!0)])):a.value.isUrl?(v(),c(`p`,ke,C(a.value.value),1)):s(`v-if`,!0)]),l(`button`,{type:`button`,onClick:i,style:{padding:`0.25rem 0.5rem`,background:`transparent`,border:`none`,cursor:`pointer`,color:`#6b7280`},"aria-label":`Clear`},` × `)])):s(`v-if`,!0)])}});const je={key:0,style:{padding:`1rem`,"text-align":`center`}},Me={key:1,style:{padding:`1rem`,"text-align":`center`,color:`#6b7280`}};var Ne=d({__name:`FlowInputs`,setup(e){let t=Z(),n=o(()=>({inputs:t.inputMetadata.value??[],isLoading:t.isDiscoveringInputs.value}));return(e,t)=>S(e.$slots,`default`,g(f(n.value)),()=>[s(` Default loading state if no slot provided `),n.value.isLoading?(v(),c(`div`,je,` Discovering flow inputs... `)):n.value.inputs.length===0?(v(),c(`div`,Me,` No inputs found for this flow. `)):s(`v-if`,!0)])}});const Pe=[`value`,`placeholder`];var $=d({inheritAttrs:!1,__name:`FlowInputUrlField`,props:{placeholder:{default:`https://example.com/file`}},setup(e){let t=Q(),n=o(()=>typeof t.value==`string`),r=o(()=>n.value?t.value:``),i=e=>{let n=e.target;t.setValue(n.value)};return(t,n)=>(v(),c(`input`,m({type:`url`,value:r.value,onInput:i,placeholder:e.placeholder},t.$attrs),null,16,Pe))}});const Fe={key:0,style:{width:`100%`}},Ie={style:{display:`flex`,"justify-content":`space-between`,"margin-bottom":`0.25rem`,"font-size":`0.875rem`}},Le={style:{width:`100%`,height:`0.5rem`,background:`#e5e7eb`,"border-radius":`0.25rem`,overflow:`hidden`}},Re={key:0,style:{"margin-top":`0.25rem`,"font-size":`0.75rem`,color:`#6b7280`}};var ze=d({__name:`FlowProgress`,setup(e){let t=Z(),n=o(()=>({progress:t.state.value.progress,bytesUploaded:t.state.value.bytesUploaded,totalBytes:t.state.value.totalBytes,status:t.state.value.status}));return(e,t)=>S(e.$slots,`default`,g(f(n.value)),()=>[s(` Default progress display `),n.value.status===`uploading`||n.value.status===`processing`?(v(),c(`div`,Fe,[l(`div`,Ie,[l(`span`,null,C(n.value.status===`uploading`?`Uploading`:`Processing`),1),l(`span`,null,C(n.value.progress.toFixed(1))+`%`,1)]),l(`div`,Le,[l(`div`,{style:_({width:`${n.value.progress}%`,height:`100%`,background:`#3b82f6`,transition:`width 0.2s ease`})},null,4)]),n.value.totalBytes?(v(),c(`div`,Re,C((n.value.bytesUploaded/1024).toFixed(0))+` KB / `+C((n.value.totalBytes/1024).toFixed(0))+` KB `,1)):s(`v-if`,!0)])):s(`v-if`,!0)])}}),Be=d({inheritAttrs:!1,__name:`FlowReset`,setup(e){let t=Z(),n=()=>{t.reset()};return(e,t)=>(v(),c(`button`,m({type:`button`,onClick:n},e.$attrs),[S(e.$slots,`default`,{},()=>[t[0]||=u(`Reset`,-1)])],16))}});const Ve={key:0,style:{padding:`0.5rem`}},He={style:{margin:`0`,"font-size":`0.875rem`,color:`#6b7280`}},Ue={key:0,style:{margin:`0.25rem 0 0`,"font-size":`0.75rem`,color:`#9ca3af`}},We={key:1,style:{margin:`0.25rem 0 0`,"font-size":`0.75rem`,color:`#9ca3af`}};var Ge=d({__name:`FlowStatus`,setup(e){let t=Z(),n=o(()=>({status:t.state.value.status,currentNodeName:t.state.value.currentNodeName,currentNodeType:t.state.value.currentNodeType,error:t.state.value.error,jobId:t.state.value.jobId,flowStarted:t.state.value.flowStarted,flowOutputs:t.state.value.flowOutputs}));return(e,t)=>S(e.$slots,`default`,g(f(n.value)),()=>[s(` Default status display - only show when not idle `),n.value.status===`idle`?s(`v-if`,!0):(v(),c(`div`,Ve,[l(`p`,He,[t[0]||=u(` Status: `,-1),l(`strong`,null,C(n.value.status),1)]),n.value.currentNodeName?(v(),c(`p`,Ue,` Processing: `+C(n.value.currentNodeName),1)):s(`v-if`,!0),n.value.jobId?(v(),c(`p`,We,` Job ID: `+C(n.value.jobId),1)):s(`v-if`,!0)]))])}});const Ke=[`disabled`];var qe=d({inheritAttrs:!1,__name:`FlowSubmit`,setup(e){let t=Z(),n=()=>{t.execute()};return(e,r)=>(v(),c(`button`,m({type:`button`,onClick:n,disabled:w(t).isUploading.value},e.$attrs),[S(e.$slots,`default`,{},()=>[r[0]||=u(`Execute`,-1)])],16,Ke))}});export{le as _,$ as a,P as b,we as c,he as d,ue as f,Q as g,Z as h,ze as i,ye as l,X as m,Ge as n,Ne as o,Y as p,Be as r,Ae as s,qe as t,ve as u,ce as v,W as y};
|
|
2
|
-
//# sourceMappingURL=components-BxBz_7tS.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"components-BxBz_7tS.mjs","names":["$attrs","$attrs","$attrs","$attrs"],"sources":["../src/components/FlowUploadList.vue","../src/components/UploadList.vue","../src/components/UploadZone.vue","../src/components/flow/Flow.vue","../src/components/flow/useFlowContext.ts","../src/components/flow/FlowCancel.vue","../src/components/flow/FlowDropZone.vue","../src/components/flow/FlowError.vue","../src/components/flow/FlowInput.vue","../src/components/flow/FlowInputDropZone.vue","../src/components/flow/FlowInputPreview.vue","../src/components/flow/FlowInputs.vue","../src/components/flow/FlowInputUrlField.vue","../src/components/flow/FlowProgress.vue","../src/components/flow/FlowReset.vue","../src/components/flow/FlowStatus.vue","../src/components/flow/FlowSubmit.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * FlowUploadList - Display list of flow uploads with processing status\n *\n * Shows the progress and processing status of files being uploaded through flow pipelines.\n * Supports filtering, sorting, and status-based grouping. Provides flexible slot-based\n * customization for rendering each flow upload item.\n *\n * @component\n * @example\n * // Basic flow upload list\n * <FlowUploadList :uploads=\"flowUploads\" />\n *\n * @example\n * // Custom item rendering with flow status\n * <FlowUploadList :uploads=\"flowUploads\">\n * <template #item=\"{ item, isSuccess, isError, isUploading }\">\n * <div class=\"flow-item\">\n * <span>{{ item.filename }}</span>\n * <progress :value=\"item.uploadProgress\" max=\"100\"></progress>\n * <span v-if=\"isUploading\">Processing...</span>\n * <span v-else-if=\"isSuccess\">Complete</span>\n * <span v-else-if=\"isError\">Failed</span>\n * </div>\n * </template>\n * </FlowUploadList>\n *\n * @example\n * // With status grouping\n * <FlowUploadList :uploads=\"flowUploads\">\n * <template #default=\"{ itemsByStatus }\">\n * <div v-if=\"itemsByStatus.uploading.length\">\n * <h3>Uploading...</h3>\n * <div v-for=\"item of itemsByStatus.uploading\" :key=\"item.id\">\n * {{ item.filename }}\n * </div>\n * </div>\n * </template>\n * </FlowUploadList>\n */\nimport type {\n BrowserUploadInput,\n FlowUploadItem,\n} from \"@uploadista/client-browser\";\nimport { computed } from \"vue\";\nimport { isBrowserFile } from \"../utils\";\n\n/**\n * Props for the FlowUploadList component\n * @property {FlowUploadItem[]} uploads - Array of flow upload items to display\n * @property {Function} filter - Optional filter for which items to display\n * @property {Function} sortBy - Optional sorting function for items (a, b) => number\n */\nexport interface FlowUploadListProps {\n /**\n * Array of flow upload items to display\n */\n uploads: FlowUploadItem<BrowserUploadInput>[];\n\n /**\n * Optional filter for which items to display\n */\n filter?: (item: FlowUploadItem<BrowserUploadInput>) => boolean;\n\n /**\n * Optional sorting function for items\n */\n sortBy?: (\n a: FlowUploadItem<BrowserUploadInput>,\n b: FlowUploadItem<BrowserUploadInput>,\n ) => number;\n}\n\nconst props = defineProps<FlowUploadListProps>();\n\ndefineSlots<{\n item(props: {\n item: FlowUploadItem<BrowserUploadInput>;\n index: number;\n isPending: boolean;\n isUploading: boolean;\n isSuccess: boolean;\n isError: boolean;\n isAborted: boolean;\n formatFileSize: (bytes: number) => string;\n }): any;\n default?(props: {\n items: FlowUploadItem<BrowserUploadInput>[];\n itemsByStatus: {\n pending: FlowUploadItem<BrowserUploadInput>[];\n uploading: FlowUploadItem<BrowserUploadInput>[];\n success: FlowUploadItem<BrowserUploadInput>[];\n error: FlowUploadItem<BrowserUploadInput>[];\n aborted: FlowUploadItem<BrowserUploadInput>[];\n };\n }): any;\n}>();\n\n// Apply filtering and sorting\nconst filteredItems = computed(() => {\n let items = props.uploads;\n\n if (props.filter) {\n items = items.filter(props.filter);\n }\n\n if (props.sortBy) {\n items = [...items].sort(props.sortBy);\n }\n\n return items;\n});\n\n// Group items by status\n// biome-ignore lint/correctness/noUnusedVariables: Used in slot templates\nconst itemsByStatus = computed(() => ({\n pending: filteredItems.value.filter((item) => item.status === \"pending\"),\n uploading: filteredItems.value.filter((item) => item.status === \"uploading\"),\n success: filteredItems.value.filter((item) => item.status === \"success\"),\n error: filteredItems.value.filter((item) => item.status === \"error\"),\n aborted: filteredItems.value.filter((item) => item.status === \"aborted\"),\n}));\n\n// Helper function to format file sizes\n// biome-ignore lint/correctness/noUnusedVariables: Used in slot templates\nconst formatFileSize = (bytes: number): string => {\n if (bytes === 0) return \"0 Bytes\";\n const k = 1024;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;\n};\n\n// Helper function to get status icon\n// biome-ignore lint/correctness/noUnusedVariables: Used in slot templates\nconst getStatusIcon = (status: string): string => {\n switch (status) {\n case \"pending\":\n return \"⏳\";\n case \"uploading\":\n return \"📤\";\n case \"success\":\n return \"✅\";\n case \"error\":\n return \"❌\";\n case \"aborted\":\n return \"⏹️\";\n default:\n return \"❓\";\n }\n};\n\n// Helper function to get status color\n// biome-ignore lint/correctness/noUnusedVariables: Used in slot templates\nconst getStatusColor = (status: string): string => {\n switch (status) {\n case \"pending\":\n return \"#6c757d\";\n case \"uploading\":\n return \"#007bff\";\n case \"success\":\n return \"#28a745\";\n case \"error\":\n return \"#dc3545\";\n case \"aborted\":\n return \"#6c757d\";\n default:\n return \"#6c757d\";\n }\n};\n</script>\n\n<template>\n <div class=\"flow-upload-list\">\n <slot :items=\"filteredItems\" :items-by-status=\"itemsByStatus\">\n <!-- Default rendering: simple list of flow upload items -->\n <div\n v-for=\"(item, index) in filteredItems\"\n :key=\"item.id\"\n class=\"flow-upload-list__item\"\n :class=\"`flow-upload-list__item--${item.status}`\"\n >\n <slot\n name=\"item\"\n :item=\"item\"\n :index=\"index\"\n :is-pending=\"item.status === 'pending'\"\n :is-uploading=\"item.status === 'uploading'\"\n :is-success=\"item.status === 'success'\"\n :is-error=\"item.status === 'error'\"\n :is-aborted=\"item.status === 'aborted'\"\n :format-file-size=\"formatFileSize\"\n >\n <!-- Default item template -->\n <div class=\"flow-upload-list__item-header\">\n <span class=\"flow-upload-list__item-icon\">\n {{ getStatusIcon(item.status) }}\n </span>\n <span class=\"flow-upload-list__item-name\">\n {{ isBrowserFile(item.file) ? item.file.name : 'File' }}\n </span>\n <span\n class=\"flow-upload-list__item-status\"\n :style=\"{ color: getStatusColor(item.status) }\"\n >\n {{ item.status.toUpperCase() }}\n </span>\n </div>\n\n <div class=\"flow-upload-list__item-details\">\n <span class=\"flow-upload-list__item-size\">\n {{ formatFileSize(item.totalBytes) }}\n </span>\n <span v-if=\"item.jobId\" class=\"flow-upload-list__item-job\">\n Job: {{ item.jobId.slice(0, 8) }}...\n </span>\n </div>\n\n <div v-if=\"item.status === 'uploading'\" class=\"flow-upload-list__item-progress\">\n <div class=\"flow-upload-list__progress-bar\">\n <div\n class=\"flow-upload-list__progress-fill\"\n :style=\"{ width: `${item.progress}%` }\"\n />\n </div>\n <span class=\"flow-upload-list__progress-text\">\n {{ item.progress }}% • {{ formatFileSize(item.bytesUploaded) }} / {{ formatFileSize(item.totalBytes) }}\n </span>\n </div>\n\n <div v-if=\"item.status === 'error' && item.error\" class=\"flow-upload-list__item-error\">\n {{ item.error.message }}\n </div>\n\n <div v-if=\"item.status === 'success'\" class=\"flow-upload-list__item-success\">\n Upload complete\n </div>\n </slot>\n </div>\n </slot>\n </div>\n</template>\n\n<style scoped>\n.flow-upload-list {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.flow-upload-list__item {\n padding: 0.75rem;\n border: 1px solid #e0e0e0;\n border-radius: 0.375rem;\n background-color: #fff;\n}\n\n.flow-upload-list__item-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-bottom: 0.5rem;\n}\n\n.flow-upload-list__item-icon {\n font-size: 1rem;\n}\n\n.flow-upload-list__item-name {\n flex: 1;\n font-weight: 500;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.flow-upload-list__item-status {\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n}\n\n.flow-upload-list__item-details {\n display: flex;\n gap: 1rem;\n font-size: 0.75rem;\n color: #666;\n margin-bottom: 0.5rem;\n}\n\n.flow-upload-list__item-size {\n font-weight: 500;\n}\n\n.flow-upload-list__item-job {\n color: #999;\n font-family: monospace;\n}\n\n.flow-upload-list__item-progress {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n}\n\n.flow-upload-list__progress-bar {\n width: 100%;\n height: 0.375rem;\n background-color: #e0e0e0;\n border-radius: 0.1875rem;\n overflow: hidden;\n}\n\n.flow-upload-list__progress-fill {\n height: 100%;\n background-color: #007bff;\n transition: width 0.2s ease;\n}\n\n.flow-upload-list__progress-text {\n font-size: 0.75rem;\n color: #666;\n}\n\n.flow-upload-list__item-error {\n margin-top: 0.5rem;\n padding: 0.5rem;\n background-color: #f8d7da;\n color: #721c24;\n font-size: 0.75rem;\n border-radius: 0.25rem;\n}\n\n.flow-upload-list__item-success {\n margin-top: 0.5rem;\n padding: 0.5rem;\n background-color: #d4edda;\n color: #155724;\n font-size: 0.75rem;\n border-radius: 0.25rem;\n}\n</style>\n","<script setup lang=\"ts\">\n/**\n * UploadList - Display a list of uploads with customizable status grouping and sorting\n *\n * Shows the progress and status of multiple file uploads. Supports filtering, sorting,\n * and status-based grouping. Provides flexible slot-based customization for rendering each item.\n *\n * @component\n * @example\n * // Basic upload list\n * <UploadList :uploads=\"uploads\" />\n *\n * @example\n * // Custom item rendering with status indicators\n * <UploadList :uploads=\"uploads\">\n * <template #item=\"{ item, isSuccess, isError }\">\n * <div class=\"upload-item\">\n * <span>{{ item.filename }}</span>\n * <progress :value=\"item.progress\" max=\"100\"></progress>\n * <span :class=\"{ success: isSuccess, error: isError }\">\n * {{ item.state.status }}\n * </span>\n * </div>\n * </template>\n * </UploadList>\n *\n * @example\n * // With filtering and sorting\n * <UploadList\n * :uploads=\"uploads\"\n * :filter=\"item => item.state.status === 'success'\"\n * :sort-by=\"(a, b) => a.uploadedAt - b.uploadedAt\"\n * />\n */\nimport { computed } from \"vue\";\nimport type { UploadItem } from \"../composables\";\nimport { isBrowserFile } from \"../utils\";\n\n/**\n * Props for the UploadList component\n * @property {UploadItem[]} uploads - Array of upload items to display\n * @property {Function} filter - Optional filter for which items to display\n * @property {Function} sortBy - Optional sorting function for items (a, b) => number\n */\nexport interface UploadListProps {\n /**\n * Array of upload items to display\n */\n uploads: UploadItem[];\n\n /**\n * Optional filter for which items to display\n */\n filter?: (item: UploadItem) => boolean;\n\n /**\n * Optional sorting function for items\n */\n sortBy?: (a: UploadItem, b: UploadItem) => number;\n}\n\nconst props = defineProps<UploadListProps>();\n\ndefineSlots<{\n item(props: {\n item: UploadItem;\n index: number;\n isUploading: boolean;\n isSuccess: boolean;\n isError: boolean;\n formatFileSize: (bytes: number) => string;\n }): any;\n default?(props: {\n items: UploadItem[];\n itemsByStatus: {\n idle: UploadItem[];\n uploading: UploadItem[];\n success: UploadItem[];\n error: UploadItem[];\n aborted: UploadItem[];\n };\n }): any;\n}>();\n\n// Apply filtering and sorting\nconst filteredItems = computed(() => {\n let items = props.uploads;\n\n if (props.filter) {\n items = items.filter(props.filter);\n }\n\n if (props.sortBy) {\n items = [...items].sort(props.sortBy);\n }\n\n return items;\n});\n\n// Group items by status\n// biome-ignore lint/correctness/noUnusedVariables: Used in slot templates\nconst itemsByStatus = computed(() => ({\n idle: filteredItems.value.filter((item) => item.state.status === \"idle\"),\n uploading: filteredItems.value.filter(\n (item) => item.state.status === \"uploading\",\n ),\n success: filteredItems.value.filter(\n (item) => item.state.status === \"success\",\n ),\n error: filteredItems.value.filter((item) => item.state.status === \"error\"),\n aborted: filteredItems.value.filter(\n (item) => item.state.status === \"aborted\",\n ),\n}));\n\n// Helper function to format file sizes\n// biome-ignore lint/correctness/noUnusedVariables: Used in slot templates\nconst formatFileSize = (bytes: number): string => {\n if (bytes === 0) return \"0 Bytes\";\n const k = 1024;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;\n};\n\n// Helper function to get status icon\n// biome-ignore lint/correctness/noUnusedVariables: Used in slot templates\nconst getStatusIcon = (status: string): string => {\n switch (status) {\n case \"idle\":\n return \"⏳\";\n case \"uploading\":\n return \"📤\";\n case \"success\":\n return \"✅\";\n case \"error\":\n return \"❌\";\n case \"aborted\":\n return \"⏹️\";\n default:\n return \"❓\";\n }\n};\n\n// Helper function to get status color\n// biome-ignore lint/correctness/noUnusedVariables: Used in slot templates\nconst getStatusColor = (status: string): string => {\n switch (status) {\n case \"idle\":\n return \"#6c757d\";\n case \"uploading\":\n return \"#007bff\";\n case \"success\":\n return \"#28a745\";\n case \"error\":\n return \"#dc3545\";\n case \"aborted\":\n return \"#6c757d\";\n default:\n return \"#6c757d\";\n }\n};\n</script>\n\n<template>\n <div class=\"upload-list\">\n <slot :items=\"filteredItems\" :items-by-status=\"itemsByStatus\">\n <!-- Default rendering: simple list of upload items -->\n <div\n v-for=\"(item, index) in filteredItems\"\n :key=\"item.id\"\n class=\"upload-list__item\"\n :class=\"`upload-list__item--${item.state.status}`\"\n >\n <slot\n name=\"item\"\n :item=\"item\"\n :index=\"index\"\n :is-uploading=\"item.state.status === 'uploading'\"\n :is-success=\"item.state.status === 'success'\"\n :is-error=\"item.state.status === 'error'\"\n :format-file-size=\"formatFileSize\"\n >\n <!-- Default item template -->\n <div class=\"upload-list__item-header\">\n <span class=\"upload-list__item-icon\">\n {{ getStatusIcon(item.state.status) }}\n </span>\n <span class=\"upload-list__item-name\">\n {{ isBrowserFile(item.file) ? item.file.name : 'File' }}\n </span>\n <span\n class=\"upload-list__item-status\"\n :style=\"{ color: getStatusColor(item.state.status) }\"\n >\n {{ item.state.status.toUpperCase() }}\n </span>\n </div>\n\n <div v-if=\"item.state.totalBytes\" class=\"upload-list__item-size\">\n {{ formatFileSize(item.state.totalBytes) }}\n </div>\n\n <div v-if=\"item.state.status === 'uploading'\" class=\"upload-list__item-progress\">\n <div class=\"upload-list__progress-bar\">\n <div\n class=\"upload-list__progress-fill\"\n :style=\"{ width: `${item.state.progress}%` }\"\n />\n </div>\n <span class=\"upload-list__progress-text\">{{ item.state.progress }}%</span>\n </div>\n\n <div v-if=\"item.state.status === 'error' && item.state.error\" class=\"upload-list__item-error\">\n {{ item.state.error.message }}\n </div>\n </slot>\n </div>\n </slot>\n </div>\n</template>\n\n<style scoped>\n.upload-list {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.upload-list__item {\n padding: 0.75rem;\n border: 1px solid #e0e0e0;\n border-radius: 0.375rem;\n background-color: #fff;\n}\n\n.upload-list__item-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-bottom: 0.5rem;\n}\n\n.upload-list__item-icon {\n font-size: 1rem;\n}\n\n.upload-list__item-name {\n flex: 1;\n font-weight: 500;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.upload-list__item-status {\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n}\n\n.upload-list__item-size {\n font-size: 0.75rem;\n color: #666;\n margin-bottom: 0.5rem;\n}\n\n.upload-list__item-progress {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n\n.upload-list__progress-bar {\n flex: 1;\n height: 0.375rem;\n background-color: #e0e0e0;\n border-radius: 0.1875rem;\n overflow: hidden;\n}\n\n.upload-list__progress-fill {\n height: 100%;\n background-color: #007bff;\n transition: width 0.2s ease;\n}\n\n.upload-list__progress-text {\n font-size: 0.75rem;\n color: #666;\n min-width: 3rem;\n text-align: right;\n}\n\n.upload-list__item-error {\n margin-top: 0.5rem;\n padding: 0.5rem;\n background-color: #f8d7da;\n color: #721c24;\n font-size: 0.75rem;\n border-radius: 0.25rem;\n}\n</style>\n","<script setup lang=\"ts\">\n/**\n * UploadZone - A flexible file upload component with drag-and-drop support\n *\n * Provides a drag-and-drop zone and file picker for uploading files. Supports both single\n * and multiple file uploads with validation. Emits events for file selection and upload events.\n *\n * @component\n * @example\n * // Basic single file upload\n * <UploadZone @file-select=\"handleFiles\" />\n *\n * @example\n * // Multiple files with validation\n * <UploadZone\n * multiple\n * accept={[\"image/*\"]}\n * :max-file-size=\"10 * 1024 * 1024\"\n * @file-select=\"handleFiles\"\n * @validation-error=\"handleErrors\"\n * >\n * <template #default=\"{ isDragging, errors, openFilePicker }\">\n * <div :class=\"{ dragging: isDragging }\" @click=\"openFilePicker\">\n * <p>{{ isDragging ? 'Drop files here' : 'Click or drag files here' }}</p>\n * <div v-if=\"errors.length\">\n * <p v-for=\"error in errors\" :key=\"error\">{{ error }}</p>\n * </div>\n * </div>\n * </template>\n * </UploadZone>\n *\n * @emits file-select - When files are selected/dropped\n * @emits upload-start - When upload begins\n * @emits validation-error - When validation fails\n */\nimport type { UploadOptions } from \"@uploadista/client-browser\";\nimport { computed, ref } from \"vue\";\nimport type { MultiUploadOptions } from \"../composables\";\nimport { useDragDrop, useMultiUpload, useUpload } from \"../composables\";\n\n/**\n * Props for the UploadZone component\n * @property {string[]} accept - Accepted file types (MIME types or file extensions)\n * @property {boolean} multiple - Whether to allow multiple files (default: true)\n * @property {boolean} disabled - Whether the upload zone is disabled (default: false)\n * @property {number} maxFileSize - Maximum file size in bytes\n * @property {Function} validator - Custom validation function for files\n * @property {MultiUploadOptions} multiUploadOptions - Multi-upload options (only used when multiple=true)\n * @property {UploadOptions} uploadOptions - Single upload options (only used when multiple=false)\n */\nexport interface UploadZoneProps {\n /**\n * Accepted file types (MIME types or file extensions)\n */\n accept?: string[];\n\n /**\n * Whether to allow multiple files\n */\n multiple?: boolean;\n\n /**\n * Whether the upload zone is disabled\n */\n disabled?: boolean;\n\n /**\n * Maximum file size in bytes\n */\n maxFileSize?: number;\n\n /**\n * Custom validation function for files\n */\n validator?: (files: File[]) => string[] | null;\n\n /**\n * Multi-upload options (only used when multiple=true)\n */\n multiUploadOptions?: MultiUploadOptions;\n\n /**\n * Single upload options (only used when multiple=false)\n */\n uploadOptions?: UploadOptions;\n}\n\nconst props = withDefaults(defineProps<UploadZoneProps>(), {\n multiple: true,\n disabled: false,\n});\n\nconst emit = defineEmits<{\n \"file-select\": [files: File[]];\n \"upload-start\": [files: File[]];\n \"validation-error\": [errors: string[]];\n}>();\n\ndefineSlots<{\n // biome-ignore lint/suspicious/noExplicitAny: Vue slot definition requires any\n default(props: {\n isDragging: boolean;\n isOver: boolean;\n isUploading: boolean;\n errors: string[];\n openFilePicker: () => void;\n }): any;\n}>();\n\n// Initialize upload composables\nconst singleUpload = props.multiple\n ? null\n : useUpload(props.uploadOptions || {});\nconst multiUpload = props.multiple\n ? useMultiUpload(props.multiUploadOptions || {})\n : null;\n\n// Handle files received from drag-drop or file picker\nconst handleFilesReceived = (files: File[]) => {\n emit(\"file-select\", files);\n emit(\"upload-start\", files);\n\n if (props.multiple && multiUpload) {\n multiUpload.addFiles(files);\n setTimeout(() => multiUpload.startAll(), 0);\n } else if (!props.multiple && singleUpload && files[0]) {\n singleUpload.upload(files[0]);\n }\n};\n\n// Handle validation errors\nconst handleValidationError = (errors: string[]) => {\n emit(\"validation-error\", errors);\n};\n\n// Initialize drag-drop\nconst dragDrop = useDragDrop({\n accept: props.accept,\n multiple: props.multiple,\n maxFileSize: props.maxFileSize,\n validator: props.validator,\n onFilesReceived: handleFilesReceived,\n onValidationError: handleValidationError,\n});\n\n// File input ref\nconst fileInputRef = ref<HTMLInputElement>();\n\n// Open file picker\n// biome-ignore lint/correctness/noUnusedVariables: Used in slot templates\nconst openFilePicker = () => {\n if (!props.disabled) {\n fileInputRef.value?.click();\n }\n};\n\n// Computed states\n// biome-ignore lint/correctness/noUnusedVariables: Used in slot templates\nconst isActive = computed(\n () => dragDrop.state.value.isDragging || dragDrop.state.value.isOver,\n);\n\n// biome-ignore lint/correctness/noUnusedVariables: Used in slot templates\nconst isUploading = computed(() => {\n if (props.multiple && multiUpload) {\n return multiUpload.state.value.isUploading;\n } else if (!props.multiple && singleUpload) {\n return singleUpload.state.value.status === \"uploading\";\n }\n return false;\n});\n</script>\n\n<template>\n <div\n class=\"upload-zone\"\n :class=\"{ 'upload-zone--active': isActive, 'upload-zone--disabled': disabled }\"\n @dragenter=\"!disabled && dragDrop.onDragEnter\"\n @dragover=\"!disabled && dragDrop.onDragOver\"\n @dragleave=\"!disabled && dragDrop.onDragLeave\"\n @drop=\"!disabled && dragDrop.onDrop\"\n @click=\"openFilePicker\"\n role=\"button\"\n :tabindex=\"disabled ? -1 : 0\"\n :aria-disabled=\"disabled\"\n :aria-label=\"multiple ? 'Upload multiple files' : 'Upload a file'\"\n @keydown.enter=\"openFilePicker\"\n @keydown.space.prevent=\"openFilePicker\"\n >\n <slot\n :is-dragging=\"dragDrop.state.value.isDragging\"\n :is-over=\"dragDrop.state.value.isOver\"\n :is-uploading=\"isUploading\"\n :errors=\"[...dragDrop.state.value.errors]\"\n :open-file-picker=\"openFilePicker\"\n >\n <!-- Default slot content -->\n <div class=\"upload-zone__content\">\n <p v-if=\"dragDrop.state.value.isDragging\">\n {{ multiple ? 'Drop files here...' : 'Drop file here...' }}\n </p>\n <p v-else>\n {{ multiple ? 'Drag files here or click to select' : 'Drag a file here or click to select' }}\n </p>\n\n <div v-if=\"dragDrop.state.value.errors.length > 0\" class=\"upload-zone__errors\">\n <p v-for=\"(error, index) in dragDrop.state.value.errors\" :key=\"index\">\n {{ error }}\n </p>\n </div>\n </div>\n </slot>\n\n <input\n ref=\"fileInputRef\"\n type=\"file\"\n :multiple=\"dragDrop.inputProps.value.multiple\"\n :accept=\"dragDrop.inputProps.value.accept\"\n :disabled=\"disabled\"\n @change=\"dragDrop.onInputChange\"\n style=\"display: none\"\n aria-hidden=\"true\"\n />\n </div>\n</template>\n\n<style scoped>\n.upload-zone {\n cursor: pointer;\n user-select: none;\n}\n\n.upload-zone--disabled {\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n.upload-zone__content {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n}\n\n.upload-zone__errors {\n margin-top: 0.5rem;\n color: #dc3545;\n font-size: 0.875rem;\n}\n\n.upload-zone__errors p {\n margin: 0.25rem 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { FlowUploadOptions } from \"@uploadista/client-browser\";\nimport type { TypedOutput } from \"@uploadista/core/flow\";\nimport { provide, computed, toRefs } from \"vue\";\nimport { useFlow, type UseFlowReturn, type FlowInputMetadata } from \"../../composables/useFlow\";\nimport type { FlowUploadState, FlowUploadStatus, InputExecutionState } from \"@uploadista/client-core\";\n\n/**\n * Props for the Flow root component.\n */\nexport interface FlowProps {\n /** Flow ID to execute */\n flowId: string;\n /** Storage ID for file uploads */\n storageId: string;\n /** Optional output node ID to wait for */\n outputNodeId?: string;\n /** Optional metadata to include with the flow execution */\n metadata?: Record<string, string>;\n}\n\nconst props = defineProps<FlowProps>();\n\nconst emit = defineEmits<{\n /** Called when flow completes successfully */\n success: [outputs: TypedOutput[]];\n /** Called when flow fails */\n error: [error: Error];\n /** Called on upload progress */\n progress: [uploadId: string, bytesUploaded: number, totalBytes: number | null];\n /** Called when flow completes with all outputs */\n flowComplete: [outputs: TypedOutput[]];\n /** Called when upload is aborted */\n abort: [];\n}>();\n\n// Build options from props and emit handlers\nconst options: FlowUploadOptions = {\n flowConfig: {\n flowId: props.flowId,\n storageId: props.storageId,\n outputNodeId: props.outputNodeId,\n metadata: props.metadata,\n },\n onSuccess: (outputs) => emit(\"success\", outputs),\n onError: (error) => emit(\"error\", error),\n onProgress: (uploadId, bytesUploaded, totalBytes) =>\n emit(\"progress\", uploadId, bytesUploaded, totalBytes),\n onFlowComplete: (outputs) => emit(\"flowComplete\", outputs),\n onAbort: () => emit(\"abort\"),\n};\n\nconst flow = useFlow(options);\n\n// Re-export types for convenience\nexport type {\n FlowUploadState,\n FlowUploadStatus,\n InputExecutionState,\n FlowInputMetadata,\n};\n\n/**\n * Context value provided by the Flow component root.\n * Contains all flow state and actions.\n */\nexport interface FlowContextValue {\n /** Current upload state */\n state: UseFlowReturn[\"state\"];\n /** Discovered input nodes metadata (null until discovery completes) */\n inputMetadata: UseFlowReturn[\"inputMetadata\"];\n /** Current input values set via setInput() */\n inputs: UseFlowReturn[\"inputs\"];\n /** Per-input execution state for multi-input flows */\n inputStates: UseFlowReturn[\"inputStates\"];\n\n /** Set an input value for a specific node */\n setInput: UseFlowReturn[\"setInput\"];\n /** Execute the flow with current inputs */\n execute: UseFlowReturn[\"execute\"];\n /** Upload a single file through the flow */\n upload: UseFlowReturn[\"upload\"];\n /** Abort the current upload */\n abort: UseFlowReturn[\"abort\"];\n /** Pause the current upload */\n pause: UseFlowReturn[\"pause\"];\n /** Reset the upload state and clear all inputs */\n reset: UseFlowReturn[\"reset\"];\n\n /** Whether an upload or flow execution is in progress */\n isUploading: UseFlowReturn[\"isUploading\"];\n /** Whether the file is currently being uploaded */\n isUploadingFile: UseFlowReturn[\"isUploadingFile\"];\n /** Whether the flow is currently processing */\n isProcessing: UseFlowReturn[\"isProcessing\"];\n /** Whether the hook is discovering flow inputs */\n isDiscoveringInputs: UseFlowReturn[\"isDiscoveringInputs\"];\n}\n\n// Create the context value\nconst contextValue: FlowContextValue = {\n state: flow.state,\n inputMetadata: flow.inputMetadata,\n inputs: flow.inputs,\n inputStates: flow.inputStates,\n setInput: flow.setInput,\n execute: flow.execute,\n upload: flow.upload,\n abort: flow.abort,\n pause: flow.pause,\n reset: flow.reset,\n isUploading: flow.isUploading,\n isUploadingFile: flow.isUploadingFile,\n isProcessing: flow.isProcessing,\n isDiscoveringInputs: flow.isDiscoveringInputs,\n};\n\n// Provide context for child components\nprovide(\"flowContext\", contextValue);\n\n// Also expose to parent via defineExpose for programmatic access\ndefineExpose(contextValue);\n</script>\n\n<template>\n <slot />\n</template>\n","import { inject } from \"vue\";\nimport type { FlowContextValue } from \"./Flow.vue\";\n\n/**\n * Injection key for the Flow context\n */\nexport const FLOW_CONTEXT_KEY = \"flowContext\";\n\n/**\n * Injection key for the FlowInput context\n */\nexport const FLOW_INPUT_CONTEXT_KEY = \"flowInputContext\";\n\n/**\n * Context value for a specific input node within a Flow.\n */\nexport interface FlowInputContextValue {\n /** Input node ID */\n nodeId: string;\n /** Input metadata from flow discovery */\n metadata: {\n nodeId: string;\n nodeName: string;\n nodeDescription: string;\n inputTypeId?: string;\n required: boolean;\n };\n /** Current value for this input */\n value: unknown;\n /** Set the value for this input */\n setValue: (value: unknown) => void;\n /** Per-input execution state (if available) */\n state: {\n status: string;\n progress: number;\n error: Error | null;\n } | undefined;\n}\n\n/**\n * Hook to access flow context from within a Flow component.\n * @throws Error if used outside of a Flow component\n */\nexport function useFlowContext(): FlowContextValue {\n const context = inject<FlowContextValue>(FLOW_CONTEXT_KEY);\n if (!context) {\n throw new Error(\n \"useFlowContext must be used within a <Flow> component. \" +\n 'Wrap your component tree with <Flow flowId=\"...\" storageId=\"...\">',\n );\n }\n return context;\n}\n\n/**\n * Hook to access flow input context from within a FlowInput component.\n * @throws Error if used outside of a FlowInput component\n */\nexport function useFlowInputContext(): FlowInputContextValue {\n const context = inject<FlowInputContextValue>(FLOW_INPUT_CONTEXT_KEY);\n if (!context) {\n throw new Error(\n \"useFlowInputContext must be used within a <FlowInput> component. \" +\n 'Wrap your component with <FlowInput nodeId=\"...\">',\n );\n }\n return context;\n}\n","<script setup lang=\"ts\">\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\nconst handleClick = () => {\n flow.abort();\n};\n</script>\n\n<template>\n <button\n type=\"button\"\n @click=\"handleClick\"\n v-bind=\"$attrs\"\n >\n <slot>Cancel</slot>\n </button>\n</template>\n\n<script lang=\"ts\">\n// Disable attribute inheritance so we can spread them manually\nexport default {\n inheritAttrs: false,\n};\n</script>\n","<script setup lang=\"ts\">\nimport { computed, ref } from \"vue\";\nimport { useDragDrop, type DragDropState } from \"../../composables/useDragDrop\";\nimport { useFlowContext } from \"./useFlowContext\";\n\n/**\n * Props for FlowDropZone component.\n */\nexport interface FlowDropZoneProps {\n /** Accepted file types (e.g., \"image/*\", \".pdf\") */\n accept?: string;\n /** Maximum file size in bytes */\n maxFileSize?: number;\n}\n\nconst props = withDefaults(defineProps<FlowDropZoneProps>(), {\n accept: undefined,\n maxFileSize: undefined,\n});\n\nconst flow = useFlowContext();\nconst inputRef = ref<HTMLInputElement | null>(null);\n\nconst dragDrop = useDragDrop({\n onFilesReceived: (files) => {\n const file = files[0];\n if (file) {\n flow.upload(file);\n }\n },\n accept: props.accept ? props.accept.split(\",\").map((t) => t.trim()) : undefined,\n maxFileSize: props.maxFileSize,\n multiple: false,\n});\n\nconst openFilePicker = () => {\n inputRef.value?.click();\n};\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowDropZoneSlotProps {\n /** Whether files are being dragged over */\n isDragging: boolean;\n /** Whether drag is over the zone */\n isOver: boolean;\n /** Upload progress (0-100) */\n progress: number;\n /** Current flow status */\n status: string;\n /** Current drag-drop state */\n dragDropState: DragDropState;\n /** Open file picker programmatically */\n openFilePicker: () => void;\n /** Drag event handlers to spread on the container */\n dragHandlers: {\n onDragenter: (e: DragEvent) => void;\n onDragover: (e: DragEvent) => void;\n onDragleave: (e: DragEvent) => void;\n onDrop: (e: DragEvent) => void;\n };\n /** Input props for the hidden file input */\n inputProps: {\n type: \"file\";\n multiple: boolean;\n accept: string | undefined;\n };\n /** Input change handler */\n onInputChange: (e: Event) => void;\n /** Ref for the file input element */\n inputRef: typeof inputRef;\n}\n\nconst slotProps = computed<FlowDropZoneSlotProps>(() => ({\n isDragging: dragDrop.state.value.isDragging,\n isOver: dragDrop.state.value.isOver,\n progress: flow.state.value.progress,\n status: flow.state.value.status,\n dragDropState: dragDrop.state.value,\n openFilePicker,\n dragHandlers: {\n onDragenter: dragDrop.onDragEnter,\n onDragover: dragDrop.onDragOver,\n onDragleave: dragDrop.onDragLeave,\n onDrop: dragDrop.onDrop,\n },\n inputProps: dragDrop.inputProps.value,\n onInputChange: dragDrop.onInputChange,\n inputRef,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\">\n <!-- Default content if no slot provided -->\n <div\n v-bind=\"slotProps.dragHandlers\"\n @click=\"openFilePicker\"\n :style=\"{\n border: slotProps.isDragging ? '2px dashed #3b82f6' : '2px dashed #d1d5db',\n borderRadius: '0.5rem',\n padding: '2rem',\n textAlign: 'center',\n cursor: flow.isUploading.value ? 'not-allowed' : 'pointer',\n opacity: flow.isUploading.value ? 0.5 : 1,\n backgroundColor: slotProps.isOver ? '#eff6ff' : 'transparent',\n transition: 'all 0.2s ease',\n }\"\n >\n <p v-if=\"slotProps.isDragging\">Drop file here...</p>\n <p v-else-if=\"flow.isUploading.value\">Uploading... {{ slotProps.progress }}%</p>\n <p v-else>Drag and drop a file here, or click to select</p>\n </div>\n <input\n ref=\"inputRef\"\n type=\"file\"\n :multiple=\"slotProps.inputProps.multiple\"\n :accept=\"slotProps.inputProps.accept\"\n @change=\"slotProps.onInputChange\"\n style=\"display: none\"\n />\n </slot>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowErrorSlotProps {\n /** Error object (null if no error) */\n error: Error | null;\n /** Whether there is an error */\n hasError: boolean;\n /** Error message */\n message: string | null;\n /** Reset the flow */\n reset: () => void;\n}\n\nconst slotProps = computed<FlowErrorSlotProps>(() => ({\n error: flow.state.value.error,\n hasError: flow.state.value.status === \"error\",\n message: flow.state.value.error?.message ?? null,\n reset: flow.reset,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\">\n <!-- Default error display -->\n <div\n v-if=\"slotProps.hasError\"\n style=\"padding: 1rem; background: #fef2f2; border: 1px solid #fecaca; border-radius: 0.5rem; color: #dc2626;\"\n >\n <p style=\"margin: 0; font-weight: 600;\">Error</p>\n <p style=\"margin: 0.25rem 0 0; font-size: 0.875rem;\">{{ slotProps.message }}</p>\n <button\n type=\"button\"\n @click=\"slotProps.reset\"\n style=\"margin-top: 0.75rem; padding: 0.5rem 1rem; background: #dc2626; color: white; border: none; border-radius: 0.375rem; cursor: pointer;\"\n >\n Try Again\n </button>\n </div>\n </slot>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, provide } from \"vue\";\nimport { useFlowContext, FLOW_INPUT_CONTEXT_KEY, type FlowInputContextValue } from \"./useFlowContext\";\n\n/**\n * Props for FlowInput component.\n */\nexport interface FlowInputProps {\n /** Input node ID */\n nodeId: string;\n}\n\nconst props = defineProps<FlowInputProps>();\nconst flow = useFlowContext();\n\n// Find metadata for this input\nconst metadata = computed(() =>\n flow.inputMetadata.value?.find((m) => m.nodeId === props.nodeId)\n);\n\n// Get current value for this input\nconst value = computed(() => flow.inputs.value[props.nodeId]);\n\n// Get execution state for this input\nconst inputState = computed(() => flow.inputStates.value.get(props.nodeId));\n\n// Create setValue function scoped to this input\nconst setValue = (newValue: unknown) => {\n flow.setInput(props.nodeId, newValue);\n};\n\n// Create a context object with getters that access computed refs\n// This ensures reactivity works while also providing stable function references\nconst contextValue: FlowInputContextValue = {\n get nodeId() {\n return props.nodeId;\n },\n get metadata() {\n return metadata.value ?? {\n nodeId: props.nodeId,\n nodeName: \"\",\n nodeDescription: \"\",\n required: false,\n };\n },\n get value() {\n return value.value;\n },\n setValue,\n get state() {\n return inputState.value;\n },\n};\n\n// Provide context for child components (FlowInputDropZone, etc.)\nprovide(FLOW_INPUT_CONTEXT_KEY, contextValue);\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowInputSlotProps {\n /** Input node ID */\n nodeId: string;\n /** Input metadata from flow discovery */\n metadata: FlowInputContextValue[\"metadata\"] | undefined;\n /** Current value for this input */\n value: unknown;\n /** Set the value for this input */\n setValue: (value: unknown) => void;\n /** Per-input execution state (if available) */\n state: FlowInputContextValue[\"state\"];\n}\n\nconst slotProps = computed<FlowInputSlotProps>(() => ({\n nodeId: props.nodeId,\n metadata: metadata.value,\n value: value.value,\n setValue,\n state: inputState.value,\n}));\n</script>\n\n<template>\n <!-- Only render if metadata is found -->\n <slot v-if=\"metadata\" v-bind=\"slotProps\" />\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from \"vue\";\nimport { useDragDrop, type DragDropState } from \"../../composables/useDragDrop\";\nimport { useFlowInputContext } from \"./useFlowContext\";\n\n// Helper function to check if value is a File (for template use)\nconst isFile = (value: unknown): value is File => value instanceof File;\n\n/**\n * Props for FlowInputDropZone component.\n */\nexport interface FlowInputDropZoneProps {\n /** Accepted file types (e.g., \"image/*\", \".pdf\") */\n accept?: string;\n /** Maximum file size in bytes */\n maxFileSize?: number;\n}\n\nconst props = withDefaults(defineProps<FlowInputDropZoneProps>(), {\n accept: undefined,\n maxFileSize: undefined,\n});\n\nconst input = useFlowInputContext();\nconst inputRef = ref<HTMLInputElement | null>(null);\n\nconst dragDrop = useDragDrop({\n onFilesReceived: (files) => {\n const file = files[0];\n if (file) {\n // Set the input value but don't trigger upload yet\n input.setValue(file);\n }\n },\n accept: props.accept ? props.accept.split(\",\").map((t) => t.trim()) : undefined,\n maxFileSize: props.maxFileSize,\n multiple: false,\n});\n\nconst openFilePicker = () => {\n inputRef.value?.click();\n};\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowInputDropZoneSlotProps {\n /** Whether files are being dragged over */\n isDragging: boolean;\n /** Whether drag is over the zone */\n isOver: boolean;\n /** Current value for this input */\n value: unknown;\n /** Per-input progress (if available) */\n progress: number;\n /** Per-input status (if available) */\n status: string;\n /** Current drag-drop state */\n dragDropState: DragDropState;\n /** Open file picker programmatically */\n openFilePicker: () => void;\n /** Drag event handlers to spread on the container */\n dragHandlers: {\n onDragenter: (e: DragEvent) => void;\n onDragover: (e: DragEvent) => void;\n onDragleave: (e: DragEvent) => void;\n onDrop: (e: DragEvent) => void;\n };\n /** Input props for the hidden file input */\n inputProps: {\n type: \"file\";\n multiple: boolean;\n accept: string | undefined;\n };\n /** Input change handler */\n onInputChange: (e: Event) => void;\n /** Ref for the file input element */\n inputRef: typeof inputRef;\n}\n\nconst slotProps = computed<FlowInputDropZoneSlotProps>(() => ({\n isDragging: dragDrop.state.value.isDragging,\n isOver: dragDrop.state.value.isOver,\n value: input.value,\n progress: input.state?.progress ?? 0,\n status: input.state?.status ?? \"idle\",\n dragDropState: dragDrop.state.value,\n openFilePicker,\n dragHandlers: {\n onDragenter: dragDrop.onDragEnter,\n onDragover: dragDrop.onDragOver,\n onDragleave: dragDrop.onDragLeave,\n onDrop: dragDrop.onDrop,\n },\n inputProps: dragDrop.inputProps.value,\n onInputChange: dragDrop.onInputChange,\n inputRef,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\">\n <!-- Default content if no slot provided -->\n <div\n v-bind=\"slotProps.dragHandlers\"\n @click=\"openFilePicker\"\n :style=\"{\n border: slotProps.isDragging ? '2px dashed #3b82f6' : '2px dashed #d1d5db',\n borderRadius: '0.5rem',\n padding: '2rem',\n textAlign: 'center',\n cursor: 'pointer',\n backgroundColor: slotProps.isOver ? '#eff6ff' : 'transparent',\n transition: 'all 0.2s ease',\n }\"\n >\n <p v-if=\"slotProps.isDragging\">Drop file here...</p>\n <p v-else-if=\"isFile(slotProps.value)\">\n Selected: {{ slotProps.value.name }}\n </p>\n <p v-else>Drag and drop a file here, or click to select</p>\n </div>\n <input\n ref=\"inputRef\"\n type=\"file\"\n :multiple=\"slotProps.inputProps.multiple\"\n :accept=\"slotProps.inputProps.accept\"\n @change=\"slotProps.onInputChange\"\n style=\"display: none\"\n />\n </slot>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { useFlowInputContext } from \"./useFlowContext\";\n\nconst input = useFlowInputContext();\n\nconst isFile = computed(() => input.value instanceof File);\nconst isUrl = computed(() => typeof input.value === \"string\" && (input.value as string).length > 0);\n\nconst clear = () => {\n input.setValue(undefined);\n};\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowInputPreviewSlotProps {\n /** Current value */\n value: unknown;\n /** Whether value is a File */\n isFile: boolean;\n /** Whether value is a URL string */\n isUrl: boolean;\n /** File name (if value is File) */\n fileName: string | null;\n /** File size in bytes (if value is File) */\n fileSize: number | null;\n /** Clear the input value */\n clear: () => void;\n}\n\nconst slotProps = computed<FlowInputPreviewSlotProps>(() => ({\n value: input.value,\n isFile: isFile.value,\n isUrl: isUrl.value,\n fileName: isFile.value ? (input.value as File).name : null,\n fileSize: isFile.value ? (input.value as File).size : null,\n clear,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\">\n <!-- Default preview content - only render for File or URL values -->\n <div v-if=\"slotProps.isFile || slotProps.isUrl\" style=\"display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem; background: #f3f4f6; border-radius: 0.375rem;\">\n <div style=\"flex: 1; min-width: 0;\">\n <p v-if=\"slotProps.isFile\" style=\"margin: 0; font-size: 0.875rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\">\n {{ slotProps.fileName }}\n <span v-if=\"slotProps.fileSize\" style=\"color: #6b7280; margin-left: 0.25rem;\">\n ({{ (slotProps.fileSize / 1024).toFixed(1) }} KB)\n </span>\n </p>\n <p v-else-if=\"slotProps.isUrl\" style=\"margin: 0; font-size: 0.875rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\">\n {{ slotProps.value }}\n </p>\n </div>\n <button\n type=\"button\"\n @click=\"clear\"\n style=\"padding: 0.25rem 0.5rem; background: transparent; border: none; cursor: pointer; color: #6b7280;\"\n aria-label=\"Clear\"\n >\n ×\n </button>\n </div>\n </slot>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport type { FlowInputMetadata } from \"../../composables/useFlow\";\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowInputsSlotProps {\n /** Discovered input metadata */\n inputs: FlowInputMetadata[];\n /** Whether inputs are still being discovered */\n isLoading: boolean;\n}\n\nconst slotProps = computed<FlowInputsSlotProps>(() => ({\n inputs: flow.inputMetadata.value ?? [],\n isLoading: flow.isDiscoveringInputs.value,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\">\n <!-- Default loading state if no slot provided -->\n <div v-if=\"slotProps.isLoading\" style=\"padding: 1rem; text-align: center;\">\n Discovering flow inputs...\n </div>\n <div v-else-if=\"slotProps.inputs.length === 0\" style=\"padding: 1rem; text-align: center; color: #6b7280;\">\n No inputs found for this flow.\n </div>\n </slot>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { useFlowInputContext } from \"./useFlowContext\";\n\n/**\n * Props for FlowInputUrlField component.\n */\nexport interface FlowInputUrlFieldProps {\n /** Placeholder text */\n placeholder?: string;\n}\n\nconst props = withDefaults(defineProps<FlowInputUrlFieldProps>(), {\n placeholder: \"https://example.com/file\",\n});\n\nconst input = useFlowInputContext();\n\n// Check if value is a URL string\nconst isUrl = computed(() => typeof input.value === \"string\");\nconst urlValue = computed(() => (isUrl.value ? (input.value as string) : \"\"));\n\nconst handleInput = (event: Event) => {\n const target = event.target as HTMLInputElement;\n input.setValue(target.value);\n};\n</script>\n\n<template>\n <input\n type=\"url\"\n :value=\"urlValue\"\n @input=\"handleInput\"\n :placeholder=\"placeholder\"\n v-bind=\"$attrs\"\n />\n</template>\n\n<script lang=\"ts\">\n// Disable attribute inheritance so we can spread them manually\nexport default {\n inheritAttrs: false,\n};\n</script>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport type { FlowUploadStatus } from \"@uploadista/client-core\";\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowProgressSlotProps {\n /** Progress percentage (0-100) */\n progress: number;\n /** Bytes uploaded so far */\n bytesUploaded: number;\n /** Total bytes to upload (null if unknown) */\n totalBytes: number | null;\n /** Current status */\n status: FlowUploadStatus;\n}\n\nconst slotProps = computed<FlowProgressSlotProps>(() => ({\n progress: flow.state.value.progress,\n bytesUploaded: flow.state.value.bytesUploaded,\n totalBytes: flow.state.value.totalBytes,\n status: flow.state.value.status,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\">\n <!-- Default progress display -->\n <div v-if=\"slotProps.status === 'uploading' || slotProps.status === 'processing'\" style=\"width: 100%;\">\n <div style=\"display: flex; justify-content: space-between; margin-bottom: 0.25rem; font-size: 0.875rem;\">\n <span>{{ slotProps.status === 'uploading' ? 'Uploading' : 'Processing' }}</span>\n <span>{{ slotProps.progress.toFixed(1) }}%</span>\n </div>\n <div style=\"width: 100%; height: 0.5rem; background: #e5e7eb; border-radius: 0.25rem; overflow: hidden;\">\n <div\n :style=\"{\n width: `${slotProps.progress}%`,\n height: '100%',\n background: '#3b82f6',\n transition: 'width 0.2s ease',\n }\"\n />\n </div>\n <div v-if=\"slotProps.totalBytes\" style=\"margin-top: 0.25rem; font-size: 0.75rem; color: #6b7280;\">\n {{ (slotProps.bytesUploaded / 1024).toFixed(0) }} KB / {{ (slotProps.totalBytes / 1024).toFixed(0) }} KB\n </div>\n </div>\n </slot>\n</template>\n","<script setup lang=\"ts\">\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\nconst handleClick = () => {\n flow.reset();\n};\n</script>\n\n<template>\n <button\n type=\"button\"\n @click=\"handleClick\"\n v-bind=\"$attrs\"\n >\n <slot>Reset</slot>\n </button>\n</template>\n\n<script lang=\"ts\">\n// Disable attribute inheritance so we can spread them manually\nexport default {\n inheritAttrs: false,\n};\n</script>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport type { FlowUploadStatus } from \"@uploadista/client-core\";\nimport type { TypedOutput } from \"@uploadista/core/flow\";\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\n/**\n * Slot props provided to the default slot.\n */\nexport interface FlowStatusSlotProps {\n /** Current status */\n status: FlowUploadStatus;\n /** Current node being processed (if any) */\n currentNodeName: string | null;\n /** Current node type (if any) */\n currentNodeType: string | null;\n /** Error (if status is error) */\n error: Error | null;\n /** Job ID (if started) */\n jobId: string | null;\n /** Whether flow has started */\n flowStarted: boolean;\n /** Flow outputs (if completed) */\n flowOutputs: TypedOutput[] | null;\n}\n\nconst slotProps = computed<FlowStatusSlotProps>(() => ({\n status: flow.state.value.status,\n currentNodeName: flow.state.value.currentNodeName,\n currentNodeType: flow.state.value.currentNodeType,\n error: flow.state.value.error,\n jobId: flow.state.value.jobId,\n flowStarted: flow.state.value.flowStarted,\n flowOutputs: flow.state.value.flowOutputs,\n}));\n</script>\n\n<template>\n <slot v-bind=\"slotProps\">\n <!-- Default status display - only show when not idle -->\n <div v-if=\"slotProps.status !== 'idle'\" style=\"padding: 0.5rem;\">\n <p style=\"margin: 0; font-size: 0.875rem; color: #6b7280;\">\n Status: <strong>{{ slotProps.status }}</strong>\n </p>\n <p v-if=\"slotProps.currentNodeName\" style=\"margin: 0.25rem 0 0; font-size: 0.75rem; color: #9ca3af;\">\n Processing: {{ slotProps.currentNodeName }}\n </p>\n <p v-if=\"slotProps.jobId\" style=\"margin: 0.25rem 0 0; font-size: 0.75rem; color: #9ca3af;\">\n Job ID: {{ slotProps.jobId }}\n </p>\n </div>\n </slot>\n</template>\n","<script setup lang=\"ts\">\nimport { useFlowContext } from \"./useFlowContext\";\n\nconst flow = useFlowContext();\n\nconst handleClick = () => {\n flow.execute();\n};\n</script>\n\n<template>\n <button\n type=\"button\"\n @click=\"handleClick\"\n :disabled=\"flow.isUploading.value\"\n v-bind=\"$attrs\"\n >\n <slot>Execute</slot>\n </button>\n</template>\n\n<script lang=\"ts\">\n// Disable attribute inheritance so we can spread them manually\nexport default {\n inheritAttrs: false,\n};\n</script>\n"],"mappings":"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"}
|