sebafermanelli

File Grid

Component

About

An accessible icon-grid for browsing files and folders, like iCloud Drive's grid view, with keyboard navigation, single/multiple selection, and a right-click/⋯ context menu per item.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds File Grid to your project and fits it to your theme.

Code

How File Grid is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

"use client";

import FileGrid, { type FileGridItem } from "@/components/ui/file-grid";
import {
  ContextMenuItem,
  ContextMenuSeparator,
} from "@/components/ui/file-grid-utils/context-menu";

const INVOICE_THUMB =
  "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2260%22%20height%3D%2280%22%3E%3Crect%20width%3D%2260%22%20height%3D%2280%22%20fill%3D%22%23fff%22%2F%3E%3Crect%20x%3D%228%22%20y%3D%2210%22%20width%3D%2230%22%20height%3D%226%22%20fill%3D%22%230071e3%22%2F%3E%3Cg%20fill%3D%22%23c7c7cc%22%3E%3Crect%20x%3D%228%22%20y%3D%2226%22%20width%3D%2244%22%20height%3D%223%22%2F%3E%3Crect%20x%3D%228%22%20y%3D%2234%22%20width%3D%2244%22%20height%3D%223%22%2F%3E%3Crect%20x%3D%228%22%20y%3D%2242%22%20width%3D%2236%22%20height%3D%223%22%2F%3E%3Crect%20x%3D%228%22%20y%3D%2258%22%20width%3D%2220%22%20height%3D%223%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E";

const CREDIT_NOTE_THUMB =
  "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2260%22%20height%3D%2280%22%3E%3Crect%20width%3D%2260%22%20height%3D%2280%22%20fill%3D%22%23fff%22%2F%3E%3Crect%20x%3D%228%22%20y%3D%2210%22%20width%3D%2230%22%20height%3D%226%22%20fill%3D%22%23ff9f0a%22%2F%3E%3Cg%20fill%3D%22%23c7c7cc%22%3E%3Crect%20x%3D%228%22%20y%3D%2226%22%20width%3D%2244%22%20height%3D%223%22%2F%3E%3Crect%20x%3D%228%22%20y%3D%2234%22%20width%3D%2244%22%20height%3D%223%22%2F%3E%3Crect%20x%3D%228%22%20y%3D%2242%22%20width%3D%2236%22%20height%3D%223%22%2F%3E%3Crect%20x%3D%228%22%20y%3D%2258%22%20width%3D%2220%22%20height%3D%223%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E";

const ARCHIVOS: FileGridItem[] = [
  { id: "facturas", name: "Facturas", kind: "24 ítems", folder: true },
  { id: "recibos", name: "Recibos", kind: "8 ítems", folder: true },
  {
    id: "f-0012",
    name: "Factura 0012.pdf",
    kind: "128 KB",
    thumbnail: <img alt="" src={INVOICE_THUMB} />,
  },
  {
    id: "f-0013",
    name: "Factura 0013.pdf",
    kind: "96 KB",
    thumbnail: <img alt="" src={INVOICE_THUMB} />,
  },
  {
    id: "nc-0003",
    name: "Nota de crédito 0003.pdf",
    kind: "54 KB",
    thumbnail: <img alt="" src={CREDIT_NOTE_THUMB} />,
  },
  { id: "resumen", name: "Resumen anual.xlsx", kind: "44 KB" },
];

function acciones(item: FileGridItem, selected: FileGridItem[]) {
  const count = selected.length;
  return (
    <>
      <ContextMenuItem onClick={() => console.log("Abrir", item.name)}>
        Abrir
      </ContextMenuItem>
      <ContextMenuItem
        onClick={() =>
          console.log(
            "Descargar",
            selected.map((s) => s.name),
          )
        }
      >
        {count > 1 ? `Descargar ${count} elementos` : "Descargar"}
      </ContextMenuItem>
      <ContextMenuItem onClick={() => console.log("Cambiar nombre", item.name)}>
        Cambiar nombre
      </ContextMenuItem>
      <ContextMenuSeparator />
      <ContextMenuItem
        variant="destructive"
        onClick={() =>
          console.log(
            "Eliminar",
            selected.map((s) => s.name),
          )
        }
      >
        {count > 1 ? `Eliminar ${count} elementos` : "Eliminar"}
      </ContextMenuItem>
    </>
  );
}

export default function FileGridDemo() {
  return (
    <div className="w-full max-w-2xl bg-background p-6">
      <FileGrid
        aria-label="Archivos"
        defaultSelected="f-0012"
        items={ARCHIVOS}
        menu={acciones}
      />
    </div>
  );
}

Dependencies

More by sebafermanelli

View all
  • Count Badge

    Count Badge

    2