);
}
/**
* Lists the files of the nearest `UploaderProvider`.
*
* @example
* ```tsx
*
* ```
*/
export function FileList({
disabled,
className,
...props
}: React.ComponentProps<'ul'> & {
/** Hides the retry, cancel and remove actions. */
disabled?: boolean;
}) {
const { fileStates } = useUploader();
if (fileStates.length === 0) return null;
return (
{fileStates.map((fileState) => (
))}
);
}
/**
* Props for the FileUploader component.
*/
export type FileUploaderProps = React.ComponentProps<'div'> & {
/**
* Maximum number of files allowed in total.
*/
maxFiles?: number;
/**
* Maximum file size in bytes.
*/
maxSize?: number;
/**
* Accepted file types.
*
* @example
* ```tsx
* accept={{ 'application/pdf': ['.pdf'] }}
* ```
*/
accept?: Accept;
/**
* Human-readable list of accepted types, shown in the hint and messages.
*
* @example "PDF or TXT"
*/
typesLabel?: string;
/**
* Whether the uploader is disabled.
*/
disabled?: boolean;
/**
* Additional className for the dropzone.
*/
dropzoneClassName?: string;
/**
* Additional className for the file list.
*/
fileListClassName?: string;
};
/**
* A dropzone with a list of files, their progress and a summary.
*
* @example
* ```tsx
*
* ```
*/
export function FileUploader({
maxFiles,
maxSize,
accept,
typesLabel,
disabled,
className,
dropzoneClassName,
fileListClassName,
...props
}: FileUploaderProps) {
const { fileStates, resetFiles } = useUploader();
const completed = fileStates.filter((fs) => fs.status === 'COMPLETE').length;
const totalSize = fileStates.reduce((sum, fs) => sum + fs.file.size, 0);
return (
{fileStates.length > 0 && (
{completed} of {fileStates.length} uploaded ·{' '}
{formatFileSize(totalSize)}
)}
);
}
````
## Usage
Install or copy the component from [Installation](#installation) before using this example.
```tsx
'use client';
import { FileUploader } from '@/components/upload/multi-file';
import {
UploaderProvider,
type UploadFn,
} from '@/components/upload/uploader-provider';
import { useEdgeStore } from '@/lib/edgestore';
import * as React from 'react';
export function MultiFileDropzoneUsage() {
const { edgestore } = useEdgeStore();
const uploadFn: UploadFn = React.useCallback(
async ({ file, onProgressChange, signal }) => {
const res = await edgestore.publicFiles.upload({
file,
signal,
onProgressChange,
});
// you can run some server action or api here
// to add the necessary data to your database
console.log(res);
return res;
},
[edgestore],
);
return (
);
}
```
## Props
| Prop | Type | Default | Description |
| ------------------- | --------- | --------- | ------------------------------------------------------------------------------------------------------------------------ |
| `maxFiles` | `number` | | Maximum number of files in total. |
| `maxSize` | `number` | | Maximum file size in bytes. |
| `accept` | `Accept` | all types | Accepted file types, in [react-dropzone's format](https://react-dropzone.js.org/#section-accepting-specific-file-types). |
| `typesLabel` | `string` | | Human-readable list of accepted types, shown in the hint and in error messages. |
| `disabled` | `boolean` | `false` | Disables the component. |
| `dropzoneClassName` | `string` | | Additional className for the dropzone. |
| `fileListClassName` | `string` | | Additional className for the file list. |
### Building your own layout
`multi-file.tsx` also exports the pieces it is made of, so you can arrange them differently:
* `FileList` lists the files of the nearest `UploaderProvider`.
* `FileListItem` renders one file with its status and actions.
* `FileBadge` renders the colored file-type tile.