UNPKG

@react-md/form

Version:

This package is for creating all the different form input types.

456 lines (455 loc) 16.2 kB
import type { ChangeEventHandler, DragEventHandler } from "react"; /** @remarks \@since 2.9.0 */ export interface BaseFileUploadStats { /** * A unique key associated with each upload generated by `nanoid`. */ key: string; /** * The file instance that is being uploaded. */ file: File; /** * The current upload progress as a percentage from 0 - 100 percent. */ progress: number; } /** @remarks \@since 2.9.0 */ export interface ProcessingFileUploadStats extends BaseFileUploadStats { status: "pending" | "uploading"; } /** @remarks \@since 2.9.0 */ export declare type FileReaderResult = FileReader["result"]; /** @remarks \@since 2.9.0 */ export interface CompletedFileUploadStats extends BaseFileUploadStats { status: "complete"; /** * The result after a `FileReader` has read a file completely. * * Note: This _should_ be an `ArrayBuffer` if the next step is to upload to a * server. * * @see {@link FileReaderParser} * @see {@link getFileParser} * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/FileReader | FileReader} */ result: FileReaderResult; } /** @remarks \@since 2.9.0 */ export declare type FileUploadStats = ProcessingFileUploadStats | CompletedFileUploadStats; /** @remarks \@since 2.9.0 */ export declare type FileUploadStatus = FileUploadStats["status"]; /** @remarks \@since 2.9.0 */ export interface FileUploadHandlers<E extends HTMLElement> { onDrop?: DragEventHandler<E>; onChange?: ChangeEventHandler<HTMLInputElement>; } /** * An error that will be created if a user tries dragging and dropping files * from a shared directory that they do not have access to. This error will not * occur much. * * @remarks \@since 2.9.0 */ export declare class FileAccessError extends Error { /** * A unique key generated by `nanoid` that can be used as a `React` key */ key: string; /** * * @param message - An optional message for the error. */ constructor(message?: string); } /** * An error that just requires a `File` to be passed as the first argument. * * @remarks \@since 2.9.0 */ export declare class GenericFileError extends Error { files: readonly File[]; reason?: string | undefined; /** * A unique key generated by `nanoid` that can be used as a `React` key */ key: string; /** * * @param files - A list of files that caused the error. * @param reason - An optional reason for the error */ constructor(files: readonly File[], reason?: string | undefined); } /** * An error that is created during the upload process if the number of files * exceeds the {@link FileUploadOptions.maxFiles} amount. * * @remarks \@since 2.9.0 */ export declare class TooManyFilesError extends GenericFileError { limit: number; /** * * @param files - The list of files that could not be uploaded due to the file * limit defined. * @param limit - The max limit of files allowed. */ constructor(files: readonly File[], limit: number); } /** * An error that will be created if a user tries to upload a file that * is either: * - less than the {@link FileValidationOptions.minFileSize} * - greater than the {@link FileValidationOptions.maxFileSize} * - including the file would be greater than the {@link FileValidationOptions.totalFileSize} * * @remarks \@since 2.9.0 */ export declare class FileSizeError extends GenericFileError { type: "min" | "max" | "total"; limit: number; /** * * @param files - The list of files that have the file size error * @param type - The file size error type * @param limit - The number of bytes allowed based on the type */ constructor(files: readonly File[], type: "min" | "max" | "total", limit: number); } /** * An error that will be created if a user tries to upload a file that does not * end with one of the {@link FileValidationOptions.extensions}. * * @remarks \@since 2.9.0 */ export declare class FileExtensionError extends GenericFileError { extensions: readonly string[]; /** * * @param files - The file that caused the error * @param extensions - The allowed list of file extensions */ constructor(files: readonly File[], extensions: readonly string[]); } /** * Mostly an internal type that is used to allow custom validation errors * * @remarks \@since 2.9.0 */ export declare type FileValidationError<E = GenericFileError> = FileAccessError | TooManyFilesError | FileSizeError | FileExtensionError | E; /** * A simple type-guard that can be used to check if the * {@link FileValidationError} is the {@link GenericFileError} which can be * useful when displaying the errors to the user. * * @param error - The error to check * @returns true if the error is a {@link FileAccessError} */ export declare function isGenericFileError<CustomError>(error: FileValidationError<CustomError>): error is GenericFileError; /** * A simple type-guard that can be used to check if the * {@link FileValidationError} is the {@link FileAccessError} which can be * useful when displaying the errors to the user. * * @param error - The error to check * @returns true if the error is a {@link FileAccessError} */ export declare function isFileAccessError<CustomError>(error: FileValidationError<CustomError>): error is FileAccessError; /** * A simple type-guard that can be used to check if the * {@link FileValidationError} is the {@link TooManyFilesError} which can be * useful when displaying the errors to the user. * * @param error - The error to check * @returns true if the error is a {@link TooManyFilesError} */ export declare function isTooManyFilesError<CustomError>(error: FileValidationError<CustomError>): error is TooManyFilesError; /** * A simple type-guard that can be used to check if the * {@link FileValidationError} is the {@link FileSizeError} which can be * useful when displaying the errors to the user. * * @param error - The error to check * @returns true if the error is a {@link FileSizeError} */ export declare function isFileSizeError<CustomError>(error: FileValidationError<CustomError>): error is FileSizeError; /** * A simple type-guard that can be used to check if the * {@link FileValidationError} is the {@link FileExtensionError} which can be * useful when displaying the errors to the user. * * @param error - The error to check * @returns true if the error is a {@link FileExtensionError} */ export declare function isFileExtensionError<CustomError>(error: FileValidationError<CustomError>): error is FileExtensionError; /** * This function is used to determine if a file should be added to the * {@link FileExtensionError}. The default implementation should work for most * use cases except when files that do not have extensions can be uploaded. i.e. * LICENSE files. * * @param file - The file being checked * @param extensionRegExp - A regex that will only be defined if the * `extensions` list had at least one value. * @param extensions - The list of extensions allowed * @returns true if the file has a valid name. * @remarks \@since 3.1.0 */ export declare type IsValidFileName = (file: File, extensionRegExp: RegExp | undefined, extensions: readonly string[]) => boolean; /** * * @defaultValue `matcher?.test(file.name) ?? true` * @remarks \@since 3.1.0 */ export declare const isValidFileName: IsValidFileName; /** @remarks \@since 2.9.0 */ export interface FileValidationOptions { /** * If the number of files should be limited, set this value to a number * greater than `0`. * * Note: This still allows "infinite" files when set to `0` since the * `<input>` element should normally be set to `disabled` if files should not * be able to be uploaded. * * @defaultValue `-1` */ maxFiles?: number; /** * An optional minimum file size to enforce for each file. This will only be * used when it is greater than `0`. * * @defaultValue `-1` */ minFileSize?: number; /** * An optional maximum file size to enforce for each file. This will only be * used when it is greater than `0`. * * @defaultValue `-1` */ maxFileSize?: number; /** * An optional list of extensions to enforce when uploading files. * * Note: The extensions and file names will be compared ignoring case. * * @example * Only Allow Images * ```ts * const extensions = ["png", "jpeg", "jpg", "gif"]; * ``` */ extensions?: readonly string[]; /** {@inheritDoc IsValidFileName} */ isValidFileName?: IsValidFileName; /** * An optional total file size to enforce when the {@link maxFiles} option is * not set to `1`. * * @defaultValue `-1` */ totalFileSize?: number; } /** @remarks \@since 2.9.0 */ export interface FilesValidationOptions extends Required<FileValidationOptions> { /** * The total number of bytes in the {@link FileUploadHookReturnValue.stats} * list. This is really just: * * ```ts * const totalBytes = stats.reduce((total, stat) => total + stat.file.size, 0); * ``` */ totalBytes: number; /** * The total number of files in the {@link FileUploadHookReturnValue.stats}. */ totalFiles: number; } /** @remarks \@since 2.9.0 */ export interface ValidatedFilesResult<CustomError> { /** * A filtered list of files that have been validated and can be queued for the * upload process. */ pending: readonly File[]; /** * A list of {@link FileValidationError} that occurred during the validation * step. * * Note: If an error has occurred, the file **should not** be added to the * {@link pending} list of files. */ errors: readonly FileValidationError<CustomError>[]; } /** * This function will be called whenever a file has been uploaded by the user * either through an `<input type="file">` or drag and drop behavior. * * @example * Simple Example * ```ts * const validateFiles: FilesValidator = (files, options) => { * const invalid: File[] = []; * const pending: File[] = []; * for (const file of files) { * if (!/\.(jpe?g|svg|png)$/i.test(name)) { * invalid.push(file); * } else { * pending.push(file); * } * } * * const errors: FileValidationError[] = []; * if (invalid.length) { * errors.push(new GenericFileError(invalid)) * } * * return { pending, errors }; * }; * ``` * * @typeparam E - An optional custom file validation error. * @param files - The list of files to check * @param options - The {@link FilesValidationOptions} * @returns the {@link ValidatedFilesResult} * @see {@link validateFiles} for the default implementation * @remarks \@since 2.9.0 */ export declare type FilesValidator<CustomError = never> = (files: readonly File[], options: FilesValidationOptions) => ValidatedFilesResult<CustomError>; /** * A pretty decent default implementation for validating files with the * {@link useFileUpload} that ensures the {@link FilesValidationOptions} are * enforced before allowing a file to be uploaded. * * @typeparam E - An optional custom file validation error. * @param files - The list of files to check * @param options - The {@link FilesValidationOptions} * @returns the {@link ValidatedFilesResult} * @remarks \@since 2.9.0 */ export declare function validateFiles<CustomError>(files: readonly File[], { maxFiles, extensions, minFileSize, maxFileSize, totalBytes, totalFiles, totalFileSize, isValidFileName, }: FilesValidationOptions): ValidatedFilesResult<CustomError>; /** * This will first check if the mime-type of the file starts with `text/` and * fallback to checking a few file names or extensions that should be considered * text. * * This function is not guaranteed to be 100% correct and is only useful if * trying to generate a preview of files uploaded to the browser. * * @param file - The file to check * @returns `true` if the file should be considered as a text-content file. * @remarks \@since 2.9.0 */ export declare function isTextFile(file: File): boolean; /** * This will first check if the mime-type of the file starts with `text\/` and * fallback to checking a few file names or extensions that should be considered * text. * * This function is not guaranteed to be 100% correct and is only useful if * trying to generate a preview of files uploaded to the browser. * * @param file - The file to check * @returns `true` if the file should be considered as a text content file. * @remarks \@since 2.9.0 */ export declare function isImageFile(file: File): boolean; /** * This will first check if the mime-type of the file starts with `audio/` and * fallback to checking a few file names or extensions that should be considered * audio. * * This function is not guaranteed to be 100% correct and is only useful if * trying to generate a preview of files uploaded to the browser. * * @param file - The file to check * @returns `true` if the file should be considered as a audio content file. * @remarks \@since 2.9.0 */ export declare function isAudioFile(file: File): boolean; /** * This will first check if the mime-type of the file starts with `video/` and * fallback to checking a few file names or extensions that should be considered * video. * * This function is not guaranteed to be 100% correct and is only useful if * trying to generate a preview of files uploaded to the browser. * * @param file - The file to check * @returns `true` if the file should be considered as a video content file. * @remarks \@since 2.9.0 */ export declare function isVideoFile(file: File): boolean; /** * This function is not guaranteed to be 100% correct and is only useful if * trying to generate a preview of files uploaded to the browser. * * @param file - The file to check * @returns `true` if the file matches an image, audio, or video file. * @remarks \@since 2.9.0 */ export declare function isMediaFile(file: File): boolean; /** * One of the function names from a `FileReader` to upload a file to the * client. * * Note: If this file does not need to be previewed in the browser and will * immediately be uploaded to a server, use `readAsArrayBuffer`. * * @remarks \@since 2.9.0 */ export declare type FileReaderParser = "readAsText" | "readAsDataURL" | "readAsBinaryString" | "readAsArrayBuffer"; /** * A function that should return one of the {@link FileReaderParser} functions * to start uploading a file to the browser. * * @example * The Default File Upload Parser * ```ts * export const getFileParser: GetFileParser = (file) => { * if (isMediaFile(file)) { * return "readAsDataURL"; * } * * if (isTextFile(file)) { * return "readAsText"; * } * * return "readAsArrayBuffer"; * }; * ``` * * @param file - The file to get a parser for * @returns the {@link FileReaderParser} string. * @remarks \@since 2.9.0 */ export declare type GetFileParser = (file: File) => FileReaderParser; /** * This function will attempt to read: * - media (image, audio, and video) files as a data url so they can be * previewed in `<img>`, `<audio>`, and `<video>` tags * - text files as plain text * - everything else as an `ArrayBuffer` which can be manually converted into a * data url if needed with `URL.createObjectURL` * * @remarks \@since 2.9.0 */ export declare const getFileParser: GetFileParser; /** @remarks \@since 2.9.0 */ export interface SplitFileUploads { readonly pending: readonly ProcessingFileUploadStats[]; readonly uploading: readonly ProcessingFileUploadStats[]; readonly complete: readonly CompletedFileUploadStats[]; } /** * This util will split all the current upload stats by status. * * @param stats - The {@link FileUploadStats} list generally returned by the * {@link useFileUpload} hook. * @returns the {@link SplitFileUploads}. * @remarks \@since 2.9.0 */ export declare function getSplitFileUploads(stats: readonly FileUploadStats[]): SplitFileUploads;