{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"list-03","type":"registry:component","title":"File Management List","description":"File management list with type-based icons, star functionality, sharing status, tags, and comprehensive action menus. Features responsive design with mobile-friendly dropdown actions, file type detection, and customizable display options.","dependencies":["lucide-react"],"registryDependencies":["avatar","badge","button","card","dropdown-menu","separator"],"files":[{"path":"src/registry/blocks/application/data-display/lists/list-03.tsx","content":"\"use client\";\n\nimport {\n  Archive,\n  Download,\n  Edit3,\n  Eye,\n  FileText,\n  ImageIcon,\n  type LucideIcon,\n  MoreHorizontal,\n  Music,\n  Share2,\n  Star,\n  Trash2,\n  Video,\n} from \"lucide-react\";\nimport { useState } from \"react\";\nimport { cn } from \"@/registry/lib/utils\";\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/registry/ui/avatar\";\nimport { Badge } from \"@/registry/ui/badge\";\nimport { Button } from \"@/registry/ui/button\";\nimport { Card, CardContent } from \"@/registry/ui/card\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/registry/ui/dropdown-menu\";\nimport { Separator } from \"@/registry/ui/separator\";\n\ninterface FileItem {\n  id: number;\n  name: string;\n  type: \"pdf\" | \"document\" | \"image\" | \"video\" | \"audio\" | \"archive\";\n  size: string;\n  modified: string;\n  modifiedBy: {\n    name: string;\n    avatar: string;\n  };\n  shared: boolean;\n  starred: boolean;\n  thumbnail: string;\n  tags: string[];\n}\n\nexport interface List03Props {\n  className?: string;\n}\n\nconst content: {\n  files: FileItem[];\n  emptyState: { message: string; icon: LucideIcon };\n} = {\n  files: [\n    {\n      id: 1,\n      name: \"Project Proposal Q4 2024.pdf\",\n      type: \"pdf\" as const,\n      size: \"2.4 MB\",\n      modified: \"2 hours ago\",\n      modifiedBy: {\n        name: \"Sarah Johnson\",\n        avatar: \"https://randomuser.me/api/portraits/women/1.jpg\",\n      },\n      shared: true,\n      starred: true,\n      thumbnail: \"https://randomuser.me/api/portraits/women/1.jpg\",\n      tags: [\"Important\", \"Q4\"],\n    },\n    {\n      id: 2,\n      name: \"Team Photo 2024.jpg\",\n      type: \"image\" as const,\n      size: \"5.2 MB\",\n      modified: \"1 day ago\",\n      modifiedBy: {\n        name: \"Michael Chen\",\n        avatar: \"https://randomuser.me/api/portraits/men/2.jpg\",\n      },\n      shared: false,\n      starred: false,\n      thumbnail: \"https://randomuser.me/api/portraits/men/2.jpg\",\n      tags: [\"Team\", \"Photos\"],\n    },\n    {\n      id: 3,\n      name: \"Product Demo Video.mp4\",\n      type: \"video\" as const,\n      size: \"45.8 MB\",\n      modified: \"3 days ago\",\n      modifiedBy: {\n        name: \"Emily Rodriguez\",\n        avatar: \"https://randomuser.me/api/portraits/women/3.jpg\",\n      },\n      shared: true,\n      starred: true,\n      thumbnail: \"https://randomuser.me/api/portraits/women/3.jpg\",\n      tags: [\"Demo\", \"Product\"],\n    },\n    {\n      id: 4,\n      name: \"Meeting Notes - Sprint Planning.docx\",\n      type: \"document\" as const,\n      size: \"156 KB\",\n      modified: \"5 days ago\",\n      modifiedBy: {\n        name: \"David Park\",\n        avatar: \"https://randomuser.me/api/portraits/men/4.jpg\",\n      },\n      shared: false,\n      starred: false,\n      thumbnail: \"https://randomuser.me/api/portraits/men/4.jpg\",\n      tags: [\"Meeting\", \"Sprint\"],\n    },\n    {\n      id: 5,\n      name: \"Background Music.mp3\",\n      type: \"audio\" as const,\n      size: \"8.7 MB\",\n      modified: \"1 week ago\",\n      modifiedBy: {\n        name: \"Lisa Thompson\",\n        avatar: \"https://randomuser.me/api/portraits/women/5.jpg\",\n      },\n      shared: false,\n      starred: false,\n      thumbnail: \"https://randomuser.me/api/portraits/women/5.jpg\",\n      tags: [\"Audio\", \"Media\"],\n    },\n    {\n      id: 6,\n      name: \"Archive_2024_Q3.zip\",\n      type: \"archive\" as const,\n      size: \"127 MB\",\n      modified: \"2 weeks ago\",\n      modifiedBy: {\n        name: \"James Wilson\",\n        avatar: \"https://randomuser.me/api/portraits/men/6.jpg\",\n      },\n      shared: true,\n      starred: false,\n      thumbnail: \"https://randomuser.me/api/portraits/men/6.jpg\",\n      tags: [\"Archive\", \"Q3\"],\n    },\n  ],\n  emptyState: {\n    message: \"No files found\",\n    icon: FileText,\n  },\n};\n\nfunction getFileIcon(type: FileItem[\"type\"]): LucideIcon {\n  switch (type) {\n    case \"pdf\":\n    case \"document\":\n      return FileText;\n    case \"image\":\n      return ImageIcon;\n    case \"video\":\n      return Video;\n    case \"audio\":\n      return Music;\n    case \"archive\":\n      return Archive;\n    default:\n      return FileText;\n  }\n}\n\nfunction getFileColor(type: FileItem[\"type\"]): string {\n  switch (type) {\n    case \"pdf\":\n      return \"text-red-600 bg-red-100 dark:bg-red-900/20\";\n    case \"document\":\n      return \"text-blue-600 bg-blue-100 dark:bg-blue-900/20\";\n    case \"image\":\n      return \"text-green-600 bg-green-100 dark:bg-green-900/20\";\n    case \"video\":\n      return \"text-purple-600 bg-purple-100 dark:bg-purple-900/20\";\n    case \"audio\":\n      return \"text-orange-600 bg-orange-100 dark:bg-orange-900/20\";\n    case \"archive\":\n      return \"text-gray-600 bg-gray-100 dark:bg-gray-900/20\";\n    default:\n      return \"text-gray-600 bg-gray-100 dark:bg-gray-900/20\";\n  }\n}\n\nexport function List03({ className }: List03Props) {\n  const [starredFiles, setStarredFiles] = useState(\n    new Set(\n      content.files.filter((file) => file.starred).map((file) => file.id),\n    ),\n  );\n\n  const handleStarToggle = (fileId: number) => {\n    const newStarred = new Set(starredFiles);\n    const isStarred = newStarred.has(fileId);\n\n    if (isStarred) {\n      newStarred.delete(fileId);\n    } else {\n      newStarred.add(fileId);\n    }\n    setStarredFiles(newStarred);\n    console.log(\"Star toggled:\", { fileId, isStarred: !isStarred });\n  };\n\n  const handleAuthorClick = (\n    author: FileItem[\"modifiedBy\"],\n    e: React.MouseEvent,\n  ) => {\n    e.stopPropagation();\n    console.log(\"Author clicked:\", author);\n  };\n\n  const handleActionClick = (action: string, file: FileItem) => {\n    console.log(`${action} clicked:`, file);\n  };\n\n  return (\n    <Card className={cn(\"w-full max-w-5xl mx-auto\", className)}>\n      <CardContent className=\"p-0\">\n        <div className=\"divide-y divide-border/50\">\n          {content.files.map((file) => {\n            const FileIcon = getFileIcon(file.type);\n            const isStarred = starredFiles.has(file.id);\n\n            return (\n              <div\n                key={file.id}\n                className=\"block p-4 hover:bg-muted/50 transition-colors\"\n                data-testid={`file-${file.id}`}\n              >\n                <div className=\"flex items-center space-x-4\">\n                  <div\n                    className={cn(\n                      \"p-2 rounded-lg flex-shrink-0\",\n                      getFileColor(file.type),\n                    )}\n                    data-testid={`file-icon-${file.id}`}\n                  >\n                    <FileIcon className=\"h-5 w-5\" />\n                  </div>\n\n                  <div className=\"flex-1 min-w-0\">\n                    <div className=\"flex items-center space-x-2 mb-1\">\n                      <h3 className=\"text-sm font-medium text-foreground truncate\">\n                        {file.name}\n                      </h3>\n                      <Button\n                        variant=\"ghost\"\n                        size=\"icon\"\n                        className=\"h-6 w-6 flex-shrink-0\"\n                        onClick={(e) => {\n                          e.stopPropagation();\n                          handleStarToggle(file.id);\n                        }}\n                        data-testid={`star-button-${file.id}`}\n                      >\n                        <Star\n                          className={cn(\n                            \"h-3 w-3\",\n                            isStarred\n                              ? \"fill-yellow-400 text-yellow-400\"\n                              : \"text-muted-foreground\",\n                          )}\n                        />\n                      </Button>\n                      {file.shared && (\n                        <Badge\n                          variant=\"outline\"\n                          className=\"text-xs\"\n                          data-testid={`shared-badge-${file.id}`}\n                        >\n                          Shared\n                        </Badge>\n                      )}\n                    </div>\n                    <div\n                      className=\"flex items-center space-x-4 text-xs text-muted-foreground mb-2\"\n                      data-testid={`file-info-${file.id}`}\n                    >\n                      <span>{file.size}</span>\n                      <Separator orientation=\"vertical\" className=\"h-3\" />\n                      <span>Modified {file.modified}</span>\n                      <Separator orientation=\"vertical\" className=\"h-3\" />\n                      <button\n                        type=\"button\"\n                        className=\"flex items-center space-x-1 cursor-pointer hover:bg-muted/50 rounded px-1 py-0.5 -mx-1 -my-0.5 transition-colors\"\n                        onClick={(e) => handleAuthorClick(file.modifiedBy, e)}\n                        data-testid={`author-${file.id}`}\n                      >\n                        <Avatar className=\"h-4 w-4\">\n                          <AvatarImage\n                            src={file.modifiedBy.avatar}\n                            alt={file.modifiedBy.name}\n                          />\n                          <AvatarFallback className=\"text-xs\">\n                            {file.modifiedBy.name\n                              .split(\" \")\n                              .map((n) => n[0])\n                              .join(\"\")}\n                          </AvatarFallback>\n                        </Avatar>\n                        <span>{file.modifiedBy.name}</span>\n                      </button>\n                    </div>\n                    {file.tags && file.tags.length > 0 && (\n                      <div\n                        className=\"flex flex-wrap gap-1\"\n                        data-testid={`tags-${file.id}`}\n                      >\n                        {file.tags.map((tag) => (\n                          <Badge\n                            key={tag}\n                            variant=\"outline\"\n                            className=\"text-xs\"\n                          >\n                            {tag}\n                          </Badge>\n                        ))}\n                      </div>\n                    )}\n                  </div>\n\n                  <div className=\"flex items-center space-x-1 flex-shrink-0\">\n                    <Button\n                      variant=\"ghost\"\n                      size=\"icon\"\n                      className=\"h-8 w-8 hidden sm:flex\"\n                      onClick={(e) => {\n                        e.stopPropagation();\n                        handleActionClick(\"preview\", file);\n                      }}\n                      data-testid={`preview-button-${file.id}`}\n                    >\n                      <Eye className=\"h-4 w-4\" />\n                    </Button>\n                    <Button\n                      variant=\"ghost\"\n                      size=\"icon\"\n                      className=\"h-8 w-8 hidden sm:flex\"\n                      onClick={(e) => {\n                        e.stopPropagation();\n                        handleActionClick(\"download\", file);\n                      }}\n                      data-testid={`download-button-${file.id}`}\n                    >\n                      <Download className=\"h-4 w-4\" />\n                    </Button>\n                    <Button\n                      variant=\"ghost\"\n                      size=\"icon\"\n                      className=\"h-8 w-8 hidden sm:flex\"\n                      onClick={(e) => {\n                        e.stopPropagation();\n                        handleActionClick(\"share\", file);\n                      }}\n                      data-testid={`share-button-${file.id}`}\n                    >\n                      <Share2 className=\"h-4 w-4\" />\n                    </Button>\n\n                    <DropdownMenu>\n                      <DropdownMenuTrigger asChild>\n                        <Button\n                          variant=\"ghost\"\n                          size=\"icon\"\n                          className=\"h-8 w-8\"\n                          data-testid={`more-actions-${file.id}`}\n                        >\n                          <MoreHorizontal className=\"h-4 w-4\" />\n                        </Button>\n                      </DropdownMenuTrigger>\n                      <DropdownMenuContent\n                        align=\"end\"\n                        data-testid={`dropdown-menu-${file.id}`}\n                      >\n                        <DropdownMenuItem\n                          className=\"sm:hidden\"\n                          onClick={() => {\n                            handleActionClick(\"preview\", file);\n                          }}\n                        >\n                          <Eye className=\"h-4 w-4 mr-2\" />\n                          Preview\n                        </DropdownMenuItem>\n                        <DropdownMenuItem\n                          className=\"sm:hidden\"\n                          onClick={() => {\n                            handleActionClick(\"download\", file);\n                          }}\n                        >\n                          <Download className=\"h-4 w-4 mr-2\" />\n                          Download\n                        </DropdownMenuItem>\n                        <DropdownMenuItem\n                          className=\"sm:hidden\"\n                          onClick={() => {\n                            handleActionClick(\"share\", file);\n                          }}\n                        >\n                          <Share2 className=\"h-4 w-4 mr-2\" />\n                          Share\n                        </DropdownMenuItem>\n                        <DropdownMenuSeparator className=\"sm:hidden\" />\n                        <DropdownMenuItem\n                          onClick={() => {\n                            handleActionClick(\"rename\", file);\n                          }}\n                        >\n                          <Edit3 className=\"h-4 w-4 mr-2\" />\n                          Rename\n                        </DropdownMenuItem>\n                        <DropdownMenuItem\n                          onClick={() => {\n                            handleActionClick(\"move\", file);\n                          }}\n                        >\n                          Move to Folder\n                        </DropdownMenuItem>\n                        <DropdownMenuSeparator />\n                        <DropdownMenuItem\n                          className=\"text-destructive\"\n                          onClick={() => {\n                            handleActionClick(\"delete\", file);\n                          }}\n                        >\n                          <Trash2 className=\"h-4 w-4 mr-2\" />\n                          Delete\n                        </DropdownMenuItem>\n                      </DropdownMenuContent>\n                    </DropdownMenu>\n                  </div>\n                </div>\n              </div>\n            );\n          })}\n        </div>\n\n        {content.files.length === 0 && (\n          <div className=\"p-8 text-center\" data-testid=\"empty-state\">\n            <content.emptyState.icon className=\"h-12 w-12 text-muted-foreground mx-auto mb-4\" />\n            <p className=\"text-muted-foreground\">\n              {content.emptyState.message}\n            </p>\n          </div>\n        )}\n      </CardContent>\n    </Card>\n  );\n}\n","type":"registry:component"},{"path":"src/registry/lib/utils.ts","content":"import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n","type":"registry:lib"}]}