# or
yarn add @zag-js/file-upload @zag-js/solid
```

## Anatomy

To set up the file upload correctly, you'll need to understand its anatomy and
how we name its parts.

> Each part includes a `data-part` attribute to help identify them in the DOM.



## Usage

First, import the file upload package into your project

```jsx
import * as fileUpload from "@zag-js/file-upload"
```

The file upload package exports two key functions:

- `machine` â The state machine logic for the file upload widget.
- `connect` â The function that translates the machine's state to JSX attributes
  and event handlers.

Next, import the required hooks and functions for your framework and use the
file upload machine in your project ð¥

```jsx
import * as fileUpload from "@zag-js/file-upload"
import { normalizeProps, useMachine } from "@zag-js/solid"
import { createUniqueId, createMemo, Index } from "solid-js"

export function FileUpload() {
  const service = useMachine(fileUpload.machine, {
    id: createUniqueId(),
  })

  const api = createMemo(() => fileUpload.connect(service, normalizeProps))

  return (
    <div {...api().getRootProps()}>
      <div {...api().getDropzoneProps()}>
        <input {...api().getHiddenInputProps()} />
        <span>Drag your file(s) here</span>
      </div>

      <button {...api().getTriggerProps()}>Choose file(s)</button>

      <ul {...api().getItemGroupProps()}>
        <Index each={api().acceptedFiles}>
          {(file) => (
            <li {...api().getItemProps({ file: file() })}>
              <div {...api().getItemNameProps({ file: file() })}>
                {file().name}
              </div>
              <button {...api().getItemDeleteTriggerProps({ file: file() })}>
                Delete
              </button>
            </li>
          )}
        </Index>
      </ul>
    </div>
  )
}
```

### Setting the accepted file types

Use the `accept` attribute to set the accepted file types.

```jsx
const service = useMachine(fileUpload.machine, {
  accept: "image/*",
})
```

Alternatively, you can provide an object with a MIME type and an array of file
extensions.

```jsx
const service = useMachine(fileUpload.machine, {
  accept: {
    "image/png": [".png"],
    "text/html": [".html", ".htm"],
  },
})
```

### Setting the maximum number of files

Use the `maxFiles` attribute to set the maximum number of files that can be
uploaded. This will set the `multiple` attribute on the underlying input
element.

```jsx
const service = useMachine(fileUpload.machine, {
  maxFiles: 5,
})
```

### Setting the maximum size per file

Use the `maxFileSize` attribute to set the maximum size per file that can be
uploaded.

```jsx
const service = useMachine(fileUpload.machine, {
  maxFileSize: 1024 * 1024 * 10, // 10MB
})
```

### Listening to file changes

When files are uploaded, the `onFileChange` callback is invoked with the details
of the accepted and rejected files.

```jsx
const service = useMachine(fileUpload.machine, {
  onFileChange(details) {
    // details => { acceptedFiles: File[], rejectedFiles: { file: File, errors: [] }[] }
    console.log(details.acceptedFiles)
    console.log(details.rejectedFiles)
  },
})
```

### Usage within a form

To use the file upload within a form, set the `name` attribute in the machine's
context, and ensure you render the input element `api.getHiddenInputProps()`

```jsx
const service = useMachine(fileUpload.machine, {
  name: "avatar",
})
```

### Displaying image preview

To display a preview of the uploaded image, use the built-in FileReader API to
read the file and set the `src` attribute of an image element.

```jsx
const service = useMachine(fileUpload.machine, {
  onFileChange(details) {
    const reader = new FileReader()
    reader.onload = (event) => {
      const image = event.target.result
      // set the image as the src of an image element
    }
    reader.readAsDataURL(details.acceptedFiles[0])
  },
})
```

### Applying custom validation

To apply custom validation, set the `validate` attribute to a function that
returns an **array of error strings**.

The returned array can contain any string as an error message. While zagjs
supports default errors such as `TOO_MANY_FILES`, `FILE_INVALID_TYPE`,
`FILE_TOO_LARGE`, or `FILE_TOO_SMALL`, you can return any string that represents
your custom validation errors.

> Return `null` if no validation errors are detected.

```jsx
const service = useMachine(fileUpload.machine, {
  validate(file) {
    // Check if file size exceeds 10MB
    if (file.size > 1024 * 1024 * 10) {
      return ["FILE_TOO_LARGE"]
    }
    return null
  },
})
```

Apply multiple validation errors:

```js
const service = useMachine(fileUpload.machine, {
  validate(file) {
    const errors = []

    // Check file size
    if (file.size > 10 * 1024 * 1024) {
      errors.push("FILE_TOO_LARGE") // Default error enum
    }

    // Ensure file is a PDF
    if (!file.name.endsWith(".pdf")) {
      errors.push("ONLY_PDF_ALLOWED") // Custom error
    }

    // Custom check: Reject duplicate files
    const isDuplicate = details.acceptedFiles.some(
      (acceptedFile) => acceptedFile.name === file.name,
    )
    if (isDuplicate) {
      errors.push("FILE_EXISTS")
    }

    return errors.length > 0 ? errors : null
  },
})
```

### Disabling drag and drop

To disable the drag and drop functionality, set the `allowDrop` context property
to `false`.

```jsx
const service = useMachine(fileUpload.machine, {
  allowDrop: false,
})
```

### Allowing directory selection

Set the `directory` property to `true` to enable selecting directories instead
of files.

This maps to the native input `webkitdirectory` HTML attribute and allows users
to select directories and their contents.

> Please note that support for this feature varies from browser to browser.

```jsx
const service = useMachine(fileUpload.machine, {
  directory: true,
})
```

### Supporting media capture on mobile devices

Set the `capture` property to specify the media capture mechanism to capture
media on the spot. The value can be:

- `user` for capturing media from the user-facing camera
- `environment` for the outward-facing camera

> This behavior only works on mobile devices. On desktop devices, it will open
> the file system like normal.

```jsx
const service = useMachine(fileUpload.machine, {
  capture: "user",
})
```

### Pasting files from clipboard

After a user copies an image, to allow pasting the files from the clipboard, you
can listen for the paste event and use the `api.setFiles` method to set the
files.

Here's an example of how to do this in React.

```jsx
function Demo() {
  const service = useMachine(fileUpload.machine, {
    accept: "image/*",
  })

  const api = fileUpload.connect(service, normalizeProps)

  return (
    <textarea
      onPaste={(event) => {
        if (event.clipboardData?.files) {
          api.setFiles(Array.from(event.clipboardData.files))
        }
      }}
    />
  )
}
```

### Transforming files before acceptance

Use the `transformFiles` callback to process files before they're added to
`acceptedFiles`. This is useful for scenarios like image cropping, compression,
or format conversion.

The `transformFiles` function receives the selected files and should return a
promise that resolves with the transformed files.

```jsx
const service = useMachine(fileUpload.machine, {
  accept: "image/*",
  transformFiles: async (files) => {
    return Promise.all(
      files.map(async (file) => {
        // Compress or transform the file
        const transformedBlob = await processImage(file)
        return new File([transformedBlob], file.name, { type: file.type })
      }),
    )
  },
})
```

While files are being transformed, the `api.transforming` boolean is `true`,
allowing you to show loading states in your UI.

## Styling guide

Earlier, we mentioned that each file upload part has a `data-part` attribute
added to them to select and style them in the DOM.

```css
[data-part="root"] {
  /* styles for root element*/
}

[data-part="dropzone"] {
  /* styles for root element*/
}

[data-part="trigger"] {
  /* styles for file picker trigger */
}

[data-part="label"] {
  /* styles for the input's label */
}
```

### Dragging State

When the user drags a file over the file upload, the `data-dragging` attribute
is added to the `root` and `dropzone` parts.

```css
[data-part="root"][data-dragging] {
  /* styles for when the user is dragging a file over the file upload */
}

[data-part="dropzone"][data-dragging] {
  /* styles for when the user is dragging a file over the file upload */
}
```

### Disabled State

When the file upload is disabled, the `data-disabled` attribute is added to the
component parts.

```css
[data-part="root"][data-disabled] {
  /* styles for when the file upload is disabled */
}

[data-part="dropzone"][data-disabled] {
  /* styles for when the file upload is disabled */
}

[data-part="trigger"][data-disabled] {
  /* styles for when the file upload is disabled */
}

[data-part="label"][data-disabled] {
  /* styles for when the file upload is disabled */
}
```

## Methods and Properties

### Machine Context

The file upload machine exposes the following context properties:

**`name`**
Type: `string`
Description: The name of the underlying file input

**`ids`**
Type: `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; }>`
Description: The ids of the elements. Useful for composition.

**`translations`**
Type: `IntlTranslations`
Description: The localized messages to use.

**`accept`**
Type: `Record<string, string[]> | FileMimeType[]`
Description: The accept file types

**`disabled`**
Type: `boolean`
Description: Whether the file input is disabled

**`required`**
Type: `boolean`
Description: Whether the file input is required

**`allowDrop`**
Type: `boolean`
Description: Whether to allow drag and drop in the dropzone element

**`maxFileSize`**
Type: `number`
Description: The maximum file size in bytes

**`minFileSize`**
Type: `number`
Description: The minimum file size in bytes

**`maxFiles`**
Type: `number`
Description: The maximum number of files

**`preventDocumentDrop`**
Type: `boolean`
Description: Whether to prevent the drop event on the document

**`validate`**
Type: `(file: File, details: FileValidateDetails) => FileError[]`
Description: Function to validate a file

**`defaultAcceptedFiles`**
Type: `File[]`
Description: The default accepted files when rendered.
Use when you don't need to control the accepted files of the input.

**`acceptedFiles`**
Type: `File[]`
Description: The controlled accepted files

**`onFileChange`**
Type: `(details: FileChangeDetails) => void`
Description: Function called when the value changes, whether accepted or rejected

**`onFileAccept`**
Type: `(details: FileAcceptDetails) => void`
Description: Function called when the file is accepted

**`onFileReject`**
Type: `(details: FileRejectDetails) => void`
Description: Function called when the file is rejected

**`capture`**
Type: `"user" | "environment"`
Description: The default camera to use when capturing media

**`directory`**
Type: `boolean`
Description: Whether to accept directories, only works in webkit browsers

**`invalid`**
Type: `boolean`
Description: Whether the file input is invalid

**`transformFiles`**
Type: `(files: File[]) => Promise<File[]>`
Description: Function to transform the accepted files to apply transformations

**`locale`**
Type: `string`
Description: The current locale. Based on the BCP 47 definition.

**`dir`**
Type: `"ltr" | "rtl"`
Description: The document's text/writing direction.

**`id`**
Type: `string`
Description: The unique identifier of the machine.

**`getRootNode`**
Type: `() => ShadowRoot | Node | Document`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.

### Machine API

The file upload `api` exposes the following methods:

**`dragging`**
Type: `boolean`
Description: Whether the user is dragging something over the root element

**`focused`**
Type: `boolean`
Description: Whether the user is focused on the dropzone element

**`disabled`**
Type: `boolean`
Description: Whether the file input is disabled

**`transforming`**
Type: `boolean`
Description: Whether files are currently being transformed via `transformFiles`

**`openFilePicker`**
Type: `VoidFunction`
Description: Function to open the file dialog

**`deleteFile`**
Type: `(file: File, type?: ItemType) => void`
Description: Function to delete the file from the list

**`acceptedFiles`**
Type: `File[]`
Description: The accepted files that have been dropped or selected

**`rejectedFiles`**
Type: `FileRejection[]`
Description: The files that have been rejected

**`setFiles`**
Type: `(files: File[]) => void`
Description: Sets the accepted files

**`clearFiles`**
Type: `VoidFunction`
Description: Clears the accepted files

**`clearRejectedFiles`**
Type: `VoidFunction`
Description: Clears the rejected files

**`getFileSize`**
Type: `(file: File) => string`
Description: Returns the formatted file size (e.g. 1.2MB)

**`createFileUrl`**
Type: `(file: File, cb: (url: string) => void) => VoidFunction`
Description: Returns the preview url of a file.
Returns a function to revoke the url.

**`setClipboardFiles`**
Type: `(dt: DataTransfer) => boolean`
Description: Sets the clipboard files
Returns `true` if the clipboard data contains files, `false` otherwise.

### Data Attributes

**`Root`**

**`data-scope`**: file-upload
**`data-part`**: root
**`data-disabled`**: Present when disabled
**`data-dragging`**: Present when in the dragging state

**`Dropzone`**

**`data-scope`**: file-upload
**`data-part`**: dropzone
**`data-invalid`**: Present when invalid
**`data-disabled`**: Present when disabled
**`data-dragging`**: Present when in the dragging state

**`Trigger`**

**`data-scope`**: file-upload
**`data-part`**: trigger
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid

**`ItemGroup`**

**`data-scope`**: file-upload
**`data-part`**: item-group
**`data-disabled`**: Present when disabled
**`data-type`**: The type of the item

**`Item`**

**`data-scope`**: file-upload
**`data-part`**: item
**`data-disabled`**: Present when disabled
**`data-type`**: The type of the item

**`ItemName`**

**`data-scope`**: file-upload
**`data-part`**: item-name
**`data-disabled`**: Present when disabled
**`data-type`**: The type of the item

**`ItemSizeText`**

**`data-scope`**: file-upload
**`data-part`**: item-size-text
**`data-disabled`**: Present when disabled
**`data-type`**: The type of the item

**`ItemPreview`**

**`data-scope`**: file-upload
**`data-part`**: item-preview
**`data-disabled`**: Present when disabled
**`data-type`**: The type of the item

**`ItemPreviewImage`**

**`data-scope`**: file-upload
**`data-part`**: item-preview-image
**`data-disabled`**: Present when disabled
**`data-type`**: The type of the item

**`ItemDeleteTrigger`**

**`data-scope`**: file-upload
**`data-part`**: item-delete-trigger
**`data-disabled`**: Present when disabled
**`data-type`**: The type of the item

**`Label`**

**`data-scope`**: file-upload
**`data-part`**: label
**`data-disabled`**: Present when disabled
**`data-required`**: Present when required

**`ClearTrigger`**

**`data-scope`**: file-upload
**`data-part`**: clear-trigger
**`data-disabled`**: Present when disabled
A floating panel is a detachable window that floats above the main interface,
typically used for displaying and editing properties. The panel can be dragged,
resized, and positioned anywhere on the screen for optimal workflow.

> Think of the panel that pops up in Figma when you click `variables` or try set
> a color.

## Resources


[Latest version: v]()
[Logic Visualizer](https://zag-visualizer.vercel.app/floating-panel)
[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/floating-panel)



**Features**

- Allows interaction with the main content
- Supports dragging and resizing
- Support for minimizing and maximizing the panel
- Controlled and uncontrolled size and position
- Support for snapping to a grid
- Support for locking the aspect ratio
- Support for closing on escape key
  - Support for persisting the size and position when closed

## Installation

To use the hover card machine in your project, run the following command in your
command line:

```bash
npm install @zag-js/floating-panel @zag-js/solid