@shipstatic/drop 0.1.6 → 0.1.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.
- package/README.md +115 -23
- package/dist/index.cjs +82 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -9
- package/dist/index.d.ts +23 -9
- package/dist/index.js +83 -54
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -52,6 +52,26 @@ interface ProcessedFile extends StaticFile {
|
|
|
52
52
|
/** Upload progress (0-100) - only set during upload */
|
|
53
53
|
progress?: number;
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* State machine values for the drop hook
|
|
57
|
+
*/
|
|
58
|
+
type DropStateValue = 'idle' | 'dragging' | 'processing' | 'ready' | 'error';
|
|
59
|
+
/**
|
|
60
|
+
* Status information with title and details
|
|
61
|
+
*/
|
|
62
|
+
interface DropStatus {
|
|
63
|
+
title: string;
|
|
64
|
+
details: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* State machine state for the drop hook
|
|
68
|
+
*/
|
|
69
|
+
interface DropState {
|
|
70
|
+
value: DropStateValue;
|
|
71
|
+
files: ProcessedFile[];
|
|
72
|
+
sourceName: string;
|
|
73
|
+
status: DropStatus | null;
|
|
74
|
+
}
|
|
55
75
|
|
|
56
76
|
interface DropOptions {
|
|
57
77
|
/** Ship SDK instance (required for validation) */
|
|
@@ -64,18 +84,12 @@ interface DropOptions {
|
|
|
64
84
|
stripPrefix?: boolean;
|
|
65
85
|
}
|
|
66
86
|
interface DropReturn {
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
-
/** Name of the source (file/folder/ZIP) that was dropped/selected */
|
|
70
|
-
sourceName: string;
|
|
71
|
-
/** Current status text */
|
|
72
|
-
statusText: string;
|
|
87
|
+
/** Current state of the drop hook */
|
|
88
|
+
state: DropState;
|
|
73
89
|
/** Whether currently processing files (ZIP extraction, etc.) */
|
|
74
90
|
isProcessing: boolean;
|
|
75
91
|
/** Whether user is currently dragging over the dropzone */
|
|
76
92
|
isDragging: boolean;
|
|
77
|
-
/** Last validation error if any */
|
|
78
|
-
validationError: ClientError | null;
|
|
79
93
|
/** Get props to spread on dropzone element (handles drag & drop) */
|
|
80
94
|
getDropzoneProps: () => {
|
|
81
95
|
onDragOver: (e: React.DragEvent) => void;
|
|
@@ -189,4 +203,4 @@ declare function normalizePath(path: string): string;
|
|
|
189
203
|
*/
|
|
190
204
|
declare function isZipFile(file: File): boolean;
|
|
191
205
|
|
|
192
|
-
export { type ClientError, type DropOptions, type DropReturn, FILE_STATUSES, type FileStatus, type ProcessedFile, type ZipExtractionResult, createProcessedFile, extractZipToFiles, formatFileSize, getValidFiles, isZipFile, normalizePath, stripCommonPrefix, useDrop };
|
|
206
|
+
export { type ClientError, type DropOptions, type DropReturn, type DropState, type DropStateValue, type DropStatus, FILE_STATUSES, type FileStatus, type ProcessedFile, type ZipExtractionResult, createProcessedFile, extractZipToFiles, formatFileSize, getValidFiles, isZipFile, normalizePath, stripCommonPrefix, useDrop };
|
package/dist/index.d.ts
CHANGED
|
@@ -52,6 +52,26 @@ interface ProcessedFile extends StaticFile {
|
|
|
52
52
|
/** Upload progress (0-100) - only set during upload */
|
|
53
53
|
progress?: number;
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* State machine values for the drop hook
|
|
57
|
+
*/
|
|
58
|
+
type DropStateValue = 'idle' | 'dragging' | 'processing' | 'ready' | 'error';
|
|
59
|
+
/**
|
|
60
|
+
* Status information with title and details
|
|
61
|
+
*/
|
|
62
|
+
interface DropStatus {
|
|
63
|
+
title: string;
|
|
64
|
+
details: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* State machine state for the drop hook
|
|
68
|
+
*/
|
|
69
|
+
interface DropState {
|
|
70
|
+
value: DropStateValue;
|
|
71
|
+
files: ProcessedFile[];
|
|
72
|
+
sourceName: string;
|
|
73
|
+
status: DropStatus | null;
|
|
74
|
+
}
|
|
55
75
|
|
|
56
76
|
interface DropOptions {
|
|
57
77
|
/** Ship SDK instance (required for validation) */
|
|
@@ -64,18 +84,12 @@ interface DropOptions {
|
|
|
64
84
|
stripPrefix?: boolean;
|
|
65
85
|
}
|
|
66
86
|
interface DropReturn {
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
-
/** Name of the source (file/folder/ZIP) that was dropped/selected */
|
|
70
|
-
sourceName: string;
|
|
71
|
-
/** Current status text */
|
|
72
|
-
statusText: string;
|
|
87
|
+
/** Current state of the drop hook */
|
|
88
|
+
state: DropState;
|
|
73
89
|
/** Whether currently processing files (ZIP extraction, etc.) */
|
|
74
90
|
isProcessing: boolean;
|
|
75
91
|
/** Whether user is currently dragging over the dropzone */
|
|
76
92
|
isDragging: boolean;
|
|
77
|
-
/** Last validation error if any */
|
|
78
|
-
validationError: ClientError | null;
|
|
79
93
|
/** Get props to spread on dropzone element (handles drag & drop) */
|
|
80
94
|
getDropzoneProps: () => {
|
|
81
95
|
onDragOver: (e: React.DragEvent) => void;
|
|
@@ -189,4 +203,4 @@ declare function normalizePath(path: string): string;
|
|
|
189
203
|
*/
|
|
190
204
|
declare function isZipFile(file: File): boolean;
|
|
191
205
|
|
|
192
|
-
export { type ClientError, type DropOptions, type DropReturn, FILE_STATUSES, type FileStatus, type ProcessedFile, type ZipExtractionResult, createProcessedFile, extractZipToFiles, formatFileSize, getValidFiles, isZipFile, normalizePath, stripCommonPrefix, useDrop };
|
|
206
|
+
export { type ClientError, type DropOptions, type DropReturn, type DropState, type DropStateValue, type DropStatus, FILE_STATUSES, type FileStatus, type ProcessedFile, type ZipExtractionResult, createProcessedFile, extractZipToFiles, formatFileSize, getValidFiles, isZipFile, normalizePath, stripCommonPrefix, useDrop };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useRef, useCallback } from 'react';
|
|
1
|
+
import { useState, useRef, useMemo, useCallback } from 'react';
|
|
2
2
|
import { formatFileSize as formatFileSize$1, getValidFiles as getValidFiles$1, filterJunk, validateFiles } from '@shipstatic/ship';
|
|
3
3
|
|
|
4
4
|
var __create = Object.create;
|
|
@@ -11871,28 +11871,32 @@ function useDrop(options) {
|
|
|
11871
11871
|
onFilesReady,
|
|
11872
11872
|
stripPrefix = true
|
|
11873
11873
|
} = options;
|
|
11874
|
-
const
|
|
11875
|
-
|
|
11876
|
-
|
|
11877
|
-
|
|
11878
|
-
|
|
11879
|
-
|
|
11874
|
+
const initialState = {
|
|
11875
|
+
value: "idle",
|
|
11876
|
+
files: [],
|
|
11877
|
+
sourceName: "",
|
|
11878
|
+
status: null
|
|
11879
|
+
};
|
|
11880
|
+
const [state, setState] = useState(initialState);
|
|
11880
11881
|
const isProcessingRef = useRef(false);
|
|
11881
11882
|
const inputRef = useRef(null);
|
|
11883
|
+
const isProcessing = useMemo(() => state.value === "processing", [state.value]);
|
|
11884
|
+
const isDragging = useMemo(() => state.value === "dragging", [state.value]);
|
|
11882
11885
|
const processFiles = useCallback(async (newFiles) => {
|
|
11883
11886
|
if (isProcessingRef.current) {
|
|
11884
11887
|
console.warn("File processing already in progress. Ignoring duplicate call.");
|
|
11885
11888
|
return;
|
|
11886
11889
|
}
|
|
11887
11890
|
if (!newFiles || newFiles.length === 0) {
|
|
11888
|
-
setStatusText("No files selected.");
|
|
11889
11891
|
return;
|
|
11890
11892
|
}
|
|
11891
11893
|
isProcessingRef.current = true;
|
|
11892
|
-
|
|
11893
|
-
|
|
11894
|
-
|
|
11895
|
-
|
|
11894
|
+
setState({
|
|
11895
|
+
value: "processing",
|
|
11896
|
+
files: [],
|
|
11897
|
+
sourceName: "",
|
|
11898
|
+
status: { title: "Processing...", details: "Validating and preparing files." }
|
|
11899
|
+
});
|
|
11896
11900
|
try {
|
|
11897
11901
|
let detectedSourceName = "";
|
|
11898
11902
|
if (newFiles.length === 1 && isZipFile(newFiles[0])) {
|
|
@@ -11905,12 +11909,14 @@ function useDrop(options) {
|
|
|
11905
11909
|
detectedSourceName = newFiles[0].name;
|
|
11906
11910
|
}
|
|
11907
11911
|
}
|
|
11908
|
-
setSourceName(detectedSourceName);
|
|
11909
11912
|
const allFiles = [];
|
|
11910
11913
|
const shouldExtractZip = newFiles.length === 1 && isZipFile(newFiles[0]);
|
|
11911
11914
|
if (shouldExtractZip) {
|
|
11912
11915
|
const zipFile = newFiles[0];
|
|
11913
|
-
|
|
11916
|
+
setState((prev) => ({
|
|
11917
|
+
...prev,
|
|
11918
|
+
status: { title: "Extracting...", details: `Extracting ${zipFile.name}...` }
|
|
11919
|
+
}));
|
|
11914
11920
|
const { files: extractedFiles, errors } = await extractZipToFiles(zipFile);
|
|
11915
11921
|
if (errors.length > 0) {
|
|
11916
11922
|
console.warn("ZIP extraction errors:", errors);
|
|
@@ -11926,29 +11932,44 @@ function useDrop(options) {
|
|
|
11926
11932
|
const filePaths = allFiles.map(getFilePath);
|
|
11927
11933
|
const validPaths = new Set(filterJunk(filePaths));
|
|
11928
11934
|
const cleanFiles = allFiles.filter((f) => validPaths.has(getFilePath(f)));
|
|
11929
|
-
|
|
11935
|
+
setState((prev) => ({
|
|
11936
|
+
...prev,
|
|
11937
|
+
status: { title: "Processing...", details: "Processing files..." }
|
|
11938
|
+
}));
|
|
11930
11939
|
const processedFiles = await Promise.all(
|
|
11931
11940
|
cleanFiles.map((file) => createProcessedFile(file))
|
|
11932
11941
|
);
|
|
11933
11942
|
const finalFiles = stripPrefix ? stripCommonPrefix(processedFiles) : processedFiles;
|
|
11934
11943
|
const config = await ship.getConfig();
|
|
11935
11944
|
const validation = validateFiles(finalFiles, config);
|
|
11936
|
-
setFiles(validation.files);
|
|
11937
|
-
setValidationError(validation.error);
|
|
11938
11945
|
if (validation.error) {
|
|
11939
|
-
|
|
11946
|
+
setState({
|
|
11947
|
+
value: "error",
|
|
11948
|
+
files: validation.files,
|
|
11949
|
+
sourceName: detectedSourceName,
|
|
11950
|
+
status: { title: validation.error.error, details: validation.error.details }
|
|
11951
|
+
});
|
|
11940
11952
|
onValidationError?.(validation.error);
|
|
11941
11953
|
} else if (validation.validFiles.length > 0) {
|
|
11942
|
-
|
|
11954
|
+
setState({
|
|
11955
|
+
value: "ready",
|
|
11956
|
+
files: validation.files,
|
|
11957
|
+
sourceName: detectedSourceName,
|
|
11958
|
+
status: { title: "Ready", details: `${validation.validFiles.length} file(s) are ready.` }
|
|
11959
|
+
});
|
|
11943
11960
|
onFilesReady?.(validation.validFiles);
|
|
11944
11961
|
} else {
|
|
11945
11962
|
const noValidError = {
|
|
11946
11963
|
error: "No Valid Files",
|
|
11947
|
-
details: "
|
|
11964
|
+
details: "None of the provided files could be processed.",
|
|
11948
11965
|
isClientError: true
|
|
11949
11966
|
};
|
|
11950
|
-
|
|
11951
|
-
|
|
11967
|
+
setState({
|
|
11968
|
+
value: "error",
|
|
11969
|
+
files: validation.files,
|
|
11970
|
+
sourceName: detectedSourceName,
|
|
11971
|
+
status: { title: noValidError.error, details: noValidError.details }
|
|
11972
|
+
});
|
|
11952
11973
|
onValidationError?.(noValidError);
|
|
11953
11974
|
}
|
|
11954
11975
|
} catch (error) {
|
|
@@ -11957,44 +11978,52 @@ function useDrop(options) {
|
|
|
11957
11978
|
details: `Failed to process files: ${error instanceof Error ? error.message : String(error)}`,
|
|
11958
11979
|
isClientError: true
|
|
11959
11980
|
};
|
|
11960
|
-
|
|
11961
|
-
|
|
11981
|
+
setState((prev) => ({
|
|
11982
|
+
...prev,
|
|
11983
|
+
value: "error",
|
|
11984
|
+
status: { title: processingError.error, details: processingError.details }
|
|
11985
|
+
}));
|
|
11962
11986
|
onValidationError?.(processingError);
|
|
11963
11987
|
} finally {
|
|
11964
11988
|
isProcessingRef.current = false;
|
|
11965
|
-
setIsProcessing(false);
|
|
11966
11989
|
}
|
|
11967
11990
|
}, [ship, onValidationError, onFilesReady, stripPrefix]);
|
|
11968
11991
|
const clearAll = useCallback(() => {
|
|
11969
|
-
|
|
11970
|
-
setSourceName("");
|
|
11971
|
-
setStatusText("");
|
|
11972
|
-
setValidationError(null);
|
|
11973
|
-
setIsDragging(false);
|
|
11992
|
+
setState(initialState);
|
|
11974
11993
|
isProcessingRef.current = false;
|
|
11975
|
-
setIsProcessing(false);
|
|
11976
11994
|
}, []);
|
|
11977
11995
|
const getValidFilesCallback = useCallback(() => {
|
|
11978
|
-
return getValidFiles(files);
|
|
11979
|
-
}, [files]);
|
|
11980
|
-
const updateFileStatus = useCallback((fileId,
|
|
11981
|
-
|
|
11982
|
-
|
|
11983
|
-
|
|
11996
|
+
return getValidFiles(state.files);
|
|
11997
|
+
}, [state.files]);
|
|
11998
|
+
const updateFileStatus = useCallback((fileId, fileState) => {
|
|
11999
|
+
setState((prev) => ({
|
|
12000
|
+
...prev,
|
|
12001
|
+
files: prev.files.map(
|
|
12002
|
+
(file) => file.id === fileId ? { ...file, ...fileState } : file
|
|
12003
|
+
)
|
|
12004
|
+
}));
|
|
11984
12005
|
}, []);
|
|
11985
12006
|
const handleDragOver = useCallback((e) => {
|
|
11986
12007
|
e.preventDefault();
|
|
11987
|
-
|
|
12008
|
+
setState((prev) => {
|
|
12009
|
+
if (prev.value === "idle" || prev.value === "ready" || prev.value === "error") {
|
|
12010
|
+
return { ...prev, value: "dragging" };
|
|
12011
|
+
}
|
|
12012
|
+
return prev;
|
|
12013
|
+
});
|
|
11988
12014
|
}, []);
|
|
11989
12015
|
const handleDragLeave = useCallback((e) => {
|
|
11990
12016
|
e.preventDefault();
|
|
11991
|
-
|
|
12017
|
+
setState((prev) => {
|
|
12018
|
+
if (prev.value !== "dragging") return prev;
|
|
12019
|
+
const nextValue = prev.files.length > 0 ? prev.status?.title === "Ready" ? "ready" : "error" : "idle";
|
|
12020
|
+
return { ...prev, value: nextValue };
|
|
12021
|
+
});
|
|
11992
12022
|
}, []);
|
|
11993
12023
|
const handleDrop = useCallback(async (e) => {
|
|
11994
12024
|
e.preventDefault();
|
|
11995
|
-
setIsDragging(false);
|
|
11996
12025
|
const items = Array.from(e.dataTransfer.items);
|
|
11997
|
-
const
|
|
12026
|
+
const files = [];
|
|
11998
12027
|
let hasEntries = false;
|
|
11999
12028
|
for (const item of items) {
|
|
12000
12029
|
if (item.kind === "file") {
|
|
@@ -12003,23 +12032,25 @@ function useDrop(options) {
|
|
|
12003
12032
|
hasEntries = true;
|
|
12004
12033
|
await traverseFileTree(
|
|
12005
12034
|
entry,
|
|
12006
|
-
|
|
12035
|
+
files,
|
|
12007
12036
|
entry.isDirectory ? entry.name : ""
|
|
12008
12037
|
);
|
|
12009
12038
|
}
|
|
12010
12039
|
}
|
|
12011
12040
|
}
|
|
12012
12041
|
if (!hasEntries && e.dataTransfer.files.length > 0) {
|
|
12013
|
-
|
|
12042
|
+
files.push(...Array.from(e.dataTransfer.files));
|
|
12014
12043
|
}
|
|
12015
|
-
if (
|
|
12016
|
-
await processFiles(
|
|
12044
|
+
if (files.length > 0) {
|
|
12045
|
+
await processFiles(files);
|
|
12046
|
+
} else if (state.value === "dragging") {
|
|
12047
|
+
setState((prev) => ({ ...prev, value: "idle" }));
|
|
12017
12048
|
}
|
|
12018
|
-
}, [processFiles]);
|
|
12049
|
+
}, [processFiles, state.value]);
|
|
12019
12050
|
const handleInputChange = useCallback((e) => {
|
|
12020
|
-
const
|
|
12021
|
-
if (
|
|
12022
|
-
processFiles(
|
|
12051
|
+
const files = Array.from(e.target.files || []);
|
|
12052
|
+
if (files.length > 0) {
|
|
12053
|
+
processFiles(files);
|
|
12023
12054
|
}
|
|
12024
12055
|
}, [processFiles]);
|
|
12025
12056
|
const open = useCallback(() => {
|
|
@@ -12040,13 +12071,11 @@ function useDrop(options) {
|
|
|
12040
12071
|
onChange: handleInputChange
|
|
12041
12072
|
}), [handleInputChange]);
|
|
12042
12073
|
return {
|
|
12043
|
-
// State
|
|
12044
|
-
|
|
12045
|
-
|
|
12046
|
-
statusText,
|
|
12074
|
+
// State machine
|
|
12075
|
+
state,
|
|
12076
|
+
// Convenience getters (computed from state)
|
|
12047
12077
|
isProcessing,
|
|
12048
12078
|
isDragging,
|
|
12049
|
-
validationError,
|
|
12050
12079
|
// Primary API: Prop getters
|
|
12051
12080
|
getDropzoneProps,
|
|
12052
12081
|
getInputProps,
|