# Masonry

> A masonry gallery that springs into place, reflows as it resizes, reorders by drag and opens images in a lightbox.

A masonry gallery whose tiles spring into place as they scroll into view, sharpening from a blur. Columns follow the container width and every tile keeps its image's shape. Hover a tile to zoom, lift or tilt it, drag a tile with the mouse to reorder the grid, and click it to open a lightbox you can step through with the arrow keys.

<demo-masonry>



</demo-masonry>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import Masonry from '~/components/ui/masonry/Masonry.vue';
  import type { MasonryItem } from '~/components/ui/masonry';

  const items: MasonryItem[] = [
    {
      id: '1015',
      img: 'https://picsum.photos/id/1015/600/900?grayscale',
      width: 600,
      height: 900,
      title: 'Alexey Topolyanskiy',
      url: 'https://unsplash.com/photos/-oWyJoSqBRM',
    },
    {
      id: '1011',
      img: 'https://picsum.photos/id/1011/600/750?grayscale',
      width: 600,
      height: 750,
      title: 'Roberto Nickson',
    },
  ];
</script>

<template>
  <Masonry
    :items="items"
    :column-width="240"
    :gap="12"
    animate-from="bottom"
    hover-effect="zoom"
    captions="hover"
    @reorder="(next) => console.log(next)"
  />
</template>
```

Give each item a `width` and `height` to lay it out at once; otherwise the image is measured first. Every tile rides a damped spring, so a resize, a column change or a drag makes the whole grid glide to its new places instead of jumping. Removed items fade out where they stood.

## 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>
        MasonryItem[]
      </code>
    </td>
    
    <td>
      <code>
        []
      </code>
    </td>
    
    <td>
      Tiles to show (see below).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        columns
      </code>
    </td>
    
    <td>
      <code>
        'auto' | number
      </code>
    </td>
    
    <td>
      <code>
        'auto'
      </code>
    </td>
    
    <td>
      Column count. <code>
        'auto'
      </code>
      
       fits as many columns of <code>
        column-width
      </code>
      
       as the container allows.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        column-width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        240
      </code>
    </td>
    
    <td>
      Target column width in px for auto columns.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        gap
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        12
      </code>
    </td>
    
    <td>
      Space between tiles in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        12
      </code>
    </td>
    
    <td>
      Corner radius of the tiles in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        animate-from
      </code>
    </td>
    
    <td>
      <code>
        'bottom' | 'top' | 'left' | 'right' | 'center' | 'random' | 'none'
      </code>
    </td>
    
    <td>
      <code>
        'bottom'
      </code>
    </td>
    
    <td>
      Where tiles come from as they appear.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stagger
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.05
      </code>
    </td>
    
    <td>
      Delay in seconds between tiles appearing.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.6
      </code>
    </td>
    
    <td>
      Length in seconds of the springs that move tiles in and between places.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bounce
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.15
      </code>
    </td>
    
    <td>
      How much the tiles overshoot as they settle, from 0 to 0.9.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        blur-to-focus
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Tiles sharpen from a blur as they appear.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reveal-on-scroll
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Tiles appear as they scroll into view instead of all at once.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        hover-effect
      </code>
    </td>
    
    <td>
      <code>
        'zoom' | 'lift' | 'tilt' | 'none'
      </code>
    </td>
    
    <td>
      <code>
        'zoom'
      </code>
    </td>
    
    <td>
      Zoom the image, lift the tile, or tilt it toward the cursor.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        focus-on-hover
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Fades the other tiles while one is hovered.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        captions
      </code>
    </td>
    
    <td>
      <code>
        'hover' | 'always' | 'none'
      </code>
    </td>
    
    <td>
      <code>
        'hover'
      </code>
    </td>
    
    <td>
      When the title and description show over a tile.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        parallax
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      How far images drift inside their tiles as the page scrolls, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        lightbox
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Clicking a tile opens it full size. Arrow keys step between images, Escape closes.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        draggable
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Drag tiles with a mouse to reorder them. The grid reflows around the tile as it moves.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the grid.
    </td>
  </tr>
</tbody>
</table>

### MasonryItem

<table>
<thead>
  <tr>
    <th>
      Field
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        id
      </code>
    </td>
    
    <td>
      <code>
        string | number
      </code>
    </td>
    
    <td>
      Unique key.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        img
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Image URL.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      Natural width. With <code>
        height
      </code>
      
      , skips measuring the image.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        height
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      Natural height.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        title
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Caption title, also the tile's accessible name.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        description
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Caption text.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        alt
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Alt text (falls back to <code>
        title
      </code>
      
      ).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        url
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Link. Opens from the lightbox, or directly when <code>
        lightbox
      </code>
      
       is off.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        span
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      Number of columns the tile covers.
    </td>
  </tr>
</tbody>
</table>

## Events

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

<tbody>
  <tr>
    <td>
      <code>
        item-click
      </code>
    </td>
    
    <td>
      <code>
        (item: MasonryItem, index: number, event: MouseEvent)
      </code>
    </td>
    
    <td>
      Fired when a tile is clicked. Call <code>
        event.preventDefault()
      </code>
      
       to skip the lightbox or link.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reorder
      </code>
    </td>
    
    <td>
      <code>
        (items: MasonryItem[])
      </code>
    </td>
    
    <td>
      Fired with the new order after a drag.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The grid is a `role="list"` of `listitem` tiles. Tiles are real `<button>` elements while the lightbox or an `item-click` handler is active, links when `lightbox` is off and the item has a `url`, and plain blocks otherwise.
- Keyboard focus shows an outline and reveals hover captions.
- The lightbox is a modal `role="dialog"`: focus moves to the close button, page scroll is locked, Escape closes, the arrow keys step through images, and focus returns to the tile on close.
- Drag to reorder is mouse-only, so touch scrolling never moves tiles.
- With `prefers-reduced-motion: reduce`, tiles appear in place without travel or blur, and reflows and the lightbox skip their animations.
- Ported from [React Bits](https://reactbits.dev/components/masonry) by David Haz.
