# Folder

> Folder that drops its flap open or swings open like a book, in solid, glass or matte, with pages you can pick, drag out and drop back in.

A spring-driven 3D folder. Click it (or hover, with `open-on="hover"`) to open it: in `flap` mode the front flap drops forward and the pages fan out above; in `book` mode it lies flat and the cover swings over like a book. Hover to tease the pages out, click a page to bring it up close, and drag pages out onto the desk and back in again. The `glass` material renders the front as refracting, frosted WebGL glass.

<demo-folder>



</demo-folder>

## Installation

```bash
npx shadcn-vue@latest add "https://nxui.geoql.in/r/folder.json"
```

## Usage

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import Folder from '~/components/ui/folder/Folder.vue';
  import type { FolderEntry } from '~/components/ui/folder/types';

  const open = ref(false);
  const items: FolderEntry[] = [
    { title: 'Brand guidelines', description: 'Logo, color and type rules.' },
    'Launch plan',
    { title: 'Moodboard', image: '/moodboard.jpg', alt: 'Moodboard' },
  ];

  function onDrop(item: FolderEntry, index: number, inside: boolean) {
    console.log(index, inside ? 'put back' : 'left on the desk');
  }
</script>

<template>
  <Folder
    v-model:open="open"
    :items="items"
    label="Projects"
    mode="book"
    variant="glass"
    color="#7aa7ff"
    layout="spread"
    @item-drop="onDrop"
  />
</template>
```

Use the `item` slot to render your own page content. It receives `{ item, index }`:

```vue
<Folder :items="tickets">
  <template #item="{ item }">
    <TicketCard :ticket="item" />
  </template>
</Folder>
```

## Props

<table>
<thead>
  <tr>
    <th>
      Prop
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        items
      </code>
    </td>
    
    <td>
      <code>
        FolderEntry[]
      </code>
    </td>
    
    <td>
      Three sample documents
    </td>
    
    <td>
      What the folder holds, up to 6. A string, a number, <code>
        { title, description }
      </code>
      
       or <code>
        { image, title, alt }
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mode
      </code>
    </td>
    
    <td>
      <code>
        'flap' | 'book'
      </code>
    </td>
    
    <td>
      <code>
        'flap'
      </code>
    </td>
    
    <td>
      <code>
        flap
      </code>
      
       stands the folder up and drops the front open. <code>
        book
      </code>
      
       lays it flat and swings the cover over.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        variant
      </code>
    </td>
    
    <td>
      <code>
        'solid' | 'glass' | 'matte'
      </code>
    </td>
    
    <td>
      <code>
        'solid'
      </code>
    </td>
    
    <td>
      Material. <code>
        glass
      </code>
      
       turns the front into refracting glass that shows the pages inside.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        layout
      </code>
    </td>
    
    <td>
      <code>
        'spread' | 'stack' | 'row'
      </code>
    </td>
    
    <td>
      <code>
        'spread'
      </code>
    </td>
    
    <td>
      How the pages are laid out once the folder is open.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#3d8bff'
      </code>
    </td>
    
    <td>
      Folder color. Shades are derived from it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        paper-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Page color.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Text on the tab. The tab grows to fit it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        tab-position
      </code>
    </td>
    
    <td>
      <code>
        'left' | 'center' | 'right'
      </code>
    </td>
    
    <td>
      <code>
        'right'
      </code>
    </td>
    
    <td>
      Where the tab sits along the top edge.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        open-on
      </code>
    </td>
    
    <td>
      <code>
        'click' | 'hover'
      </code>
    </td>
    
    <td>
      <code>
        'click'
      </code>
    </td>
    
    <td>
      Open on click, or while the cursor is over the folder.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        open
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Controls the folder (<code>
        v-model:open
      </code>
      
      ). Leave it out to let the folder manage itself.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        default-open
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Starts the folder open.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        draggable
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Drag pages out, leave them anywhere and drop them back in.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        zoom-on-click
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Clicking a page brings it up close. Click it again, press Escape or click away to put it back.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        peek
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Teases the pages out on hover. In flap mode the page under the cursor rises highest.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Entrance when the folder scrolls into view: the pages drop in and the folder closes over them.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Scale of the whole folder.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        frost
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.25
      </code>
    </td>
    
    <td>
      How much the glass front blurs what is behind it, 0 (clear) to 1. Glass variant only.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        tilt
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      How much the folder tilts toward the cursor, 0 to 1.5.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Speed of every animation.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bounce
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.4
      </code>
    </td>
    
    <td>
      How much the flap, cover and pages overshoot as they settle, 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        close-on-outside-click
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Closes the folder on a click outside it or on Escape.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        theme
      </code>
    </td>
    
    <td>
      <code>
        'dark' | 'light'
      </code>
    </td>
    
    <td>
      <code>
        'dark'
      </code>
    </td>
    
    <td>
      Tunes the shadows and glass sheen for dark or light pages.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the root.
    </td>
  </tr>
</tbody>
</table>

`FolderItem` also accepts `crossOrigin: 'anonymous'` for remote images, so the glass front can refract them (the host must send CORS headers).

## Events

<table>
<thead>
  <tr>
    <th>
      Event
    </th>
    
    <th>
      Payload
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        update:open
      </code>
    </td>
    
    <td>
      <code>
        (open: boolean)
      </code>
    </td>
    
    <td>
      The folder opened or closed.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        item-click
      </code>
    </td>
    
    <td>
      <code>
        (item: FolderEntry, index: number)
      </code>
    </td>
    
    <td>
      A page was clicked while the folder is open, or after it was dragged out.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        item-drop
      </code>
    </td>
    
    <td>
      <code>
        (item: FolderEntry, index: number, inside: boolean)
      </code>
    </td>
    
    <td>
      A dragged page was let go; <code>
        inside
      </code>
      
       says whether it landed back in the folder.
    </td>
  </tr>
</tbody>
</table>

## Slots

<table>
<thead>
  <tr>
    <th>
      Slot
    </th>
    
    <th>
      Props
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        item
      </code>
    </td>
    
    <td>
      <code>
        { item: FolderEntry, index: number }
      </code>
    </td>
    
    <td>
      Custom content for each page.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The folder is a `role="button"` with `aria-expanded`; Enter or Space toggles it and a focus ring traces the folder outline.
- Pages become buttons (focusable, Enter or Space to zoom) while the folder is open or after they are dragged out.
- Escape puts a zoomed page back, then closes the folder.
- With `prefers-reduced-motion: reduce` the springs are critically damped, the tilt and entrance are skipped and pages no longer stagger.
- Ported from [React Bits](https://reactbits.dev/components/folder) by David Haz.
