{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "file-tree",
  "title": "File Tree",
  "author": "Noppakorn Kaewsalabnil (https://www.pungrumpy.com)",
  "description": "A directory listing with collapsible folders, built from nested disclosures rather than a tree role.",
  "dependencies": [],
  "files": [
    {
      "path": "components/file-tree.tsx",
      "content": "\"use client\";\n\nimport { useId, useState } from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface FileTreeProps {\n  readonly children: ReactNode;\n  readonly className?: string;\n}\n\nexport const FileTree = ({ children, className }: FileTreeProps) => (\n  <ul\n    className={cn(\n      \"m-0 flex w-full list-none flex-col gap-0 p-0 font-mono text-gray-1000 text-sm [font-variant-ligatures:none]\",\n      className,\n    )}\n  >\n    {children}\n  </ul>\n);\n\ninterface FolderProps {\n  readonly name: string;\n  /** Open on mount. Use it for the path you want the reader to look at. */\n  readonly defaultOpen?: boolean;\n  readonly children?: ReactNode;\n}\n\nexport const Folder = ({ name, defaultOpen = false, children }: FolderProps) => {\n  const [open, setOpen] = useState(defaultOpen);\n  const id = useId();\n\n  return (\n    <li>\n      <button\n        aria-controls={children ? id : undefined}\n        aria-expanded={open}\n        className=\"flex min-h-7 w-full cursor-pointer touch-manipulation items-center gap-1.5 rounded-sm py-1 pe-2 ps-1 text-start text-gray-1000 focus-visible:-outline-offset-2 focus-visible:outline-2 focus-visible:outline-gray-1000 focus-visible:outline-solid [@media(hover:hover)_and_(pointer:fine)]:hover:bg-gray-100\"\n        onClick={() => setOpen((current) => !current)}\n        type=\"button\"\n      >\n        <svg\n          aria-hidden=\"true\"\n          className={cn(\n            \"size-4 shrink-0 text-gray-700 transition-transform duration-200 ease-[cubic-bezier(0.2,0,0,1)] motion-reduce:transition-none\",\n            open && \"rotate-90\",\n          )}\n          fill=\"none\"\n          viewBox=\"0 0 16 16\"\n        >\n          <path\n            clipRule=\"evenodd\"\n            d=\"M6.75 3.94L7.28 4.47L10.1 7.29C10.49 7.68 10.49 8.32 10.1 8.71L7.28 11.53L6.75 12.06L5.69 11L6.22 10.47L8.69 8L6.22 5.53L5.69 5L6.75 3.94Z\"\n            fill=\"currentColor\"\n            fillRule=\"evenodd\"\n          />\n        </svg>\n\n        <span aria-hidden=\"true\" className=\"relative inline-flex size-4 shrink-0 text-gray-700\">\n          <svg\n            className={cn(\n              \"absolute inset-0 size-4 transition-[opacity,scale,filter] duration-200 ease-[cubic-bezier(0.2,0,0,1)] motion-reduce:transition-none\",\n              open ? \"scale-[0.25] opacity-0 blur-xs\" : \"scale-100 opacity-100 blur-0\",\n            )}\n            fill=\"none\"\n            viewBox=\"0 0 16 16\"\n          >\n            <path\n              d=\"M1.5 3.5C1.5 2.95 1.95 2.5 2.5 2.5H6.09C6.36 2.5 6.61 2.61 6.8 2.79L8 4H13C13.55 4 14 4.45 14 5V12C14 12.55 13.55 13 13 13H2.5C1.95 13 1.5 12.55 1.5 12V3.5Z\"\n              fill=\"currentColor\"\n            />\n          </svg>\n          <svg\n            className={cn(\n              \"absolute inset-0 size-4 transition-[opacity,scale,filter] duration-200 ease-[cubic-bezier(0.2,0,0,1)] motion-reduce:transition-none\",\n              open ? \"scale-100 opacity-100 blur-0\" : \"scale-[0.25] opacity-0 blur-xs\",\n            )}\n            fill=\"none\"\n            viewBox=\"0 0 16 16\"\n          >\n            <path\n              d=\"M1.5 3.5C1.5 2.95 1.95 2.5 2.5 2.5H6.09C6.36 2.5 6.61 2.61 6.8 2.79L8 4H12.5C13.05 4 13.5 4.45 13.5 5V5.5H4.5C4.06 5.5 3.67 5.79 3.55 6.21L2.2 11H2C1.72 11 1.5 10.78 1.5 10.5V3.5ZM3.5 12.5L4.86 7.71C4.92 7.5 5.11 7.36 5.33 7.36H14.2C14.53 7.36 14.77 7.68 14.68 8L13.5 12.14C13.38 12.56 12.99 12.85 12.55 12.85H3.5V12.5Z\"\n              fill=\"currentColor\"\n            />\n          </svg>\n        </span>\n        <span className=\"truncate\" title={name}>\n          {name}\n        </span>\n      </button>\n      {children ? (\n        <ul\n          className=\"m-0 ms-3 flex list-none flex-col gap-0 border-gray-200 border-s ps-2\"\n          hidden={!open}\n          id={id}\n        >\n          {children}\n        </ul>\n      ) : null}\n    </li>\n  );\n};\n\nconst FileGlyph = () => (\n  <svg aria-hidden=\"true\" className=\"size-4 shrink-0 text-gray-700\" fill=\"none\" viewBox=\"0 0 16 16\">\n    <path\n      clipRule=\"evenodd\"\n      d=\"M3.5 2C3.5 1.72 3.72 1.5 4 1.5H9.25L13 5.25V14C13 14.28 12.78 14.5 12.5 14.5H4C3.72 14.5 3.5 14.28 3.5 14V2ZM9 2.75V5.5H11.75L9 2.75Z\"\n      fill=\"currentColor\"\n    />\n  </svg>\n);\n\ninterface FileProps {\n  readonly name: string;\n  /** Makes the row a link. Without it the name is text, with nothing to activate. */\n  readonly href?: string;\n}\n\nexport const File = ({ name, href }: FileProps) => (\n  <li>\n    {href ? (\n      <a\n        className=\"flex min-h-7 w-full cursor-pointer touch-manipulation items-center gap-1.5 rounded-sm py-1 pe-2 ps-1 text-start text-gray-1000 no-underline focus-visible:-outline-offset-2 focus-visible:outline-2 focus-visible:outline-gray-1000 focus-visible:outline-solid [@media(hover:hover)_and_(pointer:fine)]:hover:bg-gray-100\"\n        href={href}\n      >\n        <span aria-hidden=\"true\" className=\"size-4 shrink-0\" />\n        <FileGlyph />\n        <span className=\"truncate\" title={name}>\n          {name}\n        </span>\n      </a>\n    ) : (\n      <span className=\"flex min-h-7 w-full items-center gap-1.5 rounded-sm py-1 pe-2 ps-1 text-start text-gray-1000\">\n        <span aria-hidden=\"true\" className=\"size-4 shrink-0\" />\n        <FileGlyph />\n        <span className=\"truncate\" title={name}>\n          {name}\n        </span>\n      </span>\n    )}\n  </li>\n);\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}