@zag-js/file-upload
Version:
Core logic for the file-upload widget implemented as a state machine
282 lines (278 loc) • 8.25 kB
text/typescript
import { Service, EventObject, Machine } from '@zag-js/core';
import { FileMimeType, FileError } from '@zag-js/file-utils';
export { FileError, FileMimeType } from '@zag-js/file-utils';
import { RequiredBy, LocaleProperties, CommonProperties, PropTypes } from '@zag-js/types';
interface FileRejection {
file: File;
errors: FileError[];
}
interface FileChangeDetails {
acceptedFiles: File[];
rejectedFiles: FileRejection[];
}
interface FileValidateDetails {
acceptedFiles: File[];
rejectedFiles: FileRejection[];
}
interface FileAcceptDetails {
files: File[];
}
interface FileRejectDetails {
files: FileRejection[];
}
type ElementIds = Partial<{
root: string;
dropzone: string;
hiddenInput: string;
trigger: string;
label: string;
item: (id: string) => string;
itemName: (id: string) => string;
itemSizeText: (id: string) => string;
itemPreview: (id: string) => string;
itemDeleteTrigger: (id: string) => string;
}>;
interface IntlTranslations {
dropzone?: string | undefined;
itemPreview?: ((file: File) => string) | undefined;
deleteFile?: ((file: File) => string) | undefined;
}
interface FileUploadProps extends LocaleProperties, CommonProperties {
/**
* The name of the underlying file input
*/
name?: string | undefined;
/**
* The ids of the elements. Useful for composition.
*/
ids?: ElementIds | undefined;
/**
* The localized messages to use.
*/
translations?: IntlTranslations | undefined;
/**
* The accept file types
*/
accept?: Record<string, string[]> | FileMimeType | FileMimeType[] | undefined;
/**
* Whether the file input is disabled
*/
disabled?: boolean | undefined;
/**
* Whether the file input is required
*/
required?: boolean | undefined;
/**
* Whether to allow drag and drop in the dropzone element
* @default true
*/
allowDrop?: boolean | undefined;
/**
* The maximum file size in bytes
*
* @default Infinity
*/
maxFileSize?: number | undefined;
/**
* The minimum file size in bytes
*
* @default 0
*/
minFileSize?: number | undefined;
/**
* The maximum number of files
* @default 1
*/
maxFiles?: number | undefined;
/**
* Whether to prevent the drop event on the document
* @default true
*/
preventDocumentDrop?: boolean | undefined;
/**
* Function to validate a file
*/
validate?: ((file: File, details: FileValidateDetails) => FileError[] | null) | undefined;
/**
* The default accepted files when rendered.
* Use when you don't need to control the accepted files of the input.
*/
defaultAcceptedFiles?: File[] | undefined;
/**
* The controlled accepted files
*/
acceptedFiles?: File[] | undefined;
/**
* Function called when the value changes, whether accepted or rejected
*/
onFileChange?: ((details: FileChangeDetails) => void) | undefined;
/**
* Function called when the file is accepted
*/
onFileAccept?: ((details: FileAcceptDetails) => void) | undefined;
/**
* Function called when the file is rejected
*/
onFileReject?: ((details: FileRejectDetails) => void) | undefined;
/**
* The default camera to use when capturing media
*/
capture?: "user" | "environment" | undefined;
/**
* Whether to accept directories, only works in webkit browsers
*/
directory?: boolean | undefined;
/**
* Whether the file input is invalid
*/
invalid?: boolean | undefined;
/**
* Whether the file input is read-only
*/
readOnly?: boolean | undefined;
/**
* Function to transform the accepted files to apply transformations
*/
transformFiles?: ((files: File[]) => Promise<File[]>) | undefined;
}
type PropsWithDefault = "minFileSize" | "maxFileSize" | "maxFiles" | "preventDocumentDrop" | "allowDrop" | "translations";
interface Context {
/**
* The rejected files
*/
rejectedFiles: FileRejection[];
/**
* The current value of the file input
*/
acceptedFiles: File[];
/**
* Whether files are currently being transformed
*/
transforming: boolean;
}
type Computed = {
/**
* The accept attribute as a string
*/
acceptAttr: string | undefined;
/**
* Whether the file can select multiple files
*/
multiple: boolean;
};
interface FileUploadSchema {
state: "idle" | "focused" | "dragging";
props: RequiredBy<FileUploadProps, PropsWithDefault>;
context: Context;
computed: Computed;
event: EventObject;
action: string;
effect: string;
guard: string;
}
type FileUploadService = Service<FileUploadSchema>;
type FileUploadMachine = Machine<FileUploadSchema>;
type ItemType = "accepted" | "rejected";
interface ItemTypeProps {
type?: ItemType | undefined;
}
interface ItemProps extends ItemTypeProps {
file: File;
}
interface ItemPreviewImageProps extends ItemProps {
url: string;
}
interface ItemGroupProps extends ItemTypeProps {
}
interface DropzoneProps {
/**
* Whether to disable the click event on the dropzone
*/
disableClick?: boolean | undefined;
}
interface FileUploadApi<T extends PropTypes = PropTypes> {
/**
* Whether the user is dragging something over the root element
*/
dragging: boolean;
/**
* Whether the user is focused on the dropzone element
*/
focused: boolean;
/**
* Whether the file input is disabled
*/
disabled: boolean;
/**
* Whether the file input is in read-only mode
*/
readOnly: boolean;
/**
* Whether files are currently being transformed via `transformFiles`
*/
transforming: boolean;
/**
* Whether the maximum number of files has been reached
*/
maxFilesReached: boolean;
/**
* The number of files that can still be added
*/
remainingFiles: number;
/**
* Function to open the file dialog
*/
openFilePicker: VoidFunction;
/**
* Function to delete the file from the list
*/
deleteFile: (file: File, type?: ItemType | undefined) => void;
/**
* The accepted files that have been dropped or selected
*/
acceptedFiles: File[];
/**
* The files that have been rejected
*/
rejectedFiles: FileRejection[];
/**
* Sets the accepted files
*/
setFiles: (files: File[]) => void;
/**
* Clears the accepted files
*/
clearFiles: VoidFunction;
/**
* Clears the rejected files
*/
clearRejectedFiles: VoidFunction;
/**
* Returns the formatted file size (e.g. 1.2MB)
*/
getFileSize: (file: File) => string;
/**
* Returns the preview url of a file.
* Returns a function to revoke the url.
*/
createFileUrl: (file: File, cb: (url: string) => void) => VoidFunction;
/**
* Sets the clipboard files
* Returns `true` if the clipboard data contains files, `false` otherwise.
*/
setClipboardFiles: (dt: DataTransfer | null) => boolean;
getLabelProps: () => T["label"];
getRootProps: () => T["element"];
getDropzoneProps: (props?: DropzoneProps) => T["element"];
getTriggerProps: () => T["button"];
getHiddenInputProps: () => T["input"];
getItemGroupProps: (props?: ItemGroupProps) => T["element"];
getItemProps: (props: ItemProps) => T["element"];
getItemNameProps: (props: ItemProps) => T["element"];
getItemPreviewProps: (props: ItemProps) => T["element"];
getItemPreviewImageProps: (props: ItemPreviewImageProps) => T["img"];
getItemSizeTextProps: (props: ItemProps) => T["element"];
getItemDeleteTriggerProps: (props: ItemProps) => T["button"];
getClearTriggerProps: () => T["button"];
}
export type { DropzoneProps, ElementIds, FileAcceptDetails, FileChangeDetails, FileRejectDetails, FileRejection, FileUploadApi, FileUploadMachine, FileUploadProps, FileUploadSchema, FileUploadService, FileValidateDetails, IntlTranslations, ItemGroupProps, ItemPreviewImageProps, ItemProps, ItemType, ItemTypeProps };