{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "file-manager",
  "title": "File Manager",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/file-manager/FileManager.vue",
      "content": "<script setup lang=\"ts\">\nimport { ref, watch } from 'vue'\nimport type { HTMLAttributes } from 'vue'\nimport {\n  Download,\n  FileArchive,\n  FileImage,\n  FileSpreadsheet,\n  FileText,\n  FileVideo,\n  Folder,\n  LayoutGrid,\n  List,\n  MoreVertical,\n  Pencil,\n  Search,\n  Share2,\n  Trash2,\n  Upload,\n} from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport {\n  Breadcrumb,\n  BreadcrumbItem,\n  BreadcrumbLink,\n  BreadcrumbList,\n  BreadcrumbPage,\n  BreadcrumbSeparator,\n} from '@/components/ui/breadcrumb'\nimport { Button } from '@/components/ui/button'\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu'\nimport { Input } from '@/components/ui/input'\nimport { Progress } from '@/components/ui/progress'\nimport { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'\n\ntype FileKind = 'pdf' | 'image' | 'sheet' | 'video' | 'archive'\n\ninterface FolderItem {\n  id: string\n  name: string\n  count: number\n}\n\ninterface FileItem {\n  id: string\n  name: string\n  kind: FileKind\n  size: string\n  modified: string\n}\n\nconst props = withDefaults(\n  defineProps<{\n    variant?: 'grid' | 'list'\n    class?: HTMLAttributes['class']\n  }>(),\n  {\n    variant: 'grid',\n  },\n)\n\nconst view = ref<'grid' | 'list'>(props.variant)\n\nwatch(\n  () => props.variant,\n  (v) => {\n    if (v) view.value = v\n  },\n)\n\nconst folders: FolderItem[] = [\n  { id: 'folder-design-assets', name: 'Design Assets', count: 24 },\n  { id: 'folder-client-files', name: 'Client Files', count: 12 },\n  { id: 'folder-brand-kit', name: 'Brand Kit', count: 8 },\n  { id: 'folder-archive', name: 'Archive', count: 56 },\n]\n\nconst files: FileItem[] = [\n  { id: 'file-brand-guidelines', name: 'brand-guidelines.pdf', kind: 'pdf', size: '4.2 MB', modified: 'Mar 14, 2026' },\n  { id: 'file-homepage-hero', name: 'homepage-hero.png', kind: 'image', size: '2.8 MB', modified: 'Mar 12, 2026' },\n  { id: 'file-logo-final', name: 'logo-final.svg', kind: 'image', size: '48 KB', modified: 'Mar 10, 2026' },\n  { id: 'file-pricing-model', name: 'pricing-model.xlsx', kind: 'sheet', size: '316 KB', modified: 'Mar 8, 2026' },\n  { id: 'file-launch-video', name: 'launch-video.mp4', kind: 'video', size: '184 MB', modified: 'Mar 5, 2026' },\n  { id: 'file-app-iconset', name: 'app-iconset.zip', kind: 'archive', size: '12.4 MB', modified: 'Feb 27, 2026' },\n]\n\nconst fileKindIcons: Record<FileKind, typeof FileText> = {\n  pdf: FileText,\n  image: FileImage,\n  sheet: FileSpreadsheet,\n  video: FileVideo,\n  archive: FileArchive,\n}\n\nfunction setView(v: unknown) {\n  if (v === 'grid' || v === 'list') view.value = v\n}\n</script>\n\n<template>\n  <div data-slot=\"file-manager\" :class=\"cn('w-full', props.class)\">\n    <div class=\"bg-card text-card-foreground rounded-xl border shadow-xs\">\n      <div class=\"flex flex-wrap items-center gap-3 border-b p-4\">\n        <Breadcrumb>\n          <BreadcrumbList>\n            <BreadcrumbItem>\n              <BreadcrumbLink href=\"#\" class=\"inline-flex min-h-6 items-center\">Home</BreadcrumbLink>\n            </BreadcrumbItem>\n            <BreadcrumbSeparator />\n            <BreadcrumbItem>\n              <BreadcrumbLink href=\"#\" class=\"inline-flex min-h-6 items-center\">Documents</BreadcrumbLink>\n            </BreadcrumbItem>\n            <BreadcrumbSeparator />\n            <BreadcrumbItem>\n              <BreadcrumbPage>Design</BreadcrumbPage>\n            </BreadcrumbItem>\n          </BreadcrumbList>\n        </Breadcrumb>\n\n        <div class=\"relative ml-auto w-full max-w-56 sm:w-auto\">\n          <Search\n            aria-hidden=\"true\"\n            class=\"text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2\"\n          />\n          <Input type=\"search\" placeholder=\"Search files…\" class=\"pl-8\" />\n        </div>\n\n        <ToggleGroup\n          type=\"single\"\n          size=\"sm\"\n          :model-value=\"view\"\n          class=\"bg-muted items-center gap-0.5 rounded-md p-0.5\"\n          aria-label=\"View mode\"\n          @update:model-value=\"setView\"\n        >\n          <ToggleGroupItem\n            value=\"grid\"\n            class=\"data-[state=on]:bg-background size-7 rounded-sm p-0 data-[state=on]:shadow-sm\"\n            title=\"Grid view\"\n            aria-label=\"Grid view\"\n          >\n            <LayoutGrid aria-hidden=\"true\" class=\"size-4\" />\n          </ToggleGroupItem>\n          <ToggleGroupItem\n            value=\"list\"\n            class=\"data-[state=on]:bg-background size-7 rounded-sm p-0 data-[state=on]:shadow-sm\"\n            title=\"List view\"\n            aria-label=\"List view\"\n          >\n            <List aria-hidden=\"true\" class=\"size-4\" />\n          </ToggleGroupItem>\n        </ToggleGroup>\n\n        <Button size=\"sm\">\n          <Upload aria-hidden=\"true\" class=\"size-4\" />\n          Upload\n        </Button>\n      </div>\n\n      <div class=\"flex flex-wrap items-center gap-x-4 gap-y-2 border-b px-4 py-3\">\n        <Progress :model-value=\"36.4\" class=\"h-1.5 min-w-40 flex-1 sm:max-w-xs\" aria-label=\"Storage used\" />\n        <p class=\"text-muted-foreground text-xs\">18.2 GB of 50 GB used</p>\n      </div>\n\n      <div v-if=\"view === 'grid'\" class=\"grid grid-cols-2 gap-3 p-4 sm:grid-cols-3 lg:grid-cols-4\">\n        <button\n          v-for=\"folder in folders\"\n          :key=\"folder.id\"\n          type=\"button\"\n          class=\"hover:bg-muted/50 focus-visible:ring-ring flex flex-col items-start gap-3 rounded-lg border p-3 text-left shadow-xs transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n        >\n          <span class=\"bg-warning/10 text-warning grid size-9 place-items-center rounded-md\">\n            <Folder class=\"size-5\" />\n          </span>\n          <span class=\"w-full min-w-0\">\n            <span class=\"block truncate text-sm font-medium\">{{ folder.name }}</span>\n            <span class=\"text-muted-foreground block text-xs\">{{ folder.count }} items</span>\n          </span>\n        </button>\n\n        <button\n          v-for=\"file in files\"\n          :key=\"file.id\"\n          type=\"button\"\n          class=\"hover:bg-muted/50 focus-visible:ring-ring group flex flex-col items-start gap-3 rounded-lg border p-3 text-left shadow-xs transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n        >\n          <span\n            class=\"bg-muted text-muted-foreground group-hover:bg-background grid size-9 place-items-center rounded-md transition-colors\"\n          >\n            <component :is=\"fileKindIcons[file.kind]\" class=\"size-5\" />\n          </span>\n          <span class=\"min-w-0\">\n            <span class=\"block truncate text-sm font-medium\">{{ file.name }}</span>\n            <span class=\"text-muted-foreground block text-xs\">{{ file.size }} · {{ file.modified }}</span>\n          </span>\n        </button>\n      </div>\n\n      <div v-else role=\"table\" aria-label=\"Files and folders\">\n        <div role=\"row\" class=\"text-muted-foreground hidden border-b px-4 py-2 text-xs sm:flex\">\n          <span role=\"columnheader\" class=\"flex-1 pl-9\">Name</span>\n          <span role=\"columnheader\" class=\"w-20 text-right\">Size</span>\n          <span role=\"columnheader\" class=\"w-28 text-right\">Modified</span>\n          <span class=\"w-9\" />\n        </div>\n        <ul role=\"rowgroup\" class=\"divide-y\">\n          <li\n            v-for=\"item in [\n              ...folders.map((f) => ({ ...f, isFolder: true })),\n              ...files.map((f) => ({ ...f, isFolder: false })),\n            ]\"\n            :key=\"item.id\"\n            role=\"row\"\n            class=\"hover:bg-muted/50 flex items-center gap-3 px-4 py-2.5 transition-colors\"\n          >\n            <span\n              class=\"grid size-6 shrink-0 place-items-center rounded-md\"\n              :class=\"item.isFolder ? 'bg-warning/10 text-warning' : 'bg-muted text-muted-foreground'\"\n            >\n              <Folder v-if=\"item.isFolder\" class=\"size-4\" />\n              <component :is=\"fileKindIcons[(item as FileItem).kind]\" v-else class=\"size-4\" />\n            </span>\n            <span role=\"cell\" class=\"min-w-0 flex-1 truncate text-sm font-medium\">{{ item.name }}</span>\n            <span role=\"cell\" class=\"text-muted-foreground hidden w-20 shrink-0 text-right text-xs sm:block\">\n              {{ item.isFolder ? `${(item as FolderItem).count} items` : (item as FileItem).size }}\n            </span>\n            <span role=\"cell\" class=\"text-muted-foreground hidden w-28 shrink-0 text-right text-xs md:block\">\n              {{ item.isFolder ? '—' : (item as FileItem).modified }}\n            </span>\n            <DropdownMenu>\n              <DropdownMenuTrigger as-child>\n                <Button variant=\"ghost\" size=\"icon-sm\" class=\"shrink-0\" :aria-label=\"`Actions for ${item.name}`\">\n                  <MoreVertical class=\"size-4\" />\n                </Button>\n              </DropdownMenuTrigger>\n              <DropdownMenuContent align=\"end\">\n                <DropdownMenuItem><Download class=\"size-4\" />Download</DropdownMenuItem>\n                <DropdownMenuItem><Pencil class=\"size-4\" />Rename</DropdownMenuItem>\n                <DropdownMenuItem><Share2 class=\"size-4\" />Share link</DropdownMenuItem>\n                <DropdownMenuSeparator />\n                <DropdownMenuItem variant=\"destructive\"><Trash2 class=\"size-4\" />Delete</DropdownMenuItem>\n              </DropdownMenuContent>\n            </DropdownMenu>\n          </li>\n        </ul>\n      </div>\n    </div>\n  </div>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/FileManager.vue"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/button.json",
    "https://uipkge.dev/r/vue/input.json",
    "https://uipkge.dev/r/vue/breadcrumb.json",
    "https://uipkge.dev/r/vue/progress.json",
    "https://uipkge.dev/r/vue/dropdown-menu.json",
    "https://uipkge.dev/r/vue/toggle-group.json"
  ],
  "description": "Storage/files page with breadcrumbs, search, grid/list view toggle, upload action, and a storage meter over hardcoded folders and files.",
  "categories": [
    "media",
    "app"
  ]
}