@shipstatic/drop 0.1.19 → 0.2.0

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 CHANGED
@@ -22,7 +22,8 @@ function Uploader() {
22
22
  const drop = useDrop({ ship });
23
23
 
24
24
  const handleUpload = async () => {
25
- await ship.deployments.create(drop.validFiles.map(f => f.file));
25
+ const files = drop.getFilesForUpload();
26
+ await ship.deployments.create(files);
26
27
  };
27
28
 
28
29
  return (
@@ -42,7 +43,7 @@ function Uploader() {
42
43
 
43
44
  {drop.status && <p>{drop.status.title}: {drop.status.details}</p>}
44
45
 
45
- <button onClick={handleUpload} disabled={drop.phase !== 'ready'}>
46
+ <button onClick={handleUpload} disabled={!drop.validFiles.length}>
46
47
  Upload {drop.validFiles.length} files
47
48
  </button>
48
49
  </div>
@@ -65,6 +66,16 @@ function Uploader() {
65
66
  idle → dragging → processing → ready/error
66
67
  ```
67
68
 
69
+ Use semantic booleans for clean rendering:
70
+
71
+ ```tsx
72
+ {drop.isProcessing && <Spinner />}
73
+ {drop.hasError && <Error message={drop.status?.details} onRetry={drop.reset} />}
74
+ {drop.isInteractive && <DropZone />}
75
+ ```
76
+
77
+ Or use `phase` for switch-case logic:
78
+
68
79
  ```tsx
69
80
  switch (drop.phase) {
70
81
  case 'idle': return 'Drop files here';
@@ -96,22 +107,39 @@ interface DropReturn {
96
107
  phase: 'idle' | 'dragging' | 'processing' | 'ready' | 'error';
97
108
  isProcessing: boolean;
98
109
  isDragging: boolean;
110
+ isInteractive: boolean; // true when idle, dragging, or ready
111
+ hasError: boolean; // true when in error state
99
112
  files: ProcessedFile[];
100
113
  validFiles: ProcessedFile[];
101
- status: { title: string; details: string } | null;
114
+ sourceName: string;
115
+ status: { title: string; details: string; errors?: string[] } | null;
102
116
 
103
117
  // Prop getters
104
- getDropzoneProps: () => { onDragOver, onDragLeave, onDrop, onClick };
105
- getInputProps: () => { ref, type, style, multiple, webkitdirectory, onChange };
118
+ getDropzoneProps: (options?: { clickable?: boolean }) => {...};
119
+ getInputProps: () => {...};
106
120
 
107
121
  // Actions
108
- open: () => void; // Trigger file picker
122
+ open: () => void; // Trigger file picker
109
123
  processFiles: (files: File[]) => Promise<void>;
110
- clearAll: () => void;
111
- updateFileStatus: (fileId: string, state: {...}) => void;
124
+ reset: () => void; // Clear all files and reset state
125
+
126
+ // Helpers
127
+ getFilesForUpload: () => File[]; // Get raw File objects for SDK
112
128
  }
113
129
  ```
114
130
 
131
+ ### Prop Getter Options
132
+
133
+ ```tsx
134
+ // Default: clickable dropzone (click opens file picker)
135
+ <div {...drop.getDropzoneProps()}>
136
+
137
+ // Drag-only dropzone (no click behavior)
138
+ <div {...drop.getDropzoneProps({ clickable: false })}>
139
+ <button onClick={drop.open}>Select folder</button>
140
+ </div>
141
+ ```
142
+
115
143
  ## Ship SDK Integration
116
144
 
117
145
  Drop uses Ship SDK's validation automatically:
@@ -126,8 +154,8 @@ const drop = useDrop({ ship });
126
154
  Pass files to Ship SDK:
127
155
 
128
156
  ```tsx
129
- const filesToDeploy = drop.validFiles.map(f => f.file);
130
- await ship.deployments.create(filesToDeploy);
157
+ const files = drop.getFilesForUpload();
158
+ await ship.deployments.create(files);
131
159
  ```
132
160
 
133
161
  ## Requirements
package/dist/index.cjs CHANGED
@@ -11702,7 +11702,7 @@ var require_mime_db = __commonJS({
11702
11702
  }
11703
11703
  });
11704
11704
 
11705
- // node_modules/.pnpm/@shipstatic+types@0.4.5/node_modules/@shipstatic/types/dist/index.js
11705
+ // node_modules/.pnpm/@shipstatic+types@0.4.7/node_modules/@shipstatic/types/dist/index.js
11706
11706
  var ErrorType;
11707
11707
  (function(ErrorType2) {
11708
11708
  ErrorType2["Validation"] = "validation_failed";
@@ -11916,7 +11916,15 @@ function useDrop(options) {
11916
11916
  const inputRef = react.useRef(null);
11917
11917
  const isProcessing = react.useMemo(() => state.value === "processing", [state.value]);
11918
11918
  const isDragging = react.useMemo(() => state.value === "dragging", [state.value]);
11919
+ const isInteractive = react.useMemo(
11920
+ () => state.value === "idle" || state.value === "dragging" || state.value === "ready",
11921
+ [state.value]
11922
+ );
11923
+ const hasError = react.useMemo(() => state.value === "error", [state.value]);
11919
11924
  const validFiles = react.useMemo(() => ship.getValidFiles(state.files), [state.files]);
11925
+ const getFilesForUpload = react.useCallback(() => {
11926
+ return validFiles.map((f) => f.file);
11927
+ }, [validFiles]);
11920
11928
  const processFiles = react.useCallback(async (newFiles) => {
11921
11929
  if (isProcessingRef.current) {
11922
11930
  console.warn("File processing already in progress. Ignoring duplicate call.");
@@ -12027,18 +12035,10 @@ function useDrop(options) {
12027
12035
  isProcessingRef.current = false;
12028
12036
  }
12029
12037
  }, [ship$1, onValidationError, onFilesReady, stripPrefix]);
12030
- const clearAll = react.useCallback(() => {
12038
+ const reset = react.useCallback(() => {
12031
12039
  setState(initialState);
12032
12040
  isProcessingRef.current = false;
12033
12041
  }, []);
12034
- const updateFileStatus = react.useCallback((fileId, fileState) => {
12035
- setState((prev) => ({
12036
- ...prev,
12037
- files: prev.files.map(
12038
- (file) => file.id === fileId ? { ...file, ...fileState } : file
12039
- )
12040
- }));
12041
- }, []);
12042
12042
  const handleDragOver = react.useCallback((e) => {
12043
12043
  e.preventDefault();
12044
12044
  setState((prev) => {
@@ -12121,12 +12121,15 @@ function useDrop(options) {
12121
12121
  const open = react.useCallback(() => {
12122
12122
  inputRef.current?.click();
12123
12123
  }, []);
12124
- const getDropzoneProps = react.useCallback(() => ({
12125
- onDragOver: handleDragOver,
12126
- onDragLeave: handleDragLeave,
12127
- onDrop: handleDrop,
12128
- onClick: open
12129
- }), [handleDragOver, handleDragLeave, handleDrop, open]);
12124
+ const getDropzoneProps = react.useCallback((options2) => {
12125
+ const { clickable = true } = options2 ?? {};
12126
+ return {
12127
+ onDragOver: handleDragOver,
12128
+ onDragLeave: handleDragLeave,
12129
+ onDrop: handleDrop,
12130
+ ...clickable && { onClick: open }
12131
+ };
12132
+ }, [handleDragOver, handleDragLeave, handleDrop, open]);
12130
12133
  const getInputProps = react.useCallback(() => ({
12131
12134
  ref: inputRef,
12132
12135
  type: "file",
@@ -12136,11 +12139,12 @@ function useDrop(options) {
12136
12139
  onChange: handleInputChange
12137
12140
  }), [handleInputChange]);
12138
12141
  return {
12139
- // State machine
12140
12142
  // Convenience getters (computed from state)
12141
12143
  phase: state.value,
12142
12144
  isProcessing,
12143
12145
  isDragging,
12146
+ isInteractive,
12147
+ hasError,
12144
12148
  files: state.files,
12145
12149
  sourceName: state.sourceName,
12146
12150
  status: state.status,
@@ -12150,10 +12154,10 @@ function useDrop(options) {
12150
12154
  // Actions
12151
12155
  open,
12152
12156
  processFiles,
12153
- clearAll,
12157
+ reset,
12154
12158
  // Helpers
12155
12159
  validFiles,
12156
- updateFileStatus
12160
+ getFilesForUpload
12157
12161
  };
12158
12162
  }
12159
12163
  /*! Bundled license information: