{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "file-manager",
  "title": "File Manager",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/file-manager/file-manager.component.ts",
      "content": "// Storage/files page with breadcrumbs, search, grid/list view toggle, upload action,\n// and a storage meter over hardcoded folders and files. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core'\nimport {\n  Download,\n  FileArchive,\n  FileImage,\n  FileSpreadsheet,\n  FileText,\n  FileVideo,\n  Folder,\n  LayoutGrid,\n  List,\n  LucideAngularModule,\n  MoreVertical,\n  Pencil,\n  Search,\n  Share2,\n  Trash2,\n  Upload,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport {\n  UiBreadcrumbComponent,\n  UiBreadcrumbItemComponent,\n  UiBreadcrumbLinkComponent,\n  UiBreadcrumbListComponent,\n  UiBreadcrumbPageComponent,\n  UiBreadcrumbSeparatorComponent,\n} from '@/ui/breadcrumb/breadcrumb.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiDropdownMenuComponent,\n  UiDropdownMenuContentComponent,\n  UiDropdownMenuItemComponent,\n  UiDropdownMenuSeparatorComponent,\n  UiDropdownMenuTriggerComponent,\n} from '@/ui/dropdown-menu/dropdown-menu.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport { UiProgressComponent } from '@/ui/progress/progress.component'\nimport { UiToggleGroupComponent, UiToggleGroupItemComponent } from '@/ui/toggle-group/toggle-group.component'\n\nexport type FileKind = 'pdf' | 'image' | 'sheet' | 'video' | 'archive'\n\nexport interface FolderItem {\n  id: string\n  name: string\n  count: number\n}\n\nexport interface FileItem {\n  id: string\n  name: string\n  kind: FileKind\n  size: string\n  modified: string\n}\n\nexport type FileManagerRow = (FolderItem & { isFolder: true }) | (FileItem & { isFolder: false })\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\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-file-manager, [ui-file-manager]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBreadcrumbComponent,\n    UiBreadcrumbItemComponent,\n    UiBreadcrumbLinkComponent,\n    UiBreadcrumbListComponent,\n    UiBreadcrumbPageComponent,\n    UiBreadcrumbSeparatorComponent,\n    UiButtonComponent,\n    UiDropdownMenuComponent,\n    UiDropdownMenuContentComponent,\n    UiDropdownMenuItemComponent,\n    UiDropdownMenuSeparatorComponent,\n    UiDropdownMenuTriggerComponent,\n    UiInputComponent,\n    UiProgressComponent,\n    UiToggleGroupComponent,\n    UiToggleGroupItemComponent,\n  ],\n  template: `\n    <div data-slot=\"file-manager\" [class]=\"rootClass\">\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          <ui-breadcrumb>\n            <ui-breadcrumb-list>\n              <ui-breadcrumb-item>\n                <ui-breadcrumb-link href=\"#\" class=\"inline-flex min-h-6 items-center\">Home</ui-breadcrumb-link>\n              </ui-breadcrumb-item>\n              <ui-breadcrumb-separator />\n              <ui-breadcrumb-item>\n                <ui-breadcrumb-link href=\"#\" class=\"inline-flex min-h-6 items-center\">Documents</ui-breadcrumb-link>\n              </ui-breadcrumb-item>\n              <ui-breadcrumb-separator />\n              <ui-breadcrumb-item>\n                <ui-breadcrumb-page>Design</ui-breadcrumb-page>\n              </ui-breadcrumb-item>\n            </ui-breadcrumb-list>\n          </ui-breadcrumb>\n\n          <div class=\"relative ml-auto w-full max-w-56 sm:w-auto\">\n            <lucide-icon\n              [img]=\"Search\"\n              class=\"text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2\"\n            />\n            <ui-input type=\"search\" placeholder=\"Search files…\" class=\"pl-8\" />\n          </div>\n\n          <ui-toggle-group\n            type=\"single\"\n            size=\"sm\"\n            [value]=\"view()\"\n            (valueChange)=\"setView($event)\"\n            class=\"bg-muted items-center gap-0.5 rounded-md p-0.5\"\n            aria-label=\"View mode\"\n          >\n            <button\n              ui-toggle-group-item\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              <lucide-icon [img]=\"LayoutGrid\" class=\"size-4\" />\n            </button>\n            <button\n              ui-toggle-group-item\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              <lucide-icon [img]=\"List\" class=\"size-4\" />\n            </button>\n          </ui-toggle-group>\n\n          <button ui-button size=\"sm\">\n            <lucide-icon [img]=\"Upload\" 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          <div ui-progress [value]=\"36.4\" class=\"h-1.5 min-w-40 flex-1 sm:max-w-xs\" aria-label=\"Storage used\"></div>\n          <p class=\"text-muted-foreground text-xs\">18.2 GB of 50 GB used</p>\n        </div>\n\n        @if (view() === 'grid') {\n          <div class=\"grid grid-cols-2 gap-3 p-4 sm:grid-cols-3 lg:grid-cols-4\">\n            @for (folder of folders; track folder.id) {\n              <button\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                  <lucide-icon [img]=\"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\n            @for (file of files; track file.id) {\n              <button\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                  <lucide-icon [img]=\"kindIcon(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            }\n          </div>\n        } @else {\n          <div 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\"></span>\n            </div>\n            <ul role=\"rowgroup\" class=\"divide-y\">\n              @for (item of rows(); track item.id) {\n                <li role=\"row\" class=\"hover:bg-muted/50 flex items-center gap-3 px-4 py-2.5 transition-colors\">\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                    @if (item.isFolder) {\n                      <lucide-icon [img]=\"Folder\" class=\"size-4\" />\n                    } @else {\n                      <lucide-icon [img]=\"kindIcon(item.kind)\" class=\"size-4\" />\n                    }\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.count + ' items' : item.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.modified }}\n                  </span>\n                  <ui-dropdown-menu>\n                    <button\n                      ui-button\n                      ui-dropdown-menu-trigger\n                      variant=\"ghost\"\n                      size=\"icon-sm\"\n                      class=\"shrink-0\"\n                      [attr.aria-label]=\"'Actions for ' + item.name\"\n                    >\n                      <lucide-icon [img]=\"MoreVertical\" class=\"size-4\" />\n                    </button>\n                    <ui-dropdown-menu-content align=\"end\">\n                      <ui-dropdown-menu-item>\n                        <lucide-icon [img]=\"Download\" class=\"size-4\" />\n                        Download\n                      </ui-dropdown-menu-item>\n                      <ui-dropdown-menu-item>\n                        <lucide-icon [img]=\"Pencil\" class=\"size-4\" />\n                        Rename\n                      </ui-dropdown-menu-item>\n                      <ui-dropdown-menu-item>\n                        <lucide-icon [img]=\"Share2\" class=\"size-4\" />\n                        Share link\n                      </ui-dropdown-menu-item>\n                      <ui-dropdown-menu-separator />\n                      <ui-dropdown-menu-item variant=\"destructive\">\n                        <lucide-icon [img]=\"Trash2\" class=\"size-4\" />\n                        Delete\n                      </ui-dropdown-menu-item>\n                    </ui-dropdown-menu-content>\n                  </ui-dropdown-menu>\n                </li>\n              }\n            </ul>\n          </div>\n        }\n      </div>\n    </div>\n  `,\n})\nexport class UiFileManagerComponent {\n  protected readonly Download = Download\n  protected readonly Folder = Folder\n  protected readonly LayoutGrid = LayoutGrid\n  protected readonly List = List\n  protected readonly MoreVertical = MoreVertical\n  protected readonly Pencil = Pencil\n  protected readonly Search = Search\n  protected readonly Share2 = Share2\n  protected readonly Trash2 = Trash2\n  protected readonly Upload = Upload\n\n  @Input() set variant(v: 'grid' | 'list') {\n    if (v === 'grid' || v === 'list') this.view.set(v)\n  }\n  @Input('class') className?: string\n\n  readonly view = signal<'grid' | 'list'>('grid')\n\n  protected readonly folders: FolderItem[] = FOLDERS\n  protected readonly files: FileItem[] = FILES\n\n  get rootClass(): string {\n    return cn('w-full', this.className)\n  }\n\n  rows(): FileManagerRow[] {\n    return [\n      ...this.folders.map((f) => ({ ...f, isFolder: true as const })),\n      ...this.files.map((f) => ({ ...f, isFolder: false as const })),\n    ]\n  }\n\n  kindIcon(kind: FileKind): typeof FileText {\n    switch (kind) {\n      case 'image':\n        return FileImage\n      case 'sheet':\n        return FileSpreadsheet\n      case 'video':\n        return FileVideo\n      case 'archive':\n        return FileArchive\n      default:\n        return FileText\n    }\n  }\n\n  setView(v: unknown): void {\n    if (v === 'grid' || v === 'list') this.view.set(v)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/file-manager/file-manager.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/file-manager/index.ts",
      "content": "export {\n  UiFileManagerComponent,\n  type FileKind,\n  type FolderItem,\n  type FileItem,\n  type FileManagerRow,\n} from './file-manager.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/file-manager/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/breadcrumb.json",
    "https://uipkge.dev/r/angular/progress.json",
    "https://uipkge.dev/r/angular/dropdown-menu.json",
    "https://uipkge.dev/r/angular/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"
  ]
}