import { Ship } from '@shipstatic/ship';
import { FileValidationStatusType } from '@shipstatic/types';
/**
* Core types for @shipstatic/drop.
*
* `@shipstatic/types` owns the file-validation vocabulary and drop uses it by its
* real name — `FileValidationStatus`, reachable from `@shipstatic/ship`, which
* every consumer already depends on. Re-badging it as a drop-local
* `FILE_STATUSES` would put a second name on one object and leave a consumer of
* both packages wondering whether they differ.
*
* Drop adds no statuses of its own, so `ProcessedFile` stays expressible as a
* `ValidatableFile` and the packages can never disagree about what "ready" means.
*/
/**
* A file prepared for deployment.
*
* `path` is the deploy identity; `name` is the basename, for display. Ship
* computes each file's MD5 for its manifest, so no `md5` lives here.
*/
interface ProcessedFile {
/** Unique identifier for React keys and tracking */
id: string;
/** The File object — pass this to ship.deployments.upload() */
file: File;
/** Relative path for deployment (e.g. "images/photo.jpg") */
path: string;
/** File size in bytes */
size: number;
/** Filename without path — for display; `path` is the deploy identity */
name: string;
/** MIME type as reported by the browser, for UI icons/previews */
type: string;
/** Last modified timestamp */
lastModified: number;
/** Current processing status */
status: FileValidationStatusType;
/** Human-readable status message for UI */
statusMessage?: string;
}
/**
* Phase of the drop lifecycle.
*
* Dragging is not a phase — it is a pointer state that can occur over any of
* these, carried separately as `isDragging`.
*/
type DropPhase = 'idle' | 'processing' | 'ready' | 'error';
/** What to show the user about the current phase. */
interface DropStatus {
title: string;
details: string;
/** Per-item breakdown, for multi-error cases */
errors?: string[];
/** Non-blocking issues (e.g. excluded empty files) */
warnings?: string[];
}
/**
* Headless drop hook for file upload workflows.
*
* This file owns React state, DOM events, and prop getters — nothing else. The
* rules live in `./process` as a plain function.
*
* ```tsx
* const drop = useDrop({ ship });
*
*
* ```
*/
interface DropOptions {
/**
* The Ship client, for platform limits.
*
* Typed as only what drop calls, mirroring the SDK's own resource-factory
* doctrine — a real `Ship` satisfies it, and nothing else has to be faked.
*/
ship: Pick;
}
/** Options for `getDropzoneProps()` */
interface DropzonePropsOptions {
/** Whether clicking the dropzone opens the file picker (default: true) */
clickable?: boolean;
}
/**
* Which picker `getInputProps()` describes and `open()` opens.
*
* **Folder is the default**, so a bare `getInputProps()` / `open()` — and the
* dropzone's own click — address the folder picker exactly as they always have.
*
* The singular/plural mix is deliberate: you pick one folder or many files, and
* these are the words the buttons above them carry.
*/
type PickerMode = 'folder' | 'files';
/**
* The hidden input to spread onto an ``.
*
* One shape for both modes, because exactly one attribute distinguishes them:
* `webkitdirectory` makes the dialog a folder picker, and `accept` biases a file
* picker's default view. Never both — a folder picker ignores `accept`, so
* emitting it there would advertise a filter that does not apply.
*/
interface DropInputProps {
ref: React.RefObject;
type: 'file';
style: {
display: string;
};
multiple: boolean;
/** Folder mode only — the attribute that makes it a folder picker. */
webkitdirectory?: string;
/**
* Files mode only. A HINT, not a gate: every dialog offers an all-files
* escape and drag-and-drop ignores `accept` outright, so the verdict on any
* file is `validateFiles` downstream — the same one the dropzone reaches.
*/
accept?: string;
onChange: (e: React.ChangeEvent) => void;
}
interface DropReturn {
/** Current phase of the lifecycle */
phase: DropPhase;
/** Whether files are being processed (extraction, validation) */
isProcessing: boolean;
/** Whether the user is currently dragging over the dropzone */
isDragging: boolean;
/** Whether the dropzone is idle or holding a ready set */
isInteractive: boolean;
/** Whether an error occurred during processing */
hasError: boolean;
/** All processed files */
files: ProcessedFile[];
/** Friendly name of what was dropped (ZIP name, folder name, or filename) */
sourceName: string;
/** Current status for display */
status: DropStatus | null;
/** Whether the dropped files need server-side building before deployment */
needsBuild: boolean;
/** Props to spread on the dropzone element (drag & drop, optionally click) */
getDropzoneProps: (options?: DropzonePropsOptions) => {
onDragOver: (e: React.DragEvent) => void;
onDragLeave: (e: React.DragEvent) => void;
onDrop: (e: React.DragEvent) => void;
onClick?: () => void;
};
/**
* Props to spread on a hidden file input element, one per picker mode.
*
* Each mode owns its own element and its own ref, so a UI offering both
* renders both inputs — `open(mode)` clicks whichever is mounted.
*/
getInputProps: (mode?: PickerMode) => DropInputProps;
/** Programmatically trigger a picker (default: the folder picker) */
open: (mode?: PickerMode) => void;
/** Process files directly (advanced — loses folder traversal) */
processFiles: (files: File[]) => Promise;
/** Reset state and clear all files */
reset: () => void;
/** Only the files that passed validation */
validFiles: ProcessedFile[];
/** Raw File objects ready for Ship SDK upload */
getFilesForUpload: () => File[];
}
declare function useDrop({ ship }: DropOptions): DropReturn;
export { type DropStatus as D, type ProcessedFile as P, type DropInputProps as a, type DropOptions as b, type DropPhase as c, type DropReturn as d, type DropzonePropsOptions as e, type PickerMode as f, useDrop as u };