// @ts-nocheck import React from 'react'; import { Ref, useCallback } from 'react'; import clsx from 'classnames'; import { Accept, DropEvent, FileError, FileRejection, FileWithPath, useDropzone, } from 'react-dropzone-esm'; import { DropzoneProvider } from './DropzoneProvider'; import { DropzoneAccept, DropzoneIdle, DropzoneReject } from './DropzoneStatus'; import { Box } from '../Layout'; import styles from './Dropzone.module.css'; import { CloudLoadingIndicator } from '../Indicator'; export type DropzoneStylesNames = 'root' | 'inner'; export type DropzoneVariant = 'filled' | 'light'; export type DropzoneCssVariables = { root: | '--dropzone-radius' | '--dropzone-accept-color' | '--dropzone-accept-bg' | '--dropzone-reject-color' | '--dropzone-reject-bg'; }; export interface DropzoneProps { /** Key of `theme.colors` or any valid CSS color to set colors of `Dropzone.Accept`, `theme.primaryColor` by default */ acceptColor?: MantineColor; /** Key of `theme.colors` or any valid CSS color to set colors of `Dropzone.Reject`, `'red'` by default */ rejectColor?: MantineColor; /** Key of `theme.radius` or any valid CSS value to set `border-radius`, numbers are converted to rem, `theme.defaultRadius` by default */ radius?: MantineRadius; /** Determines whether files capturing should be disabled, `false` by default */ disabled?: boolean; /** Called when any files are dropped to the dropzone */ onDropAny?: (files: FileWithPath[], fileRejections: FileRejection[]) => void; /** Called when valid files are dropped to the dropzone */ onDrop: (files: FileWithPath[]) => void; /** Called when dropped files do not meet file restrictions */ onReject?: (fileRejections: FileRejection[]) => void; /** Determines whether a loading overlay should be displayed over the dropzone, `false` by default */ loading?: boolean; /** Mime types of the files that dropzone can accepts. By default, dropzone accepts all file types. */ accept?: Accept | string[]; /** A ref function which when called opens the file system file picker */ openRef?: React.ForwardedRef<() => void | undefined>; /** Determines whether multiple files can be dropped to the dropzone or selected from file system picker, `true` by default */ multiple?: boolean; /** Maximum file size in bytes */ maxSize?: number; /** Name of the form control. Submitted with the form as part of a name/value pair. */ name?: string; /** Maximum number of files that can be picked at once */ maxFiles?: number; /** Set to autofocus the root element */ autoFocus?: boolean; /** If `false`, disables click to open the native file selection dialog */ activateOnClick?: boolean; /** If `false`, disables drag 'n' drop */ activateOnDrag?: boolean; /** If `false`, disables Space/Enter to open the native file selection dialog. Note that it also stops tracking the focus state. */ activateOnKeyboard?: boolean; /** If `false`, stops drag event propagation to parents */ dragEventsBubbling?: boolean; /** Called when the `dragenter` event occurs */ onDragEnter?: (event: React.DragEvent) => void; /** Called when the `dragleave` event occurs */ onDragLeave?: (event: React.DragEvent) => void; /** Called when the `dragover` event occurs */ onDragOver?: (event: React.DragEvent) => void; /** Called when user closes the file selection dialog with no selection */ onFileDialogCancel?: () => void; /** Called when user opens the file selection dialog */ onFileDialogOpen?: () => void; /** If `false`, allow dropped items to take over the current browser window */ preventDropOnDocument?: boolean; /** Set to true to use the File System Access API to open the file picker instead of using an click event, defaults to true */ useFsAccessApi?: boolean; /** Use this to provide a custom file aggregator */ getFilesFromEvent?: ( event: DropEvent, ) => Promise>; /** Custom validation function. It must return null if there's no errors. */ validator?: (file: T) => FileError | FileError[] | null; /** Determines whether pointer events should be enabled on the inner element, `false` by default */ enablePointerEvents?: boolean; /** Props passed down to the Loader component */ loaderProps?: LoaderProps; /** Props passed down to the internal Input component */ inputProps?: React.InputHTMLAttributes; } export type DropzoneFactory = Factory<{ props: DropzoneProps; ref: HTMLDivElement; stylesNames: DropzoneStylesNames; vars: DropzoneCssVariables; staticComponents: { Accept: typeof DropzoneAccept; Idle: typeof DropzoneIdle; Reject: typeof DropzoneReject; }; }>; const defaultProps: Partial = { loading: false, multiple: true, maxSize: Infinity, autoFocus: false, activateOnClick: true, activateOnDrag: true, dragEventsBubbling: true, activateOnKeyboard: true, useFsAccessApi: true, variant: 'light', rejectColor: 'red', }; export const Dropzone = (_props: DropzoneProps) => { const { // classNames, // className, // style, // styles, // unstyled, // vars, radius, disabled, loading, multiple, maxSize, accept, children, onDropAny, onDrop, onReject, openRef, name, maxFiles, autoFocus, activateOnClick, activateOnDrag, dragEventsBubbling, activateOnKeyboard, onDragEnter, onDragLeave, onDragOver, onFileDialogCancel, onFileDialogOpen, preventDropOnDocument, useFsAccessApi, getFilesFromEvent, validator, rejectColor, acceptColor, enablePointerEvents, loaderProps, inputProps, // mod, classNames, ...others } = { ...defaultProps, ..._props, }; const { getRootProps, getInputProps, isDragAccept, isDragReject, open } = useDropzone({ onDrop: onDropAny, onDropAccepted: onDrop, onDropRejected: onReject, disabled: disabled || loading, accept: Array.isArray(accept) ? accept.reduce((r, key) => ({ ...r, [key]: [] }), {}) : accept, multiple, maxSize, maxFiles, autoFocus, noClick: !activateOnClick, noDrag: !activateOnDrag, noDragEventsBubbling: !dragEventsBubbling, noKeyboard: !activateOnKeyboard, onDragEnter, onDragLeave, onDragOver, onFileDialogCancel, onFileDialogOpen, preventDropOnDocument, useFsAccessApi, validator, ...(getFilesFromEvent ? { getFilesFromEvent } : null), }); const isIdle = !isDragAccept && !isDragReject; assignRef(openRef, open); return (
{children}
); }; Dropzone.displayName = '@mantine/dropzone/Dropzone'; Dropzone.Accept = DropzoneAccept; Dropzone.Idle = DropzoneIdle; Dropzone.Reject = DropzoneReject; type PossibleRef = Ref | undefined; export function assignRef(ref: PossibleRef, value: T) { if (typeof ref === 'function') { ref(value); } else if (typeof ref === 'object' && ref !== null && 'current' in ref) { (ref as React.MutableRefObject).current = value; } } export function mergeRefs(...refs: PossibleRef[]) { return (node: T | null) => { refs.forEach((ref) => assignRef(ref, node)); }; } export function useMergedRef(...refs: PossibleRef[]) { return useCallback(mergeRefs(...refs), refs); }