{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "folder",
  "type": "registry:ui",
  "title": "Folder",
  "description": "A folder on the canvas that holds blocks: a back panel with a tab, up to three blocks peeking up as cards, a frosted flap with its name and count; fans on hover, opens wide when a block is dragged over, swings shut when one lands; six paper colours.",
  "dependencies": [],
  "registryDependencies": [
    "https://metalui.dev/r/tokens.json"
  ],
  "files": [
    {
      "path": "packages/metalui/src/components/folder/folder.tsx",
      "type": "registry:ui",
      "target": "components/metalui/folder/folder.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { Label } from '../label/label';\n\n/* ─────────────────────────────────────────────────────────\n * FOLDER (the Soft Hardware sheet's stack folder)\n *\n * A thing on the canvas that holds blocks and takes little space. It is the closed state of a\n * container; unfolded, the same container is a region washed in the folder's colour.\n *\n *   shape     the back (translucent paper, no blur: the canvas shows softly through) and the flap\n *             taper slightly toward the bottom, like a pocket; their shadows are separate\n *             blurred layers, since the tapered outline is a clip. The flap is frosted\n *             glass: a clipped blur layer of its own (translateZ, back face hidden) under a\n *             see-through fill, so the blur keeps the pocket's shape while the flap hinges\n *   rest      the back panel with its tab; up to six of its blocks peek up as cards, each posed\n *             by its place in the pile (back to front: leaning 10° to -5°, 10 up; the sheet's\n *             poses exactly at three); more cards widen the fan a little\n *   hover     (and keyboard focus) the cards rise and fan (-30 to -44, 14° to -9°): the back\n *             lifts first, the front last (45 ms apart), on the object spring; the flap tips\n *             back to 45° on the hinge spring. Leaving, the front settles first\n *   open      a block is dragged over it: the cards rise out (-86 to -106, 18° to -14°) and the\n *             flap opens to 55°, saying \"drop in here\"; also the first beat of unfolding\n *   joining   a block is put in: it is added at the front and the others re-spread to make\n *             room, all still in view (past six, the oldest slides down into the pocket); its\n *             slot waits empty (`waiting`) until the block lands there; then the fan settles\n *   landing   the block is in: the flap swings shut past rest and settles (hinge spring);\n *             the count changes at once\n *   empty     no cards; the count reads 0\n *   colour    six soft paper stocks (neutral, red, amber, green, blue, violet); the frosted flap\n *             takes the colour through its blur. Neutral is the sheet exactly.\n *   press     double-click or Enter unfolds it (the host morphs it into its region)\n * Reduce Motion: poses change at once, no bounce.\n * ───────────────────────────────────────────────────────── */\n\nexport type FolderHue = 'neutral' | 'red' | 'amber' | 'green' | 'blue' | 'violet';\n\nexport interface FolderPeek {\n  /** Who it is, so a card glides to its new slot when another joins (else its position is used). */\n  id?: string;\n  /** Held empty: a thing is on its way into this slot; it becomes the card when it arrives. */\n  waiting?: boolean;\n  /** The card's picture: an image URL, or any CSS background (a gradient for a colour or a link's tint). */\n  thumb?: string;\n  /** A link card shows a blue line under its title. */\n  link?: boolean;\n}\n\nexport interface FolderProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n  name: string;\n  /** How many blocks it holds. */\n  count: number;\n  /** Up to three of its blocks, front last. */\n  peeks?: FolderPeek[];\n  hue?: FolderHue;\n  /** Opened wide: a block is dragged over it, or it is starting to unfold. */\n  open?: boolean;\n  /** Change this (a counter or a timestamp) each time a block lands in it: the flap swings shut. */\n  landed?: number;\n  /** Double-click or Enter: unfold it into its region. */\n  onUnfold?: () => void;\n}\n\nconst ROOT = 'mu-folder folder';\nconst SHADE = 'folder-shade';\nconst BACK = 'mu-folder-back folder-back';\nconst FLAP_SHADE = 'folder-flap-shade';\nconst CARD = 'folder-card';\n/** How many cards peek at once (the recipe's fan-max); an older one slides down into the pocket. */\nconst FAN_MAX = 6;\nconst THUMB = 'folder-thumb';\nconst LINE = 'folder-line';\nconst FLAP = 'mu-folder-flap folder-flap';\nconst LAND = 'folder-land reduced-motion:animate-none';\nconst LABEL = 'folder-label';\nconst COUNT = 'mu-folder-count folder-count';\n\n/* The outline, built once from the folder recipe's numbers (220 wide; back 150 tall + a 16 tab,\n * tapering 10 per side; flap 106 tall, tapering 12; radius 26). One path drives the clip of the\n * frosted body, its hairline edge and top light, and its shadow, so they always agree. */\nconst W = 220, R = 26;\nconst f2 = (n: number) => +n.toFixed(2);\nfunction pocket(h: number, d: number, top = 0) {\n  const k = R / h, y0 = top, y1 = top + h;\n  return `M${R} ${y0}H${W - R}Q${W} ${y0} ${f2(W - d * k)} ${y0 + R}L${f2(W - d + d * k)} ${y1 - R}Q${W - d} ${y1} ${W - d - R} ${y1}`\n    + `H${d + R}Q${d} ${y1} ${f2(d - d * k)} ${y1 - R}L${f2(d * k)} ${y0 + R}Q0 ${y0} ${R} ${y0}Z`;\n}\nfunction withTab(h: number, d: number, rise: number, tab: number) {\n  const k = R / h, y0 = rise, y1 = rise + h, tr = 18;\n  return `M0 ${tr}Q0 0 ${tr} 0H${tab - 14}C${tab - 4} 0 ${tab} ${y0} ${tab + 12} ${y0}`\n    + `H${W - R}Q${W} ${y0} ${f2(W - d * k)} ${y0 + R}L${f2(W - d + d * k)} ${y1 - R}Q${W - d} ${y1} ${W - d - R} ${y1}`\n    + `H${d + R}Q${d} ${y1} ${f2(d - d * k)} ${y1 - R}L0 ${y0 + R}Z`;\n}\nconst BACK_D = withTab(150, 10, 16, 92);\nconst FLAP_D = pocket(106, 12);\nconst clip = (d: string): React.CSSProperties => ({ clipPath: `path('${d}')`, WebkitClipPath: `path('${d}')` });\nconst EDGE = 'folder-edge';\n\nfunction Edge({ d, h, fill }: { d: string; h: number; fill?: boolean }) {\n  return (\n    <svg aria-hidden className={EDGE} viewBox={`0 0 ${W} ${h}`} preserveAspectRatio=\"none\">\n      {fill && <path d={d} className=\"folder-flap-fill\" />}\n      <path d={d} className=\"folder-edge-light\" style={clip(d)} />\n      <path d={d} className=\"folder-edge-line\" />\n    </svg>\n  );\n}\n\nconst bg = (thumb?: string) => (!thumb ? undefined : /^(url\\(|[a-z-]+-gradient\\(|#|rgb|hsl)/.test(thumb) ? thumb : `url(\"${thumb}\")`);\n\n/** A folder on the canvas. */\nexport const Folder = React.forwardRef<HTMLDivElement, FolderProps>(function Folder(\n  { name, count, peeks = [], hue = 'neutral', open, landed, onUnfold, className, onKeyDown, onDoubleClick, ...props },\n  ref,\n) {\n  // Up to six cards peek, spread by their place in the pile: t runs 0 (back) to 1 (front), and\n  // the fan widens with the count (k), so a new card joins the others instead of replacing one.\n  // One more, the oldest, slides down into the pocket as it leaves the fan.\n  const shown = peeks.slice(-(FAN_MAX + 1));\n  const pocketed = shown.length > FAN_MAX ? 1 : 0;\n  const n = shown.length - pocketed;\n  const k = Math.min(1.35, Math.max(0.8, Math.sqrt(n / 3)));\n  const fan = (i: number) => {\n    const at = i - pocketed;\n    return { '--t': n > 1 ? at / (n - 1) : 1, '--n': n, '--k': k, zIndex: i + 1 } as React.CSSProperties;\n  };\n  return (\n    <div\n      ref={ref}\n      role=\"button\"\n      tabIndex={0}\n      aria-label={`${name}, folder of ${count} ${count === 1 ? 'block' : 'blocks'}. Press Enter to open.`}\n      data-hue={hue}\n      data-open={open ? '' : undefined}\n      className={className ? `${ROOT} ${className}` : ROOT}\n      onDoubleClick={(e) => { onDoubleClick?.(e); onUnfold?.(); }}\n      onKeyDown={(e) => { onKeyDown?.(e); if (e.key === 'Enter') { e.preventDefault(); onUnfold?.(); } }}\n      {...props}\n    >\n      <div aria-hidden className={SHADE}><div className=\"folder-shade-body\" style={clip(BACK_D)} /></div>\n      <div aria-hidden className={`${BACK} folder-layer`} style={clip(BACK_D)} />\n      <div aria-hidden className=\"folder-frame\"><Edge d={BACK_D} h={166} /></div>\n      {shown.map((c, i) => (\n        <div\n          key={c.id ?? i}\n          aria-hidden\n          data-card={c.id}\n          className={[CARD, i < pocketed && 'folder-card-pocketed', c.waiting && 'folder-card-waiting'].filter(Boolean).join(' ')}\n          style={fan(i)}\n        >\n          <div className={THUMB} style={{ background: bg(c.thumb) }} />\n          <i className={`${LINE} folder-line-lg`} style={{ width: '70%' }} />\n          <i className={c.link ? `${LINE} folder-line-blue` : LINE} />\n          <i className={LINE} style={{ width: '60%' }} />\n        </div>\n      ))}\n      <div aria-hidden className={FLAP_SHADE}><div className=\"folder-shade-body\" style={clip(FLAP_D)} /></div>\n      <div key={landed} aria-hidden className={landed ? `${FLAP} ${LAND}` : FLAP}>\n        {/* The frost is its own clipped layer, so the blur keeps the pocket's shape while the flap\n            hinges. The flap stays flat (no preserve-3d): in a 3D scene the cards' depth would beat\n            the flap's z-index and paint them over it. */}\n        <div className=\"folder-frost\" style={clip(FLAP_D)} />\n        <Edge d={FLAP_D} h={106} fill />\n        <div className=\"folder-flap-content\">\n        <div className={LABEL}>\n          <Label variant=\"title\" as=\"b\">{name}</Label>\n          <Label variant=\"engraved\">{`Folder · ${count} ${count === 1 ? 'block' : 'blocks'}`}</Label>\n        </div>\n        <span className={COUNT}>{count}</span>\n        </div>\n      </div>\n    </div>\n  );\n});\n"
    }
  ],
  "docs": "Agent guide: https://metalui.dev/r/folder.md. SwiftUI: MetalFolder in the MetalUI Swift package."
}
