@shipstatic/drop 0.1.7 → 0.1.8
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 +31 -4
- package/dist/index.cjs +55 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +55 -38
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,10 +4,26 @@
|
|
|
4
4
|
|
|
5
5
|
A focused React hook for preparing files for deployment with [@shipstatic/ship](https://github.com/shipstatic/ship). Handles ZIP extraction, path normalization, and validation - everything needed before calling `ship.deploy()`.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Built-in drag & drop support with prop getters! No need to manually implement drag & drop handlers - just spread `{...drop.getDropzoneProps()}` on your container and `{...drop.getInputProps()}` on the input element. Folder structure preservation is handled automatically.
|
|
8
8
|
|
|
9
9
|
**Note:** MD5 calculation is handled by Ship SDK during deployment. Drop focuses on file processing and UI state management.
|
|
10
10
|
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Why Headless?](#why-headless)
|
|
14
|
+
- [Features](#features)
|
|
15
|
+
- [Installation](#installation)
|
|
16
|
+
- [Requirements](#requirements)
|
|
17
|
+
- [Quick Start](#quick-start)
|
|
18
|
+
- [Configuration Architecture](#️-configuration-architecture)
|
|
19
|
+
- [Advanced Usage](#advanced-programmatic-file-picker)
|
|
20
|
+
- [API Reference](#api)
|
|
21
|
+
- [State Machine](#state-machine)
|
|
22
|
+
- [Error Handling](#error-handling)
|
|
23
|
+
- [Types](#types)
|
|
24
|
+
- [Ship SDK Integration](#direct-ship-sdk-integration)
|
|
25
|
+
- [Architecture Decisions](#architecture-decisions)
|
|
26
|
+
|
|
11
27
|
## Why Headless?
|
|
12
28
|
|
|
13
29
|
This package provides **zero UI components** - just a React hook with built-in drag & drop functionality. You bring your own styling.
|
|
@@ -16,8 +32,7 @@ This package provides **zero UI components** - just a React hook with built-in d
|
|
|
16
32
|
1. **Built-in drag & drop** - Proper folder support with `webkitGetAsEntry` API, all handled internally
|
|
17
33
|
2. **Prop getters API** - Similar to `react-dropzone`, just spread props on your elements
|
|
18
34
|
3. **Full styling control** - No imposed CSS, design system, or theming
|
|
19
|
-
4. **
|
|
20
|
-
5. **Ship SDK integration** - Purpose-built for Ship deployments, not a generic file upload library
|
|
35
|
+
4. **Ship SDK integration** - Purpose-built for Ship deployments, not a generic file upload library
|
|
21
36
|
|
|
22
37
|
**What's different from other libraries:**
|
|
23
38
|
- Generic dropzone libraries don't preserve folder structure properly
|
|
@@ -34,6 +49,7 @@ This package provides **zero UI components** - just a React hook with built-in d
|
|
|
34
49
|
- 🔒 **Path Sanitization** - Defense-in-depth protection against directory traversal attacks
|
|
35
50
|
- 📁 **Folder Structure Preservation** - Proper folder paths via `webkitRelativePath`
|
|
36
51
|
- 🎨 **Headless UI** - No visual components, just logic and state management
|
|
52
|
+
- 📘 **Full TypeScript Support** - Complete type definitions with discriminated unions for state machine
|
|
37
53
|
- 🚀 **Focused Scope** - File processing and UI state only. MD5 calculation and deployment handled by Ship SDK
|
|
38
54
|
|
|
39
55
|
## Installation
|
|
@@ -44,6 +60,17 @@ npm install @shipstatic/drop
|
|
|
44
60
|
pnpm add @shipstatic/drop
|
|
45
61
|
```
|
|
46
62
|
|
|
63
|
+
## Requirements
|
|
64
|
+
|
|
65
|
+
- **React**: ^18.0.0 or ^19.0.0
|
|
66
|
+
- **TypeScript**: Full TypeScript support with exported types
|
|
67
|
+
- **Browsers**: Modern browsers with support for:
|
|
68
|
+
- File API (universal support)
|
|
69
|
+
- DataTransfer API for drag & drop (universal support)
|
|
70
|
+
- `webkitGetAsEntry` for folder uploads (Chrome, Edge, Safari 11.1+, Firefox 50+)
|
|
71
|
+
|
|
72
|
+
**Note on folder uploads**: The folder drag & drop feature uses the `webkitGetAsEntry` API. While widely supported, older browsers may only support file-by-file selection. ZIP extraction works universally as a fallback.
|
|
73
|
+
|
|
47
74
|
## Quick Start
|
|
48
75
|
|
|
49
76
|
```tsx
|
|
@@ -231,7 +258,7 @@ interface DropReturn {
|
|
|
231
258
|
type: 'file';
|
|
232
259
|
style: { display: string };
|
|
233
260
|
multiple: boolean;
|
|
234
|
-
webkitdirectory: string;
|
|
261
|
+
webkitdirectory: string; // Note: React expects string ('') for boolean attributes
|
|
235
262
|
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
236
263
|
};
|
|
237
264
|
|
package/dist/index.cjs
CHANGED
|
@@ -37,9 +37,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
37
37
|
|
|
38
38
|
// node_modules/.pnpm/jszip@3.10.1/node_modules/jszip/dist/jszip.min.js
|
|
39
39
|
var require_jszip_min = __commonJS({
|
|
40
|
-
"node_modules/.pnpm/jszip@3.10.1/node_modules/jszip/dist/jszip.min.js"(exports, module) {
|
|
40
|
+
"node_modules/.pnpm/jszip@3.10.1/node_modules/jszip/dist/jszip.min.js"(exports$1, module) {
|
|
41
41
|
!(function(e) {
|
|
42
|
-
if ("object" == typeof exports && "undefined" != typeof module) module.exports = e();
|
|
42
|
+
if ("object" == typeof exports$1 && "undefined" != typeof module) module.exports = e();
|
|
43
43
|
else if ("function" == typeof define && define.amd) define([], e);
|
|
44
44
|
else {
|
|
45
45
|
("undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : this).JSZip = e();
|
|
@@ -2349,7 +2349,7 @@ var require_jszip_min = __commonJS({
|
|
|
2349
2349
|
|
|
2350
2350
|
// node_modules/.pnpm/mime-db@1.54.0/node_modules/mime-db/db.json
|
|
2351
2351
|
var require_db = __commonJS({
|
|
2352
|
-
"node_modules/.pnpm/mime-db@1.54.0/node_modules/mime-db/db.json"(exports, module) {
|
|
2352
|
+
"node_modules/.pnpm/mime-db@1.54.0/node_modules/mime-db/db.json"(exports$1, module) {
|
|
2353
2353
|
module.exports = {
|
|
2354
2354
|
"application/1d-interleaved-parityfec": {
|
|
2355
2355
|
source: "iana"
|
|
@@ -11697,7 +11697,7 @@ var require_db = __commonJS({
|
|
|
11697
11697
|
|
|
11698
11698
|
// node_modules/.pnpm/mime-db@1.54.0/node_modules/mime-db/index.js
|
|
11699
11699
|
var require_mime_db = __commonJS({
|
|
11700
|
-
"node_modules/.pnpm/mime-db@1.54.0/node_modules/mime-db/index.js"(exports, module) {
|
|
11700
|
+
"node_modules/.pnpm/mime-db@1.54.0/node_modules/mime-db/index.js"(exports$1, module) {
|
|
11701
11701
|
module.exports = require_db();
|
|
11702
11702
|
}
|
|
11703
11703
|
});
|
|
@@ -11835,35 +11835,39 @@ function stripCommonPrefix(files) {
|
|
|
11835
11835
|
}));
|
|
11836
11836
|
}
|
|
11837
11837
|
async function traverseFileTree(entry, files, currentPath = "") {
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
|
|
11845
|
-
|
|
11846
|
-
|
|
11847
|
-
|
|
11848
|
-
|
|
11849
|
-
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
const
|
|
11853
|
-
|
|
11854
|
-
|
|
11838
|
+
try {
|
|
11839
|
+
if (entry.isFile) {
|
|
11840
|
+
const file = await new Promise((resolve, reject) => {
|
|
11841
|
+
entry.file(resolve, reject);
|
|
11842
|
+
});
|
|
11843
|
+
const relativePath = currentPath ? `${currentPath}/${file.name}` : file.name;
|
|
11844
|
+
Object.defineProperty(file, "webkitRelativePath", {
|
|
11845
|
+
value: relativePath,
|
|
11846
|
+
writable: false
|
|
11847
|
+
});
|
|
11848
|
+
files.push(file);
|
|
11849
|
+
} else if (entry.isDirectory) {
|
|
11850
|
+
const dirReader = entry.createReader();
|
|
11851
|
+
let allEntries = [];
|
|
11852
|
+
const readEntriesBatch = async () => {
|
|
11853
|
+
const batch = await new Promise(
|
|
11854
|
+
(resolve, reject) => {
|
|
11855
|
+
dirReader.readEntries(resolve, reject);
|
|
11856
|
+
}
|
|
11857
|
+
);
|
|
11858
|
+
if (batch.length > 0) {
|
|
11859
|
+
allEntries = allEntries.concat(batch);
|
|
11860
|
+
await readEntriesBatch();
|
|
11855
11861
|
}
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11862
|
+
};
|
|
11863
|
+
await readEntriesBatch();
|
|
11864
|
+
for (const childEntry of allEntries) {
|
|
11865
|
+
const entryPath = childEntry.isDirectory ? currentPath ? `${currentPath}/${childEntry.name}` : childEntry.name : currentPath;
|
|
11866
|
+
await traverseFileTree(childEntry, files, entryPath);
|
|
11860
11867
|
}
|
|
11861
|
-
};
|
|
11862
|
-
await readEntriesBatch();
|
|
11863
|
-
for (const childEntry of allEntries) {
|
|
11864
|
-
const entryPath = childEntry.isDirectory ? currentPath ? `${currentPath}/${childEntry.name}` : childEntry.name : currentPath;
|
|
11865
|
-
await traverseFileTree(childEntry, files, entryPath);
|
|
11866
11868
|
}
|
|
11869
|
+
} catch (error) {
|
|
11870
|
+
console.warn(`Error traversing file tree for entry ${entry.name}:`, error);
|
|
11867
11871
|
}
|
|
11868
11872
|
}
|
|
11869
11873
|
function useDrop(options) {
|
|
@@ -12029,14 +12033,27 @@ function useDrop(options) {
|
|
|
12029
12033
|
let hasEntries = false;
|
|
12030
12034
|
for (const item of items) {
|
|
12031
12035
|
if (item.kind === "file") {
|
|
12032
|
-
|
|
12033
|
-
|
|
12034
|
-
|
|
12035
|
-
|
|
12036
|
-
|
|
12037
|
-
|
|
12038
|
-
|
|
12039
|
-
|
|
12036
|
+
try {
|
|
12037
|
+
const entry = item.webkitGetAsEntry?.();
|
|
12038
|
+
if (entry) {
|
|
12039
|
+
hasEntries = true;
|
|
12040
|
+
await traverseFileTree(
|
|
12041
|
+
entry,
|
|
12042
|
+
files,
|
|
12043
|
+
entry.isDirectory ? entry.name : ""
|
|
12044
|
+
);
|
|
12045
|
+
} else {
|
|
12046
|
+
const file = item.getAsFile();
|
|
12047
|
+
if (file) {
|
|
12048
|
+
files.push(file);
|
|
12049
|
+
}
|
|
12050
|
+
}
|
|
12051
|
+
} catch (error) {
|
|
12052
|
+
console.warn("Error processing drop item:", error);
|
|
12053
|
+
const file = item.getAsFile();
|
|
12054
|
+
if (file) {
|
|
12055
|
+
files.push(file);
|
|
12056
|
+
}
|
|
12040
12057
|
}
|
|
12041
12058
|
}
|
|
12042
12059
|
}
|