@uploadista/vue 0.0.20-beta.5 → 0.0.20-beta.7

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.
Files changed (55) hide show
  1. package/dist/components/index.d.mts +3 -3
  2. package/dist/components/index.mjs +1 -1
  3. package/dist/components-Dk25ojCY.mjs +2 -0
  4. package/dist/components-Dk25ojCY.mjs.map +1 -0
  5. package/dist/composables/index.d.mts +1 -1
  6. package/dist/composables/index.mjs +1 -1
  7. package/dist/composables-BZ2c_WgI.mjs +2 -0
  8. package/dist/composables-BZ2c_WgI.mjs.map +1 -0
  9. package/dist/index-CDJUpsAf.d.mts +49 -0
  10. package/dist/index-CDJUpsAf.d.mts.map +1 -0
  11. package/dist/index-Ci1I0jRB.d.mts +787 -0
  12. package/dist/index-Ci1I0jRB.d.mts.map +1 -0
  13. package/dist/{index-stnCFF2-.d.mts → index-qvkAz1kU.d.mts} +18 -18
  14. package/dist/index-qvkAz1kU.d.mts.map +1 -0
  15. package/dist/index.d.mts +4 -4
  16. package/dist/index.mjs +1 -1
  17. package/dist/providers/index.d.mts +2 -2
  18. package/dist/providers/index.mjs +1 -1
  19. package/dist/providers-DL9Qq-3z.mjs +2 -0
  20. package/dist/providers-DL9Qq-3z.mjs.map +1 -0
  21. package/dist/useUploadistaClient-WVuo8jYH.mjs +2 -0
  22. package/dist/useUploadistaClient-WVuo8jYH.mjs.map +1 -0
  23. package/package.json +5 -5
  24. package/src/components/flow/Flow.vue +127 -0
  25. package/src/components/flow/FlowCancel.vue +26 -0
  26. package/src/components/flow/FlowDropZone.vue +124 -0
  27. package/src/components/flow/FlowError.vue +47 -0
  28. package/src/components/flow/FlowInput.vue +86 -0
  29. package/src/components/flow/FlowInputDropZone.vue +129 -0
  30. package/src/components/flow/FlowInputPreview.vue +67 -0
  31. package/src/components/flow/FlowInputUrlField.vue +44 -0
  32. package/src/components/flow/FlowInputs.vue +34 -0
  33. package/src/components/flow/FlowProgress.vue +53 -0
  34. package/src/components/flow/FlowReset.vue +26 -0
  35. package/src/components/flow/FlowStatus.vue +55 -0
  36. package/src/components/flow/FlowSubmit.vue +27 -0
  37. package/src/components/flow/index.ts +101 -0
  38. package/src/components/flow/useFlowContext.ts +68 -0
  39. package/src/components/index.ts +3 -0
  40. package/src/providers/FlowManagerProvider.vue +4 -4
  41. package/src/providers/UploadistaProvider.vue +4 -1
  42. package/src/providers/index.ts +1 -0
  43. package/dist/components-Bhroc6MN.mjs +0 -2
  44. package/dist/components-Bhroc6MN.mjs.map +0 -1
  45. package/dist/composables-7rR8DrBp.mjs +0 -2
  46. package/dist/composables-7rR8DrBp.mjs.map +0 -1
  47. package/dist/index-Co5GjwL2.d.mts +0 -183
  48. package/dist/index-Co5GjwL2.d.mts.map +0 -1
  49. package/dist/index-cDdde3bt.d.mts +0 -33
  50. package/dist/index-cDdde3bt.d.mts.map +0 -1
  51. package/dist/index-stnCFF2-.d.mts.map +0 -1
  52. package/dist/plugin-BC-8nlFO.mjs +0 -2
  53. package/dist/plugin-BC-8nlFO.mjs.map +0 -1
  54. package/dist/providers-kZkr_iMD.mjs +0 -2
  55. package/dist/providers-kZkr_iMD.mjs.map +0 -1
@@ -0,0 +1,47 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+ import { useFlowContext } from "./useFlowContext";
4
+
5
+ const flow = useFlowContext();
6
+
7
+ /**
8
+ * Slot props provided to the default slot.
9
+ */
10
+ export interface FlowErrorSlotProps {
11
+ /** Error object (null if no error) */
12
+ error: Error | null;
13
+ /** Whether there is an error */
14
+ hasError: boolean;
15
+ /** Error message */
16
+ message: string | null;
17
+ /** Reset the flow */
18
+ reset: () => void;
19
+ }
20
+
21
+ const slotProps = computed<FlowErrorSlotProps>(() => ({
22
+ error: flow.state.value.error,
23
+ hasError: flow.state.value.status === "error",
24
+ message: flow.state.value.error?.message ?? null,
25
+ reset: flow.reset,
26
+ }));
27
+ </script>
28
+
29
+ <template>
30
+ <slot v-bind="slotProps">
31
+ <!-- Default error display -->
32
+ <div
33
+ v-if="slotProps.hasError"
34
+ style="padding: 1rem; background: #fef2f2; border: 1px solid #fecaca; border-radius: 0.5rem; color: #dc2626;"
35
+ >
36
+ <p style="margin: 0; font-weight: 600;">Error</p>
37
+ <p style="margin: 0.25rem 0 0; font-size: 0.875rem;">{{ slotProps.message }}</p>
38
+ <button
39
+ type="button"
40
+ @click="slotProps.reset"
41
+ style="margin-top: 0.75rem; padding: 0.5rem 1rem; background: #dc2626; color: white; border: none; border-radius: 0.375rem; cursor: pointer;"
42
+ >
43
+ Try Again
44
+ </button>
45
+ </div>
46
+ </slot>
47
+ </template>
@@ -0,0 +1,86 @@
1
+ <script setup lang="ts">
2
+ import { computed, provide } from "vue";
3
+ import { useFlowContext, FLOW_INPUT_CONTEXT_KEY, type FlowInputContextValue } from "./useFlowContext";
4
+
5
+ /**
6
+ * Props for FlowInput component.
7
+ */
8
+ export interface FlowInputProps {
9
+ /** Input node ID */
10
+ nodeId: string;
11
+ }
12
+
13
+ const props = defineProps<FlowInputProps>();
14
+ const flow = useFlowContext();
15
+
16
+ // Find metadata for this input
17
+ const metadata = computed(() =>
18
+ flow.inputMetadata.value?.find((m) => m.nodeId === props.nodeId)
19
+ );
20
+
21
+ // Get current value for this input
22
+ const value = computed(() => flow.inputs.value[props.nodeId]);
23
+
24
+ // Get execution state for this input
25
+ const inputState = computed(() => flow.inputStates.value.get(props.nodeId));
26
+
27
+ // Create setValue function scoped to this input
28
+ const setValue = (newValue: unknown) => {
29
+ flow.setInput(props.nodeId, newValue);
30
+ };
31
+
32
+ // Create a context object with getters that access computed refs
33
+ // This ensures reactivity works while also providing stable function references
34
+ const contextValue: FlowInputContextValue = {
35
+ get nodeId() {
36
+ return props.nodeId;
37
+ },
38
+ get metadata() {
39
+ return metadata.value ?? {
40
+ nodeId: props.nodeId,
41
+ nodeName: "",
42
+ nodeDescription: "",
43
+ required: false,
44
+ };
45
+ },
46
+ get value() {
47
+ return value.value;
48
+ },
49
+ setValue,
50
+ get state() {
51
+ return inputState.value;
52
+ },
53
+ };
54
+
55
+ // Provide context for child components (FlowInputDropZone, etc.)
56
+ provide(FLOW_INPUT_CONTEXT_KEY, contextValue);
57
+
58
+ /**
59
+ * Slot props provided to the default slot.
60
+ */
61
+ export interface FlowInputSlotProps {
62
+ /** Input node ID */
63
+ nodeId: string;
64
+ /** Input metadata from flow discovery */
65
+ metadata: FlowInputContextValue["metadata"] | undefined;
66
+ /** Current value for this input */
67
+ value: unknown;
68
+ /** Set the value for this input */
69
+ setValue: (value: unknown) => void;
70
+ /** Per-input execution state (if available) */
71
+ state: FlowInputContextValue["state"];
72
+ }
73
+
74
+ const slotProps = computed<FlowInputSlotProps>(() => ({
75
+ nodeId: props.nodeId,
76
+ metadata: metadata.value,
77
+ value: value.value,
78
+ setValue,
79
+ state: inputState.value,
80
+ }));
81
+ </script>
82
+
83
+ <template>
84
+ <!-- Only render if metadata is found -->
85
+ <slot v-if="metadata" v-bind="slotProps" />
86
+ </template>
@@ -0,0 +1,129 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref } from "vue";
3
+ import { useDragDrop, type DragDropState } from "../../composables/useDragDrop";
4
+ import { useFlowInputContext } from "./useFlowContext";
5
+
6
+ /**
7
+ * Props for FlowInputDropZone component.
8
+ */
9
+ export interface FlowInputDropZoneProps {
10
+ /** Accepted file types (e.g., "image/*", ".pdf") */
11
+ accept?: string;
12
+ /** Maximum file size in bytes */
13
+ maxFileSize?: number;
14
+ }
15
+
16
+ const props = withDefaults(defineProps<FlowInputDropZoneProps>(), {
17
+ accept: undefined,
18
+ maxFileSize: undefined,
19
+ });
20
+
21
+ const input = useFlowInputContext();
22
+ const inputRef = ref<HTMLInputElement | null>(null);
23
+
24
+ const dragDrop = useDragDrop({
25
+ onFilesReceived: (files) => {
26
+ const file = files[0];
27
+ if (file) {
28
+ // Set the input value but don't trigger upload yet
29
+ input.setValue(file);
30
+ }
31
+ },
32
+ accept: props.accept ? props.accept.split(",").map((t) => t.trim()) : undefined,
33
+ maxFileSize: props.maxFileSize,
34
+ multiple: false,
35
+ });
36
+
37
+ const openFilePicker = () => {
38
+ inputRef.value?.click();
39
+ };
40
+
41
+ /**
42
+ * Slot props provided to the default slot.
43
+ */
44
+ export interface FlowInputDropZoneSlotProps {
45
+ /** Whether files are being dragged over */
46
+ isDragging: boolean;
47
+ /** Whether drag is over the zone */
48
+ isOver: boolean;
49
+ /** Current value for this input */
50
+ value: unknown;
51
+ /** Per-input progress (if available) */
52
+ progress: number;
53
+ /** Per-input status (if available) */
54
+ status: string;
55
+ /** Current drag-drop state */
56
+ dragDropState: DragDropState;
57
+ /** Open file picker programmatically */
58
+ openFilePicker: () => void;
59
+ /** Drag event handlers to spread on the container */
60
+ dragHandlers: {
61
+ onDragenter: (e: DragEvent) => void;
62
+ onDragover: (e: DragEvent) => void;
63
+ onDragleave: (e: DragEvent) => void;
64
+ onDrop: (e: DragEvent) => void;
65
+ };
66
+ /** Input props for the hidden file input */
67
+ inputProps: {
68
+ type: "file";
69
+ multiple: boolean;
70
+ accept: string | undefined;
71
+ };
72
+ /** Input change handler */
73
+ onInputChange: (e: Event) => void;
74
+ /** Ref for the file input element */
75
+ inputRef: typeof inputRef;
76
+ }
77
+
78
+ const slotProps = computed<FlowInputDropZoneSlotProps>(() => ({
79
+ isDragging: dragDrop.state.value.isDragging,
80
+ isOver: dragDrop.state.value.isOver,
81
+ value: input.value,
82
+ progress: input.state?.progress ?? 0,
83
+ status: input.state?.status ?? "idle",
84
+ dragDropState: dragDrop.state.value,
85
+ openFilePicker,
86
+ dragHandlers: {
87
+ onDragenter: dragDrop.onDragEnter,
88
+ onDragover: dragDrop.onDragOver,
89
+ onDragleave: dragDrop.onDragLeave,
90
+ onDrop: dragDrop.onDrop,
91
+ },
92
+ inputProps: dragDrop.inputProps.value,
93
+ onInputChange: dragDrop.onInputChange,
94
+ inputRef,
95
+ }));
96
+ </script>
97
+
98
+ <template>
99
+ <slot v-bind="slotProps">
100
+ <!-- Default content if no slot provided -->
101
+ <div
102
+ v-bind="slotProps.dragHandlers"
103
+ @click="openFilePicker"
104
+ :style="{
105
+ border: slotProps.isDragging ? '2px dashed #3b82f6' : '2px dashed #d1d5db',
106
+ borderRadius: '0.5rem',
107
+ padding: '2rem',
108
+ textAlign: 'center',
109
+ cursor: 'pointer',
110
+ backgroundColor: slotProps.isOver ? '#eff6ff' : 'transparent',
111
+ transition: 'all 0.2s ease',
112
+ }"
113
+ >
114
+ <p v-if="slotProps.isDragging">Drop file here...</p>
115
+ <p v-else-if="slotProps.value instanceof File">
116
+ Selected: {{ (slotProps.value as File).name }}
117
+ </p>
118
+ <p v-else>Drag and drop a file here, or click to select</p>
119
+ </div>
120
+ <input
121
+ ref="inputRef"
122
+ type="file"
123
+ :multiple="slotProps.inputProps.multiple"
124
+ :accept="slotProps.inputProps.accept"
125
+ @change="slotProps.onInputChange"
126
+ style="display: none"
127
+ />
128
+ </slot>
129
+ </template>
@@ -0,0 +1,67 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+ import { useFlowInputContext } from "./useFlowContext";
4
+
5
+ const input = useFlowInputContext();
6
+
7
+ const isFile = computed(() => input.value instanceof File);
8
+ const isUrl = computed(() => typeof input.value === "string" && (input.value as string).length > 0);
9
+
10
+ const clear = () => {
11
+ input.setValue(undefined);
12
+ };
13
+
14
+ /**
15
+ * Slot props provided to the default slot.
16
+ */
17
+ export interface FlowInputPreviewSlotProps {
18
+ /** Current value */
19
+ value: unknown;
20
+ /** Whether value is a File */
21
+ isFile: boolean;
22
+ /** Whether value is a URL string */
23
+ isUrl: boolean;
24
+ /** File name (if value is File) */
25
+ fileName: string | null;
26
+ /** File size in bytes (if value is File) */
27
+ fileSize: number | null;
28
+ /** Clear the input value */
29
+ clear: () => void;
30
+ }
31
+
32
+ const slotProps = computed<FlowInputPreviewSlotProps>(() => ({
33
+ value: input.value,
34
+ isFile: isFile.value,
35
+ isUrl: isUrl.value,
36
+ fileName: isFile.value ? (input.value as File).name : null,
37
+ fileSize: isFile.value ? (input.value as File).size : null,
38
+ clear,
39
+ }));
40
+ </script>
41
+
42
+ <template>
43
+ <slot v-bind="slotProps">
44
+ <!-- Default preview content - only render for File or URL values -->
45
+ <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;">
46
+ <div style="flex: 1; min-width: 0;">
47
+ <p v-if="slotProps.isFile" style="margin: 0; font-size: 0.875rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
48
+ {{ slotProps.fileName }}
49
+ <span v-if="slotProps.fileSize" style="color: #6b7280; margin-left: 0.25rem;">
50
+ ({{ (slotProps.fileSize / 1024).toFixed(1) }} KB)
51
+ </span>
52
+ </p>
53
+ <p v-else-if="slotProps.isUrl" style="margin: 0; font-size: 0.875rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
54
+ {{ slotProps.value }}
55
+ </p>
56
+ </div>
57
+ <button
58
+ type="button"
59
+ @click="clear"
60
+ style="padding: 0.25rem 0.5rem; background: transparent; border: none; cursor: pointer; color: #6b7280;"
61
+ aria-label="Clear"
62
+ >
63
+ &times;
64
+ </button>
65
+ </div>
66
+ </slot>
67
+ </template>
@@ -0,0 +1,44 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+ import { useFlowInputContext } from "./useFlowContext";
4
+
5
+ /**
6
+ * Props for FlowInputUrlField component.
7
+ */
8
+ export interface FlowInputUrlFieldProps {
9
+ /** Placeholder text */
10
+ placeholder?: string;
11
+ }
12
+
13
+ const props = withDefaults(defineProps<FlowInputUrlFieldProps>(), {
14
+ placeholder: "https://example.com/file",
15
+ });
16
+
17
+ const input = useFlowInputContext();
18
+
19
+ // Check if value is a URL string
20
+ const isUrl = computed(() => typeof input.value === "string");
21
+ const urlValue = computed(() => (isUrl.value ? (input.value as string) : ""));
22
+
23
+ const handleInput = (event: Event) => {
24
+ const target = event.target as HTMLInputElement;
25
+ input.setValue(target.value);
26
+ };
27
+ </script>
28
+
29
+ <template>
30
+ <input
31
+ type="url"
32
+ :value="urlValue"
33
+ @input="handleInput"
34
+ :placeholder="placeholder"
35
+ v-bind="$attrs"
36
+ />
37
+ </template>
38
+
39
+ <script lang="ts">
40
+ // Disable attribute inheritance so we can spread them manually
41
+ export default {
42
+ inheritAttrs: false,
43
+ };
44
+ </script>
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+ import type { FlowInputMetadata } from "../../composables/useFlow";
4
+ import { useFlowContext } from "./useFlowContext";
5
+
6
+ const flow = useFlowContext();
7
+
8
+ /**
9
+ * Slot props provided to the default slot.
10
+ */
11
+ export interface FlowInputsSlotProps {
12
+ /** Discovered input metadata */
13
+ inputs: FlowInputMetadata[];
14
+ /** Whether inputs are still being discovered */
15
+ isLoading: boolean;
16
+ }
17
+
18
+ const slotProps = computed<FlowInputsSlotProps>(() => ({
19
+ inputs: flow.inputMetadata.value ?? [],
20
+ isLoading: flow.isDiscoveringInputs.value,
21
+ }));
22
+ </script>
23
+
24
+ <template>
25
+ <slot v-bind="slotProps">
26
+ <!-- Default loading state if no slot provided -->
27
+ <div v-if="slotProps.isLoading" style="padding: 1rem; text-align: center;">
28
+ Discovering flow inputs...
29
+ </div>
30
+ <div v-else-if="slotProps.inputs.length === 0" style="padding: 1rem; text-align: center; color: #6b7280;">
31
+ No inputs found for this flow.
32
+ </div>
33
+ </slot>
34
+ </template>
@@ -0,0 +1,53 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+ import type { FlowUploadStatus } from "@uploadista/client-core";
4
+ import { useFlowContext } from "./useFlowContext";
5
+
6
+ const flow = useFlowContext();
7
+
8
+ /**
9
+ * Slot props provided to the default slot.
10
+ */
11
+ export interface FlowProgressSlotProps {
12
+ /** Progress percentage (0-100) */
13
+ progress: number;
14
+ /** Bytes uploaded so far */
15
+ bytesUploaded: number;
16
+ /** Total bytes to upload (null if unknown) */
17
+ totalBytes: number | null;
18
+ /** Current status */
19
+ status: FlowUploadStatus;
20
+ }
21
+
22
+ const slotProps = computed<FlowProgressSlotProps>(() => ({
23
+ progress: flow.state.value.progress,
24
+ bytesUploaded: flow.state.value.bytesUploaded,
25
+ totalBytes: flow.state.value.totalBytes,
26
+ status: flow.state.value.status,
27
+ }));
28
+ </script>
29
+
30
+ <template>
31
+ <slot v-bind="slotProps">
32
+ <!-- Default progress display -->
33
+ <div v-if="slotProps.status === 'uploading' || slotProps.status === 'processing'" style="width: 100%;">
34
+ <div style="display: flex; justify-content: space-between; margin-bottom: 0.25rem; font-size: 0.875rem;">
35
+ <span>{{ slotProps.status === 'uploading' ? 'Uploading' : 'Processing' }}</span>
36
+ <span>{{ slotProps.progress.toFixed(1) }}%</span>
37
+ </div>
38
+ <div style="width: 100%; height: 0.5rem; background: #e5e7eb; border-radius: 0.25rem; overflow: hidden;">
39
+ <div
40
+ :style="{
41
+ width: `${slotProps.progress}%`,
42
+ height: '100%',
43
+ background: '#3b82f6',
44
+ transition: 'width 0.2s ease',
45
+ }"
46
+ />
47
+ </div>
48
+ <div v-if="slotProps.totalBytes" style="margin-top: 0.25rem; font-size: 0.75rem; color: #6b7280;">
49
+ {{ (slotProps.bytesUploaded / 1024).toFixed(0) }} KB / {{ (slotProps.totalBytes / 1024).toFixed(0) }} KB
50
+ </div>
51
+ </div>
52
+ </slot>
53
+ </template>
@@ -0,0 +1,26 @@
1
+ <script setup lang="ts">
2
+ import { useFlowContext } from "./useFlowContext";
3
+
4
+ const flow = useFlowContext();
5
+
6
+ const handleClick = () => {
7
+ flow.reset();
8
+ };
9
+ </script>
10
+
11
+ <template>
12
+ <button
13
+ type="button"
14
+ @click="handleClick"
15
+ v-bind="$attrs"
16
+ >
17
+ <slot>Reset</slot>
18
+ </button>
19
+ </template>
20
+
21
+ <script lang="ts">
22
+ // Disable attribute inheritance so we can spread them manually
23
+ export default {
24
+ inheritAttrs: false,
25
+ };
26
+ </script>
@@ -0,0 +1,55 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+ import type { FlowUploadStatus } from "@uploadista/client-core";
4
+ import type { TypedOutput } from "@uploadista/core/flow";
5
+ import { useFlowContext } from "./useFlowContext";
6
+
7
+ const flow = useFlowContext();
8
+
9
+ /**
10
+ * Slot props provided to the default slot.
11
+ */
12
+ export interface FlowStatusSlotProps {
13
+ /** Current status */
14
+ status: FlowUploadStatus;
15
+ /** Current node being processed (if any) */
16
+ currentNodeName: string | null;
17
+ /** Current node type (if any) */
18
+ currentNodeType: string | null;
19
+ /** Error (if status is error) */
20
+ error: Error | null;
21
+ /** Job ID (if started) */
22
+ jobId: string | null;
23
+ /** Whether flow has started */
24
+ flowStarted: boolean;
25
+ /** Flow outputs (if completed) */
26
+ flowOutputs: TypedOutput[] | null;
27
+ }
28
+
29
+ const slotProps = computed<FlowStatusSlotProps>(() => ({
30
+ status: flow.state.value.status,
31
+ currentNodeName: flow.state.value.currentNodeName,
32
+ currentNodeType: flow.state.value.currentNodeType,
33
+ error: flow.state.value.error,
34
+ jobId: flow.state.value.jobId,
35
+ flowStarted: flow.state.value.flowStarted,
36
+ flowOutputs: flow.state.value.flowOutputs,
37
+ }));
38
+ </script>
39
+
40
+ <template>
41
+ <slot v-bind="slotProps">
42
+ <!-- Default status display - only show when not idle -->
43
+ <div v-if="slotProps.status !== 'idle'" style="padding: 0.5rem;">
44
+ <p style="margin: 0; font-size: 0.875rem; color: #6b7280;">
45
+ Status: <strong>{{ slotProps.status }}</strong>
46
+ </p>
47
+ <p v-if="slotProps.currentNodeName" style="margin: 0.25rem 0 0; font-size: 0.75rem; color: #9ca3af;">
48
+ Processing: {{ slotProps.currentNodeName }}
49
+ </p>
50
+ <p v-if="slotProps.jobId" style="margin: 0.25rem 0 0; font-size: 0.75rem; color: #9ca3af;">
51
+ Job ID: {{ slotProps.jobId }}
52
+ </p>
53
+ </div>
54
+ </slot>
55
+ </template>
@@ -0,0 +1,27 @@
1
+ <script setup lang="ts">
2
+ import { useFlowContext } from "./useFlowContext";
3
+
4
+ const flow = useFlowContext();
5
+
6
+ const handleClick = () => {
7
+ flow.execute();
8
+ };
9
+ </script>
10
+
11
+ <template>
12
+ <button
13
+ type="button"
14
+ @click="handleClick"
15
+ :disabled="flow.isUploading.value"
16
+ v-bind="$attrs"
17
+ >
18
+ <slot>Execute</slot>
19
+ </button>
20
+ </template>
21
+
22
+ <script lang="ts">
23
+ // Disable attribute inheritance so we can spread them manually
24
+ export default {
25
+ inheritAttrs: false,
26
+ };
27
+ </script>
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Vue 3 Flow Compound Components for Uploadista
3
+ *
4
+ * These components provide a composable, headless API for building flow upload interfaces.
5
+ * They use Vue's provide/inject for context and scoped slots for complete UI control.
6
+ *
7
+ * @example Simple Drop Zone
8
+ * ```vue
9
+ * <template>
10
+ * <Flow flowId="image-optimizer" storageId="s3" @success="handleSuccess">
11
+ * <FlowDropZone accept="image/*" v-slot="{ isDragging, progress, 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
+ * <progress v-if="progress > 0" :value="progress" max="100" />
16
+ * </div>
17
+ * </FlowDropZone>
18
+ * </Flow>
19
+ * </template>
20
+ *
21
+ * <script setup>
22
+ * import { Flow, FlowDropZone } from '@uploadista/vue'
23
+ *
24
+ * const handleSuccess = (outputs) => {
25
+ * console.log('Upload complete:', outputs)
26
+ * }
27
+ * </script>
28
+ * ```
29
+ *
30
+ * @example Multi-Input Flow
31
+ * ```vue
32
+ * <template>
33
+ * <Flow flowId="video-processor" storageId="s3">
34
+ * <FlowInputs v-slot="{ inputs, isLoading }">
35
+ * <div v-if="isLoading">Loading...</div>
36
+ * <div v-else v-for="input in inputs" :key="input.nodeId">
37
+ * <FlowInput :nodeId="input.nodeId" v-slot="{ metadata }">
38
+ * <label>{{ metadata.nodeName }}</label>
39
+ * <FlowInputDropZone accept="video/*" v-slot="{ dragHandlers, openFilePicker }">
40
+ * <div v-bind="dragHandlers" @click="openFilePicker">
41
+ * Drop video here
42
+ * </div>
43
+ * </FlowInputDropZone>
44
+ * </FlowInput>
45
+ * </div>
46
+ * </FlowInputs>
47
+ * <FlowSubmit>Process</FlowSubmit>
48
+ * </Flow>
49
+ * </template>
50
+ * ```
51
+ */
52
+
53
+ // Root component
54
+ export { default as Flow } from "./Flow.vue";
55
+ export type { FlowProps, FlowContextValue } from "./Flow.vue";
56
+
57
+ // Context hooks
58
+ export {
59
+ useFlowContext,
60
+ useFlowInputContext,
61
+ FLOW_CONTEXT_KEY,
62
+ FLOW_INPUT_CONTEXT_KEY,
63
+ type FlowInputContextValue,
64
+ } from "./useFlowContext";
65
+
66
+ // Drop zone
67
+ export { default as FlowDropZone } from "./FlowDropZone.vue";
68
+ export type { FlowDropZoneProps, FlowDropZoneSlotProps } from "./FlowDropZone.vue";
69
+
70
+ // Input discovery
71
+ export { default as FlowInputs } from "./FlowInputs.vue";
72
+ export type { FlowInputsSlotProps } from "./FlowInputs.vue";
73
+
74
+ // Input context
75
+ export { default as FlowInput } from "./FlowInput.vue";
76
+ export type { FlowInputProps, FlowInputSlotProps } from "./FlowInput.vue";
77
+
78
+ // Input primitives
79
+ export { default as FlowInputDropZone } from "./FlowInputDropZone.vue";
80
+ export type { FlowInputDropZoneProps, FlowInputDropZoneSlotProps } from "./FlowInputDropZone.vue";
81
+
82
+ export { default as FlowInputUrlField } from "./FlowInputUrlField.vue";
83
+ export type { FlowInputUrlFieldProps } from "./FlowInputUrlField.vue";
84
+
85
+ export { default as FlowInputPreview } from "./FlowInputPreview.vue";
86
+ export type { FlowInputPreviewSlotProps } from "./FlowInputPreview.vue";
87
+
88
+ // Status primitives
89
+ export { default as FlowProgress } from "./FlowProgress.vue";
90
+ export type { FlowProgressSlotProps } from "./FlowProgress.vue";
91
+
92
+ export { default as FlowStatus } from "./FlowStatus.vue";
93
+ export type { FlowStatusSlotProps } from "./FlowStatus.vue";
94
+
95
+ export { default as FlowError } from "./FlowError.vue";
96
+ export type { FlowErrorSlotProps } from "./FlowError.vue";
97
+
98
+ // Action primitives
99
+ export { default as FlowSubmit } from "./FlowSubmit.vue";
100
+ export { default as FlowCancel } from "./FlowCancel.vue";
101
+ export { default as FlowReset } from "./FlowReset.vue";