@burdenoff/microfe-files 2026.624.3 → 2026.625.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/files/components/FileBrowser.js +2 -2
- package/dist/files/components/FileBrowser.js.map +1 -1
- package/dist/files/components/FileCard.js +112 -110
- package/dist/files/components/FileCard.js.map +1 -1
- package/dist/files/components/FileInfoPanel.js +89 -84
- package/dist/files/components/FileInfoPanel.js.map +1 -1
- package/dist/files/components/FileListItem.js +1 -1
- package/dist/files/components/FileListItem.js.map +1 -1
- package/dist/files/components/FileVersionPanel.js +3 -3
- package/dist/files/components/FileVersionPanel.js.map +1 -1
- package/dist/files/components/FolderCard.js +128 -126
- package/dist/files/components/FolderCard.js.map +1 -1
- package/dist/files/pages/BrowserPage.js +390 -405
- package/dist/files/pages/BrowserPage.js.map +1 -1
- package/dist/files/pages/ShareAccessPage.js +45 -43
- package/dist/files/pages/ShareAccessPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileListItem.js","names":[],"sources":["../../../src/files/components/FileListItem.tsx"],"sourcesContent":["import { type FC, useMemo, useState } from \"react\";\nimport {\n Download,\n Share2,\n Trash2,\n Star,\n RotateCcw,\n Edit,\n Copy,\n FolderInput,\n Info,\n Activity,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { FileItem } from \"../types\";\nimport { formatFileSize, formatFileDate, getFileIcon } from \"../utils\";\nimport { useFilesStore } from \"../store/filesStore\";\nimport { ActionMenu } from \"./ActionMenu\";\nimport { getDragOperation } from \"../hooks/useDragDrop\";\nimport { tWithFallback } from \"../utils/i18n\";\n\ninterface FileListItemProps {\n file: FileItem;\n isSelected?: boolean;\n isDragging?: boolean;\n onSelect?: (fileId: string) => void;\n onPreview?: (file: FileItem) => void;\n onShare?: (file: FileItem) => void;\n onDelete?: (file: FileItem) => void;\n onRestore?: (file: FileItem) => void;\n onFavorite?: (file: FileItem) => void;\n onRename?: (file: FileItem) => void;\n onDownload?: (file: FileItem) => void;\n onCopy?: (file: FileItem) => void;\n onMove?: (file: FileItem) => void;\n onInfo?: (file: FileItem) => void;\n onActivity?: (file: FileItem) => void;\n onDragStart?: (file: FileItem, operation: \"move\" | \"copy\") => void;\n onDragEnd?: () => void;\n onContextMenu?: (file: FileItem, x: number, y: number) => void;\n}\n\ninterface DraggableFileListItemProps extends Omit<\n FileListItemProps,\n \"onDragStart\" | \"onDragEnd\"\n> {\n onDragStart?: (file: FileItem, operation: \"move\" | \"copy\") => void;\n onDragEnd?: () => void;\n}\n\nexport const FileListItem: FC<DraggableFileListItemProps> = ({\n file,\n isSelected = false,\n isDragging = false,\n onSelect,\n onPreview,\n onShare,\n onDelete,\n onRestore,\n onFavorite,\n onRename,\n onDownload,\n onCopy,\n onMove,\n onInfo,\n onActivity,\n onDragStart,\n onDragEnd,\n onContextMenu,\n}) => {\n const { t } = useI18n();\n const [menuOpen, setMenuOpen] = useState(false);\n const { toggleFileSelection } = useFilesStore();\n\n const openPreview = () => {\n if (onPreview) {\n onPreview(file);\n }\n };\n\n const handleCheckboxChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n e.stopPropagation();\n if (onSelect) {\n onSelect(file.id);\n } else {\n toggleFileSelection(file.id);\n }\n };\n\n const handleActionClick = (e: React.MouseEvent, action: () => void) => {\n e.stopPropagation();\n action();\n };\n\n const handleDragStart = (e: React.DragEvent<HTMLDivElement>) => {\n e.dataTransfer.effectAllowed = \"copyMove\";\n e.dataTransfer.setData(\"application/x-file-id\", file.id);\n e.dataTransfer.setData(\"application/x-file-type\", \"file\");\n const operation = getDragOperation(e);\n e.dataTransfer.setData(\"application/x-drag-operation\", operation);\n onDragStart?.(file, operation);\n };\n\n const handleDragEnd = () => {\n onDragEnd?.();\n };\n\n const handleContextMenu = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onContextMenu?.(file, e.clientX, e.clientY);\n };\n\n const FileIconComponent = getFileIcon(file.mimeType);\n\n // Memoize menu items to prevent re-renders\n const menuItems = useMemo(\n () => [\n ...(onShare\n ? [\n {\n label: tWithFallback(t, \"files.card.share\", \"Share\"),\n icon: <Share2 className=\"size-4\" />,\n onClick: () => onShare(file),\n },\n ]\n : []),\n ...(onDownload\n ? [\n {\n label: tWithFallback(t, \"files.card.download\", \"Download\"),\n icon: <Download className=\"size-4\" />,\n onClick: () => onDownload(file),\n },\n ]\n : []),\n ...(onCopy\n ? [\n {\n label: tWithFallback(t, \"files.card.makeCopy\", \"Make a copy\"),\n icon: <Copy className=\"size-4\" />,\n onClick: () => onCopy(file),\n },\n ]\n : []),\n ...(onMove\n ? [\n {\n label: tWithFallback(t, \"files.card.moveTo\", \"Move to\"),\n icon: <FolderInput className=\"size-4\" />,\n onClick: () => onMove(file),\n },\n ]\n : []),\n ...(onRename\n ? [\n {\n label: tWithFallback(t, \"files.card.rename\", \"Rename\"),\n icon: <Edit className=\"size-4\" />,\n onClick: () => onRename(file),\n divider: true,\n },\n ]\n : []),\n ...(onFavorite\n ? [\n {\n label: file.starred\n ? tWithFallback(\n t,\n \"files.card.removeFromStarred\",\n \"Remove from starred\",\n )\n : tWithFallback(t, \"files.card.addToStarred\", \"Add to starred\"),\n icon: <Star className=\"size-4\" />,\n onClick: () => onFavorite(file),\n },\n ]\n : []),\n ...(onInfo\n ? [\n {\n label: tWithFallback(t, \"files.card.details\", \"Details\"),\n icon: <Info className=\"size-4\" />,\n onClick: () => onInfo(file),\n },\n ]\n : []),\n ...(onActivity\n ? [\n {\n label: tWithFallback(t, \"files.card.activity\", \"Activity\"),\n icon: <Activity className=\"size-4\" />,\n onClick: () => onActivity(file),\n },\n ]\n : []),\n ...(onDelete\n ? [\n {\n label: tWithFallback(t, \"files.card.delete\", \"Delete\"),\n icon: <Trash2 className=\"size-4\" />,\n onClick: () => onDelete(file),\n variant: \"danger\" as const,\n divider: true,\n },\n ]\n : []),\n ...(onRestore\n ? [\n {\n label: tWithFallback(t, \"files.card.restore\", \"Restore\"),\n icon: <RotateCcw className=\"size-4\" />,\n onClick: () => onRestore(file),\n },\n ]\n : []),\n ],\n [\n file,\n onShare,\n onDownload,\n onCopy,\n onMove,\n onRename,\n onFavorite,\n onInfo,\n onActivity,\n onDelete,\n onRestore,\n t,\n ],\n );\n\n return (\n <div\n className={`@container group flex items-center gap-3 border-b border-border-subtle bg-bg-surface p-3 transition-colors hover:bg-bg-sunken focus-within:bg-bg-sunken ${isDragging ? \"opacity-50\" : \"\"}`}\n onClick={openPreview}\n onContextMenu={handleContextMenu}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n openPreview();\n }\n }}\n draggable={Boolean(onDragStart)}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n role=\"button\"\n tabIndex={0}\n aria-label={`Preview ${file.name}`}\n >\n {/* Selection checkbox */}\n <div className=\"flex-shrink-0\">\n <input\n type=\"checkbox\"\n checked={isSelected}\n onChange={handleCheckboxChange}\n className=\"size-4 rounded border-border-default bg-bg-surface text-action-primary-bg focus:ring-2 focus:ring-action-primary-bg focus:ring-offset-2\"\n aria-label={`Select ${file.name}`}\n />\n </div>\n\n {/* File icon */}\n <div className=\"flex-shrink-0\">\n {file.thumbnailUrl ? (\n <img\n src={file.thumbnailUrl}\n alt={file.name}\n className=\"size-8 object-cover rounded\"\n />\n ) : (\n <FileIconComponent className=\"size-6 text-text-muted\" />\n )}\n </div>\n\n {/* File name - takes remaining space */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-text-md font-weight-medium text-text-primary truncate\">\n {file.name}\n </span>\n {file.starred && (\n <Star className=\"size-3 flex-shrink-0 fill-yellow-400 text-yellow-400\" />\n )}\n {file.isShared && (\n <Share2 className=\"size-3 flex-shrink-0 text-text-muted\" />\n )}\n </div>\n\n {/* Tags - visible on medium+ containers */}\n {file.tags && file.tags.length > 0 && (\n <div className=\"hidden @md:flex flex-wrap gap-1 mt-1\">\n {file.tags.slice(0, 3).map((tag) => (\n <span\n key={tag}\n className=\"px-1.5 py-0.5 bg-bg-accent text-text-primary font-text-xs rounded-sm\"\n >\n {tag}\n </span>\n ))}\n {file.tags.length > 3 && (\n <span className=\"text-text-muted font-text-xs\">\n +{file.tags.length - 3}\n </span>\n )}\n </div>\n )}\n </div>\n\n {/* Owner - visible on large+ containers */}\n {file.createdBy && (\n <div className=\"hidden @lg:flex items-center gap-2 flex-shrink-0 w-40\">\n {file.createdBy.avatar && (\n <img\n src={file.createdBy.avatar}\n alt={file.createdBy.name}\n className=\"size-6 rounded-full\"\n />\n )}\n <span className=\"font-text-sm text-text-secondary truncate\">\n {file.createdBy.name}\n </span>\n </div>\n )}\n\n {/* Size - visible on medium+ containers */}\n <div className=\"hidden @md:block flex-shrink-0 w-24 text-right\">\n <span className=\"font-text-sm text-text-secondary tabular-nums\">\n {formatFileSize(file.size)}\n </span>\n </div>\n\n {/* Modified date - visible on large+ containers */}\n <div className=\"hidden @lg:block flex-shrink-0 w-32 text-right\">\n <span className=\"font-text-sm text-text-secondary\">\n {formatFileDate(file.updatedAt)}\n </span>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-1\">\n <div className=\"flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100\">\n {file.canRead && onDownload && (\n <button\n type=\"button\"\n onClick={(e) => handleActionClick(e, () => onDownload(file))}\n className=\"p-1.5 hover:bg-action-ghost-bg-hover rounded transition-colors\"\n aria-label={tWithFallback(t, \"files.card.download\", \"Download\")}\n title={tWithFallback(t, \"files.card.download\", \"Download\")}\n >\n <Download className=\"size-4 text-text-primary\" />\n </button>\n )}\n\n {onShare && (\n <button\n type=\"button\"\n onClick={(e) => handleActionClick(e, () => onShare(file))}\n className=\"p-1.5 hover:bg-action-ghost-bg-hover rounded transition-colors\"\n aria-label={tWithFallback(t, \"files.card.share\", \"Share\")}\n title={tWithFallback(t, \"files.card.share\", \"Share\")}\n >\n <Share2 className=\"size-4 text-text-primary\" />\n </button>\n )}\n\n {onFavorite && (\n <button\n type=\"button\"\n onClick={(e) => handleActionClick(e, () => onFavorite(file))}\n className=\"p-1.5 hover:bg-action-ghost-bg-hover rounded transition-colors\"\n aria-label={\n file.starred\n ? tWithFallback(t, \"files.card.unfavorite\", \"Unfavorite\")\n : tWithFallback(t, \"files.card.favorite\", \"Favorite\")\n }\n title={\n file.starred\n ? tWithFallback(t, \"files.card.unfavorite\", \"Unfavorite\")\n : tWithFallback(t, \"files.card.favorite\", \"Favorite\")\n }\n >\n <Star\n className={`size-4 ${\n file.starred\n ? \"fill-status-warning-bg text-status-warning-bg\"\n : \"text-text-primary\"\n }`}\n />\n </button>\n )}\n\n {onRestore && (\n <button\n type=\"button\"\n onClick={(e) => handleActionClick(e, () => onRestore(file))}\n className=\"p-1.5 hover:bg-status-success-bg-subtle rounded transition-colors\"\n aria-label={tWithFallback(t, \"files.card.restore\", \"Restore\")}\n title={tWithFallback(t, \"files.card.restore\", \"Restore\")}\n >\n <RotateCcw className=\"size-4 text-status-success-text\" />\n </button>\n )}\n\n {file.canWrite && onRename && (\n <button\n type=\"button\"\n onClick={(e) => handleActionClick(e, () => onRename(file))}\n className=\"hidden @md:block p-1.5 hover:bg-action-ghost-bg-hover rounded transition-colors\"\n aria-label={tWithFallback(t, \"files.card.rename\", \"Rename\")}\n title={tWithFallback(t, \"files.card.rename\", \"Rename\")}\n >\n <Edit className=\"size-4 text-text-primary\" />\n </button>\n )}\n\n {file.canDelete && onDelete && (\n <button\n type=\"button\"\n onClick={(e) => handleActionClick(e, () => onDelete(file))}\n className=\"p-1.5 hover:bg-action-danger-bg-hover rounded transition-colors\"\n aria-label={tWithFallback(t, \"files.card.delete\", \"Delete\")}\n title={tWithFallback(t, \"files.card.delete\", \"Delete\")}\n >\n <Trash2 className=\"size-4 text-status-error-text\" />\n </button>\n )}\n </div>\n\n {/* More actions dropdown - always visible */}\n <ActionMenu\n items={menuItems}\n align=\"right\"\n isOpen={menuOpen}\n onToggle={setMenuOpen}\n />\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FileListItem.js","names":[],"sources":["../../../src/files/components/FileListItem.tsx"],"sourcesContent":["import { type FC, useMemo, useState } from \"react\";\nimport {\n Download,\n Share2,\n Trash2,\n Star,\n RotateCcw,\n Edit,\n Copy,\n FolderInput,\n Info,\n Activity,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { FileItem } from \"../types\";\nimport { formatFileSize, formatFileDate, getFileIcon } from \"../utils\";\nimport { useFilesStore } from \"../store/filesStore\";\nimport { ActionMenu } from \"./ActionMenu\";\nimport { getDragOperation } from \"../hooks/useDragDrop\";\nimport { tWithFallback } from \"../utils/i18n\";\n\ninterface FileListItemProps {\n file: FileItem;\n isSelected?: boolean;\n isDragging?: boolean;\n onSelect?: (fileId: string) => void;\n onPreview?: (file: FileItem) => void;\n onShare?: (file: FileItem) => void;\n onDelete?: (file: FileItem) => void;\n onRestore?: (file: FileItem) => void;\n onFavorite?: (file: FileItem) => void;\n onRename?: (file: FileItem) => void;\n onDownload?: (file: FileItem) => void;\n onCopy?: (file: FileItem) => void;\n onMove?: (file: FileItem) => void;\n onInfo?: (file: FileItem) => void;\n onActivity?: (file: FileItem) => void;\n onDragStart?: (file: FileItem, operation: \"move\" | \"copy\") => void;\n onDragEnd?: () => void;\n onContextMenu?: (file: FileItem, x: number, y: number) => void;\n}\n\ninterface DraggableFileListItemProps extends Omit<\n FileListItemProps,\n \"onDragStart\" | \"onDragEnd\"\n> {\n onDragStart?: (file: FileItem, operation: \"move\" | \"copy\") => void;\n onDragEnd?: () => void;\n}\n\nexport const FileListItem: FC<DraggableFileListItemProps> = ({\n file,\n isSelected = false,\n isDragging = false,\n onSelect,\n onPreview,\n onShare,\n onDelete,\n onRestore,\n onFavorite,\n onRename,\n onDownload,\n onCopy,\n onMove,\n onInfo,\n onActivity,\n onDragStart,\n onDragEnd,\n onContextMenu,\n}) => {\n const { t } = useI18n();\n const [menuOpen, setMenuOpen] = useState(false);\n const { toggleFileSelection } = useFilesStore();\n\n const openPreview = () => {\n if (onPreview) {\n onPreview(file);\n }\n };\n\n const handleCheckboxChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n e.stopPropagation();\n if (onSelect) {\n onSelect(file.id);\n } else {\n toggleFileSelection(file.id);\n }\n };\n\n const handleActionClick = (e: React.MouseEvent, action: () => void) => {\n e.stopPropagation();\n action();\n };\n\n const handleDragStart = (e: React.DragEvent<HTMLDivElement>) => {\n e.dataTransfer.effectAllowed = \"copyMove\";\n e.dataTransfer.setData(\"application/x-file-id\", file.id);\n e.dataTransfer.setData(\"application/x-file-type\", \"file\");\n const operation = getDragOperation(e);\n e.dataTransfer.setData(\"application/x-drag-operation\", operation);\n onDragStart?.(file, operation);\n };\n\n const handleDragEnd = () => {\n onDragEnd?.();\n };\n\n const handleContextMenu = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onContextMenu?.(file, e.clientX, e.clientY);\n };\n\n const FileIconComponent = getFileIcon(file.mimeType);\n\n // Memoize menu items to prevent re-renders\n const menuItems = useMemo(\n () => [\n ...(onShare\n ? [\n {\n label: tWithFallback(t, \"files.card.share\", \"Share\"),\n icon: <Share2 className=\"size-4\" />,\n onClick: () => onShare(file),\n },\n ]\n : []),\n ...(onDownload\n ? [\n {\n label: tWithFallback(t, \"files.card.download\", \"Download\"),\n icon: <Download className=\"size-4\" />,\n onClick: () => onDownload(file),\n },\n ]\n : []),\n ...(onCopy\n ? [\n {\n label: tWithFallback(t, \"files.card.makeCopy\", \"Make a copy\"),\n icon: <Copy className=\"size-4\" />,\n onClick: () => onCopy(file),\n },\n ]\n : []),\n ...(onMove\n ? [\n {\n label: tWithFallback(t, \"files.card.moveTo\", \"Move to\"),\n icon: <FolderInput className=\"size-4\" />,\n onClick: () => onMove(file),\n },\n ]\n : []),\n ...(onRename\n ? [\n {\n label: tWithFallback(t, \"files.card.rename\", \"Rename\"),\n icon: <Edit className=\"size-4\" />,\n onClick: () => onRename(file),\n divider: true,\n },\n ]\n : []),\n ...(onFavorite\n ? [\n {\n label: file.starred\n ? tWithFallback(\n t,\n \"files.card.removeFromStarred\",\n \"Remove from starred\",\n )\n : tWithFallback(t, \"files.card.addToStarred\", \"Add to starred\"),\n icon: <Star className=\"size-4\" />,\n onClick: () => onFavorite(file),\n },\n ]\n : []),\n ...(onInfo\n ? [\n {\n label: tWithFallback(t, \"files.card.details\", \"Details\"),\n icon: <Info className=\"size-4\" />,\n onClick: () => onInfo(file),\n },\n ]\n : []),\n ...(onActivity\n ? [\n {\n label: tWithFallback(t, \"files.card.activity\", \"Activity\"),\n icon: <Activity className=\"size-4\" />,\n onClick: () => onActivity(file),\n },\n ]\n : []),\n ...(onDelete\n ? [\n {\n label: tWithFallback(t, \"files.card.delete\", \"Delete\"),\n icon: <Trash2 className=\"size-4\" />,\n onClick: () => onDelete(file),\n variant: \"danger\" as const,\n divider: true,\n },\n ]\n : []),\n ...(onRestore\n ? [\n {\n label: tWithFallback(t, \"files.card.restore\", \"Restore\"),\n icon: <RotateCcw className=\"size-4\" />,\n onClick: () => onRestore(file),\n },\n ]\n : []),\n ],\n [\n file,\n onShare,\n onDownload,\n onCopy,\n onMove,\n onRename,\n onFavorite,\n onInfo,\n onActivity,\n onDelete,\n onRestore,\n t,\n ],\n );\n\n return (\n <div\n className={`@container group flex items-center gap-3 border-b border-border-seam bg-bg-surface p-3 transition-colors hover:bg-bg-sunken focus-within:bg-bg-sunken ${isDragging ? \"opacity-50\" : \"\"}`}\n onClick={openPreview}\n onContextMenu={handleContextMenu}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n openPreview();\n }\n }}\n draggable={Boolean(onDragStart)}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n role=\"button\"\n tabIndex={0}\n aria-label={`Preview ${file.name}`}\n >\n {/* Selection checkbox */}\n <div className=\"flex-shrink-0\">\n <input\n type=\"checkbox\"\n checked={isSelected}\n onChange={handleCheckboxChange}\n className=\"size-4 rounded border-border-default bg-bg-surface text-action-primary-bg focus:ring-2 focus:ring-action-primary-bg focus:ring-offset-2\"\n aria-label={`Select ${file.name}`}\n />\n </div>\n\n {/* File icon */}\n <div className=\"flex-shrink-0\">\n {file.thumbnailUrl ? (\n <img\n src={file.thumbnailUrl}\n alt={file.name}\n className=\"size-8 object-cover rounded\"\n />\n ) : (\n <FileIconComponent className=\"size-6 text-text-muted\" />\n )}\n </div>\n\n {/* File name - takes remaining space */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-text-md font-weight-medium text-text-primary truncate\">\n {file.name}\n </span>\n {file.starred && (\n <Star className=\"size-3 flex-shrink-0 fill-yellow-400 text-yellow-400\" />\n )}\n {file.isShared && (\n <Share2 className=\"size-3 flex-shrink-0 text-text-muted\" />\n )}\n </div>\n\n {/* Tags - visible on medium+ containers */}\n {file.tags && file.tags.length > 0 && (\n <div className=\"hidden @md:flex flex-wrap gap-1 mt-1\">\n {file.tags.slice(0, 3).map((tag) => (\n <span\n key={tag}\n className=\"px-1.5 py-0.5 bg-bg-accent text-text-primary font-text-xs rounded-sm\"\n >\n {tag}\n </span>\n ))}\n {file.tags.length > 3 && (\n <span className=\"text-text-muted font-text-xs\">\n +{file.tags.length - 3}\n </span>\n )}\n </div>\n )}\n </div>\n\n {/* Owner - visible on large+ containers */}\n {file.createdBy && (\n <div className=\"hidden @lg:flex items-center gap-2 flex-shrink-0 w-40\">\n {file.createdBy.avatar && (\n <img\n src={file.createdBy.avatar}\n alt={file.createdBy.name}\n className=\"size-6 rounded-full\"\n />\n )}\n <span className=\"font-text-sm text-text-secondary truncate\">\n {file.createdBy.name}\n </span>\n </div>\n )}\n\n {/* Size - visible on medium+ containers */}\n <div className=\"hidden @md:block flex-shrink-0 w-24 text-right\">\n <span className=\"font-text-sm text-text-secondary tabular-nums\">\n {formatFileSize(file.size)}\n </span>\n </div>\n\n {/* Modified date - visible on large+ containers */}\n <div className=\"hidden @lg:block flex-shrink-0 w-32 text-right\">\n <span className=\"font-text-sm text-text-secondary\">\n {formatFileDate(file.updatedAt)}\n </span>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-1\">\n <div className=\"flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100\">\n {file.canRead && onDownload && (\n <button\n type=\"button\"\n onClick={(e) => handleActionClick(e, () => onDownload(file))}\n className=\"p-1.5 hover:bg-action-ghost-bg-hover rounded transition-colors\"\n aria-label={tWithFallback(t, \"files.card.download\", \"Download\")}\n title={tWithFallback(t, \"files.card.download\", \"Download\")}\n >\n <Download className=\"size-4 text-text-primary\" />\n </button>\n )}\n\n {onShare && (\n <button\n type=\"button\"\n onClick={(e) => handleActionClick(e, () => onShare(file))}\n className=\"p-1.5 hover:bg-action-ghost-bg-hover rounded transition-colors\"\n aria-label={tWithFallback(t, \"files.card.share\", \"Share\")}\n title={tWithFallback(t, \"files.card.share\", \"Share\")}\n >\n <Share2 className=\"size-4 text-text-primary\" />\n </button>\n )}\n\n {onFavorite && (\n <button\n type=\"button\"\n onClick={(e) => handleActionClick(e, () => onFavorite(file))}\n className=\"p-1.5 hover:bg-action-ghost-bg-hover rounded transition-colors\"\n aria-label={\n file.starred\n ? tWithFallback(t, \"files.card.unfavorite\", \"Unfavorite\")\n : tWithFallback(t, \"files.card.favorite\", \"Favorite\")\n }\n title={\n file.starred\n ? tWithFallback(t, \"files.card.unfavorite\", \"Unfavorite\")\n : tWithFallback(t, \"files.card.favorite\", \"Favorite\")\n }\n >\n <Star\n className={`size-4 ${\n file.starred\n ? \"fill-status-warning-bg text-status-warning-bg\"\n : \"text-text-primary\"\n }`}\n />\n </button>\n )}\n\n {onRestore && (\n <button\n type=\"button\"\n onClick={(e) => handleActionClick(e, () => onRestore(file))}\n className=\"p-1.5 hover:bg-status-success-bg-subtle rounded transition-colors\"\n aria-label={tWithFallback(t, \"files.card.restore\", \"Restore\")}\n title={tWithFallback(t, \"files.card.restore\", \"Restore\")}\n >\n <RotateCcw className=\"size-4 text-status-success-text\" />\n </button>\n )}\n\n {file.canWrite && onRename && (\n <button\n type=\"button\"\n onClick={(e) => handleActionClick(e, () => onRename(file))}\n className=\"hidden @md:block p-1.5 hover:bg-action-ghost-bg-hover rounded transition-colors\"\n aria-label={tWithFallback(t, \"files.card.rename\", \"Rename\")}\n title={tWithFallback(t, \"files.card.rename\", \"Rename\")}\n >\n <Edit className=\"size-4 text-text-primary\" />\n </button>\n )}\n\n {file.canDelete && onDelete && (\n <button\n type=\"button\"\n onClick={(e) => handleActionClick(e, () => onDelete(file))}\n className=\"p-1.5 hover:bg-action-danger-bg-hover rounded transition-colors\"\n aria-label={tWithFallback(t, \"files.card.delete\", \"Delete\")}\n title={tWithFallback(t, \"files.card.delete\", \"Delete\")}\n >\n <Trash2 className=\"size-4 text-status-error-text\" />\n </button>\n )}\n </div>\n\n {/* More actions dropdown - always visible */}\n <ActionMenu\n items={menuItems}\n align=\"right\"\n isOpen={menuOpen}\n onToggle={setMenuOpen}\n />\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -65,7 +65,7 @@ var C = ({ file: C, onClose: w }) => {
|
|
|
65
65
|
className: "bg-bg-surface rounded-lg shadow-xl max-w-3xl w-full max-h-[90vh] overflow-hidden flex flex-col animate-in zoom-in-95 duration-200",
|
|
66
66
|
children: [
|
|
67
67
|
/* @__PURE__ */ x("div", {
|
|
68
|
-
className: "flex items-center justify-between p-6 border-b border-border-
|
|
68
|
+
className: "flex items-center justify-between p-6 border-b border-border-seam",
|
|
69
69
|
children: [/* @__PURE__ */ x("div", {
|
|
70
70
|
className: "flex items-center gap-3",
|
|
71
71
|
children: [/* @__PURE__ */ b(f, { className: "size-6 text-text-link" }), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("h2", {
|
|
@@ -113,7 +113,7 @@ var C = ({ file: C, onClose: w }) => {
|
|
|
113
113
|
!M && !N && P.length > 0 && /* @__PURE__ */ b("div", {
|
|
114
114
|
className: "space-y-3",
|
|
115
115
|
children: P.map((e) => /* @__PURE__ */ b("div", {
|
|
116
|
-
className: `p-4 border rounded-lg transition-all duration-200 ${e.isCurrentVersion ? "border-action-primary-border bg-bg-accent" : E === e.id ? "border-border-
|
|
116
|
+
className: `p-4 border rounded-lg transition-all duration-200 ${e.isCurrentVersion ? "border-action-primary-border bg-bg-accent" : E === e.id ? "border-border-seam bg-bg-sunken" : "border-border-seam hover:border-border-strong hover:bg-action-ghost-bg-hover"}`,
|
|
117
117
|
onClick: () => D(e.id),
|
|
118
118
|
children: /* @__PURE__ */ x("div", {
|
|
119
119
|
className: "flex items-start justify-between gap-4",
|
|
@@ -197,7 +197,7 @@ var C = ({ file: C, onClose: w }) => {
|
|
|
197
197
|
]
|
|
198
198
|
}),
|
|
199
199
|
/* @__PURE__ */ b("div", {
|
|
200
|
-
className: "p-6 border-t border-border-
|
|
200
|
+
className: "p-6 border-t border-border-seam bg-bg-sunken",
|
|
201
201
|
children: /* @__PURE__ */ x("div", {
|
|
202
202
|
className: "flex items-center justify-between text-sm text-text-secondary",
|
|
203
203
|
children: [/* @__PURE__ */ x("span", { children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileVersionPanel.js","names":[],"sources":["../../../src/files/components/FileVersionPanel.tsx"],"sourcesContent":["import { type FC, useState } from \"react\";\nimport {\n Clock,\n Download,\n RotateCcw,\n Trash2,\n X,\n CheckCircle2,\n AlertCircle,\n Loader2,\n Tag,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { FileItem } from \"../types\";\nimport { formatFileSize, formatFileDate } from \"../utils\";\nimport { tWithFallback } from \"../utils/i18n\";\nimport {\n useFileVersions,\n useRestoreFileVersion,\n useDeleteFileVersion,\n useVersionDownloadUrl,\n} from \"../hooks/useFileVersions\";\nimport { useToast } from \"../hooks/useToast\";\nimport { ConfirmDialog } from \"./ConfirmDialog\";\n\ninterface FileVersionPanelProps {\n file: FileItem;\n onClose: () => void;\n}\n\nexport const FileVersionPanel: FC<FileVersionPanelProps> = ({\n file,\n onClose,\n}) => {\n const { t } = useI18n();\n const [selectedVersionId, setSelectedVersionId] = useState<string | null>(\n null,\n );\n const [confirmAction, setConfirmAction] = useState<{\n title: string;\n description: string;\n confirmLabel: string;\n variant?: \"danger\" | \"default\";\n onConfirm: () => Promise<void>;\n loading?: boolean;\n } | null>(null);\n const toast = useToast();\n\n // Fetch versions\n const { data: versionsData, isLoading, error } = useFileVersions(file.id);\n const versions = versionsData?.versions || [];\n\n // Mutations\n const restoreMutation = useRestoreFileVersion();\n const deleteMutation = useDeleteFileVersion();\n const versionDownload = useVersionDownloadUrl();\n\n const handleRestore = async (versionId: string, versionNumber: number) => {\n setConfirmAction({\n title: `${tWithFallback(t, \"files.version.restoreVersion\", \"Restore version\")} ${versionNumber}`,\n description: tWithFallback(\n t,\n \"files.version.restoreDescription\",\n \"This will create a new version with the restored content.\",\n ),\n confirmLabel: tWithFallback(t, \"files.version.restore\", \"Restore\"),\n onConfirm: async () => {\n try {\n await restoreMutation.mutateAsync({\n versionId,\n createNewVersion: true,\n changeMessage: `${tWithFallback(t, \"files.version.restoredFrom\", \"Restored from version\")} ${versionNumber}`,\n });\n toast.success(\n `${tWithFallback(t, \"files.version.versionRestored\", \"Version {{number}} restored successfully\", { number: versionNumber })}`,\n );\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.version.restoreFailed\",\n \"Failed to restore version\",\n ),\n );\n }\n },\n });\n };\n\n const handleDelete = async (versionId: string, versionNumber: number) => {\n setConfirmAction({\n title: `${tWithFallback(t, \"files.version.deleteVersion\", \"Delete version\")} ${versionNumber}`,\n description: tWithFallback(\n t,\n \"files.version.deleteDescription\",\n \"This action cannot be undone.\",\n ),\n confirmLabel: tWithFallback(\n t,\n \"files.version.deleteVersionBtn\",\n \"Delete version\",\n ),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await deleteMutation.mutateAsync(versionId);\n toast.success(\n `${tWithFallback(t, \"files.version.versionDeleted\", \"Version {{number}} deleted\", { number: versionNumber })}`,\n );\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.version.deleteFailed\",\n \"Failed to delete version\",\n ),\n );\n }\n },\n });\n };\n\n const runConfirmAction = async () => {\n if (!confirmAction || confirmAction.loading) return;\n\n const action = confirmAction.onConfirm;\n setConfirmAction({ ...confirmAction, loading: true });\n await action();\n setConfirmAction(null);\n };\n\n const handleDownload = async (versionId: string) => {\n try {\n const downloadUrl = await versionDownload.getDownloadUrl(versionId);\n\n if (!downloadUrl) {\n throw new Error(\n tWithFallback(\n t,\n \"files.version.downloadUnavailable\",\n \"Version download URL is unavailable\",\n ),\n );\n }\n\n window.open(downloadUrl, \"_blank\", \"noopener,noreferrer\");\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.version.downloadFailed\",\n \"Failed to download version\",\n ),\n );\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 z-[110] bg-bg-overlay flex items-center justify-center p-4 animate-in fade-in duration-200\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={tWithFallback(\n t,\n \"files.version.historyFor\",\n \"Version history for {{name}}\",\n { name: file.name },\n )}\n >\n <div className=\"bg-bg-surface rounded-lg shadow-xl max-w-3xl w-full max-h-[90vh] overflow-hidden flex flex-col animate-in zoom-in-95 duration-200\">\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 border-b border-border-default\">\n <div className=\"flex items-center gap-3\">\n <Clock className=\"size-6 text-text-link\" />\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tWithFallback(\n t,\n \"files.version.versionHistory\",\n \"Version History\",\n )}\n </h2>\n <p className=\"text-sm text-text-secondary\">{file.name}</p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-2 text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n aria-label={tWithFallback(\n t,\n \"files.version.closeHistory\",\n \"Close version history\",\n )}\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto p-6\">\n {isLoading && (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-8 text-text-link animate-spin\" />\n </div>\n )}\n\n {error && (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <AlertCircle className=\"size-12 text-status-error-text mx-auto mb-4\" />\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.version.loadFailed\",\n \"Failed to load version history\",\n )}\n </p>\n </div>\n </div>\n )}\n\n {!isLoading && !error && versions.length === 0 && (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <Clock className=\"size-12 text-text-tertiary mx-auto mb-4\" />\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.version.noHistory\",\n \"No version history available\",\n )}\n </p>\n </div>\n </div>\n )}\n\n {!isLoading && !error && versions.length > 0 && (\n <div className=\"space-y-3\">\n {versions.map((version: (typeof versions)[number]) => (\n <div\n key={version.id}\n className={`p-4 border rounded-lg transition-all duration-200 ${\n version.isCurrentVersion\n ? \"border-action-primary-border bg-bg-accent\"\n : selectedVersionId === version.id\n ? \"border-border-default bg-bg-sunken\"\n : \"border-border-default hover:border-border-default hover:bg-action-ghost-bg-hover\"\n }`}\n onClick={() => setSelectedVersionId(version.id)}\n >\n <div className=\"flex items-start justify-between gap-4\">\n {/* Version info */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 mb-2\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {tWithFallback(t, \"files.version.version\", \"Version\")}{\" \"}\n {version.versionNumber}\n </span>\n\n {version.isCurrentVersion && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 bg-action-primary-bg text-text-inverse text-xs font-medium rounded\">\n <CheckCircle2 className=\"size-3\" />\n {tWithFallback(\n t,\n \"files.version.current\",\n \"Current\",\n )}\n </span>\n )}\n\n {version.isMajorVersion && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 bg-status-info-bg-subtle text-status-info-text text-xs font-medium rounded\">\n <Tag className=\"size-3\" />\n {tWithFallback(t, \"files.version.major\", \"Major\")}\n </span>\n )}\n\n {version.versionLabel && (\n <span className=\"inline-flex items-center px-2 py-0.5 bg-status-success-bg-subtle text-status-success-text text-xs font-medium rounded\">\n {version.versionLabel}\n </span>\n )}\n </div>\n\n {version.changeMessage && (\n <p className=\"text-sm text-text-primary mb-2\">\n {version.changeMessage}\n </p>\n )}\n\n <div className=\"flex items-center gap-4 text-xs text-text-secondary\">\n <span className=\"tabular-nums\">\n {formatFileSize(version.size)}\n </span>\n <span>•</span>\n <span>{formatFileDate(version.createdAt)}</span>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleDownload(version.id);\n }}\n className=\"p-2 text-text-secondary hover:text-text-link hover:bg-bg-accent rounded-lg transition-colors duration-200\"\n title={tWithFallback(\n t,\n \"files.version.downloadThisVersion\",\n \"Download this version\",\n )}\n >\n <Download className=\"size-4\" />\n </button>\n\n {!version.isCurrentVersion && (\n <>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleRestore(version.id, version.versionNumber);\n }}\n disabled={restoreMutation.isPending}\n className=\"p-2 text-text-secondary hover:text-status-success-text hover:bg-status-success-bg-subtle rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tWithFallback(\n t,\n \"files.version.restoreThisVersion\",\n \"Restore this version\",\n )}\n >\n {restoreMutation.isPending ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <RotateCcw className=\"size-4\" />\n )}\n </button>\n\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleDelete(version.id, version.versionNumber);\n }}\n disabled={deleteMutation.isPending}\n className=\"p-2 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tWithFallback(\n t,\n \"files.version.deleteThisVersion\",\n \"Delete this version\",\n )}\n >\n {deleteMutation.isPending ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <Trash2 className=\"size-4\" />\n )}\n </button>\n </>\n )}\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"p-6 border-t border-border-default bg-bg-sunken\">\n <div className=\"flex items-center justify-between text-sm text-text-secondary\">\n <span>\n {versionsData?.total || 0}{\" \"}\n {versionsData?.total !== 1\n ? tWithFallback(t, \"files.version.versions\", \"versions\")\n : tWithFallback(\n t,\n \"files.version.versionSingular\",\n \"version\",\n )}{\" \"}\n {tWithFallback(t, \"files.version.total\", \"total\")}\n </span>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"px-4 py-2 text-sm font-medium text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n >\n {tWithFallback(t, \"files.common.actions.close\", \"Close\")}\n </button>\n </div>\n </div>\n </div>\n\n {confirmAction && (\n <ConfirmDialog\n title={confirmAction.title}\n description={confirmAction.description}\n confirmLabel={confirmAction.confirmLabel}\n cancelLabel={tWithFallback(\n t,\n \"files.common.actions.cancel\",\n \"Cancel\",\n )}\n variant={confirmAction.variant}\n loading={confirmAction.loading}\n onConfirm={runConfirmAction}\n onCancel={() => {\n if (!confirmAction.loading) {\n setConfirmAction(null);\n }\n }}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FileVersionPanel.js","names":[],"sources":["../../../src/files/components/FileVersionPanel.tsx"],"sourcesContent":["import { type FC, useState } from \"react\";\nimport {\n Clock,\n Download,\n RotateCcw,\n Trash2,\n X,\n CheckCircle2,\n AlertCircle,\n Loader2,\n Tag,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { FileItem } from \"../types\";\nimport { formatFileSize, formatFileDate } from \"../utils\";\nimport { tWithFallback } from \"../utils/i18n\";\nimport {\n useFileVersions,\n useRestoreFileVersion,\n useDeleteFileVersion,\n useVersionDownloadUrl,\n} from \"../hooks/useFileVersions\";\nimport { useToast } from \"../hooks/useToast\";\nimport { ConfirmDialog } from \"./ConfirmDialog\";\n\ninterface FileVersionPanelProps {\n file: FileItem;\n onClose: () => void;\n}\n\nexport const FileVersionPanel: FC<FileVersionPanelProps> = ({\n file,\n onClose,\n}) => {\n const { t } = useI18n();\n const [selectedVersionId, setSelectedVersionId] = useState<string | null>(\n null,\n );\n const [confirmAction, setConfirmAction] = useState<{\n title: string;\n description: string;\n confirmLabel: string;\n variant?: \"danger\" | \"default\";\n onConfirm: () => Promise<void>;\n loading?: boolean;\n } | null>(null);\n const toast = useToast();\n\n // Fetch versions\n const { data: versionsData, isLoading, error } = useFileVersions(file.id);\n const versions = versionsData?.versions || [];\n\n // Mutations\n const restoreMutation = useRestoreFileVersion();\n const deleteMutation = useDeleteFileVersion();\n const versionDownload = useVersionDownloadUrl();\n\n const handleRestore = async (versionId: string, versionNumber: number) => {\n setConfirmAction({\n title: `${tWithFallback(t, \"files.version.restoreVersion\", \"Restore version\")} ${versionNumber}`,\n description: tWithFallback(\n t,\n \"files.version.restoreDescription\",\n \"This will create a new version with the restored content.\",\n ),\n confirmLabel: tWithFallback(t, \"files.version.restore\", \"Restore\"),\n onConfirm: async () => {\n try {\n await restoreMutation.mutateAsync({\n versionId,\n createNewVersion: true,\n changeMessage: `${tWithFallback(t, \"files.version.restoredFrom\", \"Restored from version\")} ${versionNumber}`,\n });\n toast.success(\n `${tWithFallback(t, \"files.version.versionRestored\", \"Version {{number}} restored successfully\", { number: versionNumber })}`,\n );\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.version.restoreFailed\",\n \"Failed to restore version\",\n ),\n );\n }\n },\n });\n };\n\n const handleDelete = async (versionId: string, versionNumber: number) => {\n setConfirmAction({\n title: `${tWithFallback(t, \"files.version.deleteVersion\", \"Delete version\")} ${versionNumber}`,\n description: tWithFallback(\n t,\n \"files.version.deleteDescription\",\n \"This action cannot be undone.\",\n ),\n confirmLabel: tWithFallback(\n t,\n \"files.version.deleteVersionBtn\",\n \"Delete version\",\n ),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await deleteMutation.mutateAsync(versionId);\n toast.success(\n `${tWithFallback(t, \"files.version.versionDeleted\", \"Version {{number}} deleted\", { number: versionNumber })}`,\n );\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.version.deleteFailed\",\n \"Failed to delete version\",\n ),\n );\n }\n },\n });\n };\n\n const runConfirmAction = async () => {\n if (!confirmAction || confirmAction.loading) return;\n\n const action = confirmAction.onConfirm;\n setConfirmAction({ ...confirmAction, loading: true });\n await action();\n setConfirmAction(null);\n };\n\n const handleDownload = async (versionId: string) => {\n try {\n const downloadUrl = await versionDownload.getDownloadUrl(versionId);\n\n if (!downloadUrl) {\n throw new Error(\n tWithFallback(\n t,\n \"files.version.downloadUnavailable\",\n \"Version download URL is unavailable\",\n ),\n );\n }\n\n window.open(downloadUrl, \"_blank\", \"noopener,noreferrer\");\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.version.downloadFailed\",\n \"Failed to download version\",\n ),\n );\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 z-[110] bg-bg-overlay flex items-center justify-center p-4 animate-in fade-in duration-200\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={tWithFallback(\n t,\n \"files.version.historyFor\",\n \"Version history for {{name}}\",\n { name: file.name },\n )}\n >\n <div className=\"bg-bg-surface rounded-lg shadow-xl max-w-3xl w-full max-h-[90vh] overflow-hidden flex flex-col animate-in zoom-in-95 duration-200\">\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 border-b border-border-seam\">\n <div className=\"flex items-center gap-3\">\n <Clock className=\"size-6 text-text-link\" />\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tWithFallback(\n t,\n \"files.version.versionHistory\",\n \"Version History\",\n )}\n </h2>\n <p className=\"text-sm text-text-secondary\">{file.name}</p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-2 text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n aria-label={tWithFallback(\n t,\n \"files.version.closeHistory\",\n \"Close version history\",\n )}\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto p-6\">\n {isLoading && (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-8 text-text-link animate-spin\" />\n </div>\n )}\n\n {error && (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <AlertCircle className=\"size-12 text-status-error-text mx-auto mb-4\" />\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.version.loadFailed\",\n \"Failed to load version history\",\n )}\n </p>\n </div>\n </div>\n )}\n\n {!isLoading && !error && versions.length === 0 && (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <Clock className=\"size-12 text-text-tertiary mx-auto mb-4\" />\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.version.noHistory\",\n \"No version history available\",\n )}\n </p>\n </div>\n </div>\n )}\n\n {!isLoading && !error && versions.length > 0 && (\n <div className=\"space-y-3\">\n {versions.map((version: (typeof versions)[number]) => (\n <div\n key={version.id}\n className={`p-4 border rounded-lg transition-all duration-200 ${\n version.isCurrentVersion\n ? \"border-action-primary-border bg-bg-accent\"\n : selectedVersionId === version.id\n ? \"border-border-seam bg-bg-sunken\"\n : \"border-border-seam hover:border-border-strong hover:bg-action-ghost-bg-hover\"\n }`}\n onClick={() => setSelectedVersionId(version.id)}\n >\n <div className=\"flex items-start justify-between gap-4\">\n {/* Version info */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 mb-2\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {tWithFallback(t, \"files.version.version\", \"Version\")}{\" \"}\n {version.versionNumber}\n </span>\n\n {version.isCurrentVersion && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 bg-action-primary-bg text-text-inverse text-xs font-medium rounded\">\n <CheckCircle2 className=\"size-3\" />\n {tWithFallback(\n t,\n \"files.version.current\",\n \"Current\",\n )}\n </span>\n )}\n\n {version.isMajorVersion && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 bg-status-info-bg-subtle text-status-info-text text-xs font-medium rounded\">\n <Tag className=\"size-3\" />\n {tWithFallback(t, \"files.version.major\", \"Major\")}\n </span>\n )}\n\n {version.versionLabel && (\n <span className=\"inline-flex items-center px-2 py-0.5 bg-status-success-bg-subtle text-status-success-text text-xs font-medium rounded\">\n {version.versionLabel}\n </span>\n )}\n </div>\n\n {version.changeMessage && (\n <p className=\"text-sm text-text-primary mb-2\">\n {version.changeMessage}\n </p>\n )}\n\n <div className=\"flex items-center gap-4 text-xs text-text-secondary\">\n <span className=\"tabular-nums\">\n {formatFileSize(version.size)}\n </span>\n <span>•</span>\n <span>{formatFileDate(version.createdAt)}</span>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleDownload(version.id);\n }}\n className=\"p-2 text-text-secondary hover:text-text-link hover:bg-bg-accent rounded-lg transition-colors duration-200\"\n title={tWithFallback(\n t,\n \"files.version.downloadThisVersion\",\n \"Download this version\",\n )}\n >\n <Download className=\"size-4\" />\n </button>\n\n {!version.isCurrentVersion && (\n <>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleRestore(version.id, version.versionNumber);\n }}\n disabled={restoreMutation.isPending}\n className=\"p-2 text-text-secondary hover:text-status-success-text hover:bg-status-success-bg-subtle rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tWithFallback(\n t,\n \"files.version.restoreThisVersion\",\n \"Restore this version\",\n )}\n >\n {restoreMutation.isPending ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <RotateCcw className=\"size-4\" />\n )}\n </button>\n\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleDelete(version.id, version.versionNumber);\n }}\n disabled={deleteMutation.isPending}\n className=\"p-2 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tWithFallback(\n t,\n \"files.version.deleteThisVersion\",\n \"Delete this version\",\n )}\n >\n {deleteMutation.isPending ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <Trash2 className=\"size-4\" />\n )}\n </button>\n </>\n )}\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"p-6 border-t border-border-seam bg-bg-sunken\">\n <div className=\"flex items-center justify-between text-sm text-text-secondary\">\n <span>\n {versionsData?.total || 0}{\" \"}\n {versionsData?.total !== 1\n ? tWithFallback(t, \"files.version.versions\", \"versions\")\n : tWithFallback(\n t,\n \"files.version.versionSingular\",\n \"version\",\n )}{\" \"}\n {tWithFallback(t, \"files.version.total\", \"total\")}\n </span>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"px-4 py-2 text-sm font-medium text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n >\n {tWithFallback(t, \"files.common.actions.close\", \"Close\")}\n </button>\n </div>\n </div>\n </div>\n\n {confirmAction && (\n <ConfirmDialog\n title={confirmAction.title}\n description={confirmAction.description}\n confirmLabel={confirmAction.confirmLabel}\n cancelLabel={tWithFallback(\n t,\n \"files.common.actions.cancel\",\n \"Cancel\",\n )}\n variant={confirmAction.variant}\n loading={confirmAction.loading}\n onConfirm={runConfirmAction}\n onCancel={() => {\n if (!confirmAction.loading) {\n setConfirmAction(null);\n }\n }}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,214 +1,216 @@
|
|
|
1
1
|
import { useFilesStore as e } from "../store/filesStore.js";
|
|
2
2
|
import { formatFileDate as t } from "../utils/index.js";
|
|
3
3
|
import { tWithFallback as n } from "../utils/i18n.js";
|
|
4
|
-
import { ActionMenu as
|
|
5
|
-
import { canDropOnFolder as
|
|
6
|
-
import { useMemo as
|
|
7
|
-
import { Activity as
|
|
8
|
-
import { Fragment as
|
|
9
|
-
import { useI18n as
|
|
4
|
+
import { ActionMenu as r } from "./ActionMenu.js";
|
|
5
|
+
import { canDropOnFolder as i, getDragOperation as a } from "../hooks/useDragDrop.js";
|
|
6
|
+
import { useMemo as ee, useState as o } from "react";
|
|
7
|
+
import { Activity as s, ChevronRight as c, Edit as l, Folder as u, FolderInput as d, FolderOpen as f, Info as p, RotateCcw as m, Share2 as h, Star as g, Trash2 as _ } from "lucide-react";
|
|
8
|
+
import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
9
|
+
import { useI18n as x } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
10
|
+
import { GlassCard as te } from "@burdenoff/fe-libs/ui";
|
|
10
11
|
//#region src/files/components/FolderCard.tsx
|
|
11
|
-
var
|
|
12
|
-
let { t:
|
|
13
|
-
|
|
14
|
-
},
|
|
15
|
-
e.stopPropagation(),
|
|
12
|
+
var S = ({ folder: S, isSelected: C = !1, isDragging: w = !1, isDropTarget: T = !1, draggedItem: E = null, onSelect: D, onOpen: O, onShare: k, onDelete: A, onRestore: j, onRename: M, onFavorite: N, onCopy: ne, onMove: P, onInfo: F, onActivity: I, onDragStart: L, onDragEnd: R, onDrop: z, onContextMenu: B }) => {
|
|
13
|
+
let { t: V } = x(), [H, U] = o(!1), [W, G] = o(!1), { toggleFolderSelection: K } = e(), re = i(E, S), q = T && re, J = S.filesCount ?? 0, Y = S.subfoldersCount ?? 0, X = J + Y, Z = () => {
|
|
14
|
+
O && O(S);
|
|
15
|
+
}, ie = (e) => {
|
|
16
|
+
e.stopPropagation(), D ? D(S.id) : K(S.id);
|
|
16
17
|
}, Q = (e, t) => {
|
|
17
18
|
e.stopPropagation(), t();
|
|
18
|
-
},
|
|
19
|
-
e.dataTransfer.effectAllowed = "copyMove", e.dataTransfer.setData("application/x-file-id",
|
|
20
|
-
let t =
|
|
21
|
-
e.dataTransfer.setData("application/x-drag-operation", t),
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
}, $ = (e) => {
|
|
25
|
-
q && (e.preventDefault(), e.dataTransfer.dropEffect = T?.operation === "copy" ? "copy" : "move");
|
|
19
|
+
}, ae = (e) => {
|
|
20
|
+
e.dataTransfer.effectAllowed = "copyMove", e.dataTransfer.setData("application/x-file-id", S.id), e.dataTransfer.setData("application/x-file-type", "folder");
|
|
21
|
+
let t = a(e);
|
|
22
|
+
e.dataTransfer.setData("application/x-drag-operation", t), L?.(S, t);
|
|
23
|
+
}, $ = () => {
|
|
24
|
+
R?.();
|
|
26
25
|
}, oe = (e) => {
|
|
26
|
+
q && (e.preventDefault(), e.dataTransfer.dropEffect = E?.operation === "copy" ? "copy" : "move");
|
|
27
|
+
}, se = (e) => {
|
|
27
28
|
q && e.preventDefault();
|
|
28
|
-
},
|
|
29
|
-
e.preventDefault(), q &&
|
|
30
|
-
},
|
|
31
|
-
e.preventDefault(), e.stopPropagation(),
|
|
32
|
-
},
|
|
33
|
-
...
|
|
34
|
-
label: n(
|
|
35
|
-
icon: /* @__PURE__ */
|
|
36
|
-
onClick: () =>
|
|
37
|
-
}] : [],
|
|
38
|
-
...N ? [{
|
|
39
|
-
label: n(B, "files.card.moveTo", "Move to"),
|
|
40
|
-
icon: /* @__PURE__ */ v(u, { className: "size-4" }),
|
|
41
|
-
onClick: () => N(x)
|
|
29
|
+
}, ce = () => {}, le = (e) => {
|
|
30
|
+
e.preventDefault(), q && z?.(S);
|
|
31
|
+
}, ue = (e) => {
|
|
32
|
+
e.preventDefault(), e.stopPropagation(), B?.(S, e.clientX, e.clientY);
|
|
33
|
+
}, de = H ? f : u, fe = ee(() => [
|
|
34
|
+
...k ? [{
|
|
35
|
+
label: n(V, "files.card.share", "Share"),
|
|
36
|
+
icon: /* @__PURE__ */ y(h, { className: "size-4" }),
|
|
37
|
+
onClick: () => k(S)
|
|
42
38
|
}] : [],
|
|
43
|
-
...
|
|
44
|
-
label: n(
|
|
45
|
-
icon: /* @__PURE__ */
|
|
46
|
-
onClick: () =>
|
|
47
|
-
divider: !0
|
|
39
|
+
...P ? [{
|
|
40
|
+
label: n(V, "files.card.moveTo", "Move to"),
|
|
41
|
+
icon: /* @__PURE__ */ y(d, { className: "size-4" }),
|
|
42
|
+
onClick: () => P(S)
|
|
48
43
|
}] : [],
|
|
49
44
|
...M ? [{
|
|
50
|
-
label:
|
|
51
|
-
icon: /* @__PURE__ */
|
|
52
|
-
onClick: () => M(
|
|
45
|
+
label: n(V, "files.card.rename", "Rename"),
|
|
46
|
+
icon: /* @__PURE__ */ y(l, { className: "size-4" }),
|
|
47
|
+
onClick: () => M(S),
|
|
48
|
+
divider: !0
|
|
53
49
|
}] : [],
|
|
54
|
-
...
|
|
55
|
-
label: n(
|
|
56
|
-
icon: /* @__PURE__ */
|
|
57
|
-
onClick: () =>
|
|
50
|
+
...N ? [{
|
|
51
|
+
label: S.starred ? n(V, "files.card.removeFromStarred", "Remove from starred") : n(V, "files.card.addToStarred", "Add to starred"),
|
|
52
|
+
icon: /* @__PURE__ */ y(g, { className: "size-4" }),
|
|
53
|
+
onClick: () => N(S)
|
|
58
54
|
}] : [],
|
|
59
55
|
...F ? [{
|
|
60
|
-
label: n(
|
|
61
|
-
icon: /* @__PURE__ */
|
|
62
|
-
onClick: () => F(
|
|
56
|
+
label: n(V, "files.card.details", "Details"),
|
|
57
|
+
icon: /* @__PURE__ */ y(p, { className: "size-4" }),
|
|
58
|
+
onClick: () => F(S)
|
|
63
59
|
}] : [],
|
|
64
|
-
...
|
|
65
|
-
label: n(
|
|
66
|
-
icon: /* @__PURE__ */
|
|
67
|
-
onClick: () =>
|
|
60
|
+
...I ? [{
|
|
61
|
+
label: n(V, "files.card.activity", "Activity"),
|
|
62
|
+
icon: /* @__PURE__ */ y(s, { className: "size-4" }),
|
|
63
|
+
onClick: () => I(S)
|
|
64
|
+
}] : [],
|
|
65
|
+
...A ? [{
|
|
66
|
+
label: n(V, "files.card.delete", "Delete"),
|
|
67
|
+
icon: /* @__PURE__ */ y(_, { className: "size-4" }),
|
|
68
|
+
onClick: () => A(S),
|
|
68
69
|
variant: "danger",
|
|
69
70
|
divider: !0
|
|
70
71
|
}] : [],
|
|
71
|
-
...
|
|
72
|
-
label: n(
|
|
73
|
-
icon: /* @__PURE__ */
|
|
74
|
-
onClick: () =>
|
|
72
|
+
...j ? [{
|
|
73
|
+
label: n(V, "files.card.restore", "Restore"),
|
|
74
|
+
icon: /* @__PURE__ */ y(m, { className: "size-4" }),
|
|
75
|
+
onClick: () => j(S)
|
|
75
76
|
}] : []
|
|
76
77
|
], [
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
N,
|
|
80
|
-
j,
|
|
81
|
-
M,
|
|
78
|
+
S,
|
|
79
|
+
k,
|
|
82
80
|
P,
|
|
81
|
+
M,
|
|
82
|
+
N,
|
|
83
83
|
F,
|
|
84
|
-
|
|
84
|
+
I,
|
|
85
85
|
A,
|
|
86
|
-
|
|
86
|
+
j,
|
|
87
|
+
V
|
|
87
88
|
]);
|
|
88
|
-
return /* @__PURE__ */
|
|
89
|
-
|
|
89
|
+
return /* @__PURE__ */ y(te, {
|
|
90
|
+
treatment: "insight",
|
|
91
|
+
className: `group cursor-pointer focus-within:shadow-elevation-2 ${w ? "opacity-50" : ""} ${q ? "border-action-primary-bg ring-2 ring-action-primary-bg ring-offset-2" : C ? "border-action-primary-bg bg-bg-accent" : "border-border-seam"}`,
|
|
90
92
|
onClick: Z,
|
|
91
|
-
onContextMenu:
|
|
92
|
-
onMouseEnter: () =>
|
|
93
|
+
onContextMenu: ue,
|
|
94
|
+
onMouseEnter: () => U(!0),
|
|
93
95
|
onMouseLeave: () => {
|
|
94
|
-
|
|
96
|
+
W || U(!1);
|
|
95
97
|
},
|
|
96
|
-
onFocus: () =>
|
|
98
|
+
onFocus: () => U(!0),
|
|
97
99
|
onBlur: (e) => {
|
|
98
|
-
!e.currentTarget.contains(e.relatedTarget) && !
|
|
100
|
+
!e.currentTarget.contains(e.relatedTarget) && !W && U(!1);
|
|
99
101
|
},
|
|
100
102
|
onKeyDown: (e) => {
|
|
101
103
|
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), Z());
|
|
102
104
|
},
|
|
103
|
-
draggable: !!
|
|
104
|
-
onDragStart:
|
|
105
|
-
onDragEnd:
|
|
106
|
-
onDragOver:
|
|
107
|
-
onDragEnter:
|
|
108
|
-
onDragLeave:
|
|
109
|
-
onDrop:
|
|
105
|
+
draggable: !!L,
|
|
106
|
+
onDragStart: ae,
|
|
107
|
+
onDragEnd: $,
|
|
108
|
+
onDragOver: oe,
|
|
109
|
+
onDragEnter: se,
|
|
110
|
+
onDragLeave: ce,
|
|
111
|
+
onDrop: le,
|
|
110
112
|
role: "button",
|
|
111
113
|
tabIndex: 0,
|
|
112
|
-
"aria-label": `${n(
|
|
113
|
-
children: /* @__PURE__ */
|
|
114
|
+
"aria-label": `${n(V, "files.browser.openFolder", "Open folder")} ${S.name}`,
|
|
115
|
+
children: /* @__PURE__ */ b("div", {
|
|
114
116
|
className: "flex items-center gap-3 p-3",
|
|
115
117
|
children: [
|
|
116
|
-
/* @__PURE__ */
|
|
117
|
-
className: `flex-shrink-0 transition-opacity ${
|
|
118
|
+
/* @__PURE__ */ y("div", {
|
|
119
|
+
className: `flex-shrink-0 transition-opacity ${C ? "opacity-100" : "opacity-0 group-hover:opacity-100 group-focus-within:opacity-100"}`,
|
|
118
120
|
onClick: (e) => e.stopPropagation(),
|
|
119
|
-
children: /* @__PURE__ */
|
|
121
|
+
children: /* @__PURE__ */ y("input", {
|
|
120
122
|
type: "checkbox",
|
|
121
|
-
checked:
|
|
122
|
-
onChange:
|
|
123
|
+
checked: C,
|
|
124
|
+
onChange: ie,
|
|
123
125
|
className: "size-4 rounded border-border-default bg-bg-surface text-action-primary-bg focus:ring-2 focus:ring-action-primary-bg focus:ring-offset-2",
|
|
124
|
-
"aria-label": `Select ${
|
|
126
|
+
"aria-label": `Select ${S.name}`
|
|
125
127
|
})
|
|
126
128
|
}),
|
|
127
|
-
/* @__PURE__ */
|
|
129
|
+
/* @__PURE__ */ y("div", {
|
|
128
130
|
className: "flex-shrink-0 flex items-center justify-center size-10 rounded-lg bg-action-primary-bg/10",
|
|
129
|
-
children: /* @__PURE__ */
|
|
131
|
+
children: /* @__PURE__ */ y(de, { className: "size-5 text-action-primary-bg" })
|
|
130
132
|
}),
|
|
131
|
-
/* @__PURE__ */
|
|
133
|
+
/* @__PURE__ */ b("div", {
|
|
132
134
|
className: "flex-1 min-w-0 pr-1",
|
|
133
|
-
children: [/* @__PURE__ */
|
|
135
|
+
children: [/* @__PURE__ */ b("div", {
|
|
134
136
|
className: "flex items-center gap-1.5",
|
|
135
|
-
children: [/* @__PURE__ */
|
|
137
|
+
children: [/* @__PURE__ */ y("span", {
|
|
136
138
|
className: "text-sm font-medium text-text-primary truncate",
|
|
137
|
-
children:
|
|
138
|
-
}),
|
|
139
|
-
}), /* @__PURE__ */
|
|
139
|
+
children: S.name
|
|
140
|
+
}), S.starred && /* @__PURE__ */ y(g, { className: "size-3 flex-shrink-0 fill-yellow-400 text-yellow-400" })]
|
|
141
|
+
}), /* @__PURE__ */ b("div", {
|
|
140
142
|
className: "flex items-center gap-1 mt-0.5 text-xs text-text-secondary",
|
|
141
143
|
children: [
|
|
142
|
-
Y > 0 && /* @__PURE__ */
|
|
144
|
+
Y > 0 && /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ b("span", { children: [
|
|
143
145
|
Y,
|
|
144
146
|
" ",
|
|
145
|
-
Y === 1 ? n(
|
|
146
|
-
] }), J > 0 && /* @__PURE__ */
|
|
147
|
+
Y === 1 ? n(V, "files.folder.subfolder", "folder") : n(V, "files.folder.subfolders", "folders")
|
|
148
|
+
] }), J > 0 && /* @__PURE__ */ y("span", {
|
|
147
149
|
className: "text-border-subtle",
|
|
148
150
|
children: "·"
|
|
149
151
|
})] }),
|
|
150
|
-
J > 0 && /* @__PURE__ */
|
|
152
|
+
J > 0 && /* @__PURE__ */ b("span", { children: [
|
|
151
153
|
J,
|
|
152
154
|
" ",
|
|
153
|
-
J === 1 ? n(
|
|
155
|
+
J === 1 ? n(V, "files.folder.file", "file") : n(V, "files.folder.files", "files")
|
|
154
156
|
] }),
|
|
155
|
-
X === 0 && /* @__PURE__ */
|
|
156
|
-
/* @__PURE__ */
|
|
157
|
+
X === 0 && /* @__PURE__ */ y("span", { children: n(V, "files.folder.empty", "Empty") }),
|
|
158
|
+
/* @__PURE__ */ y("span", {
|
|
157
159
|
className: "text-border-subtle",
|
|
158
160
|
children: "·"
|
|
159
161
|
}),
|
|
160
|
-
/* @__PURE__ */
|
|
162
|
+
/* @__PURE__ */ y("span", {
|
|
161
163
|
className: "truncate",
|
|
162
|
-
children: t(
|
|
164
|
+
children: t(S.updatedAt)
|
|
163
165
|
})
|
|
164
166
|
]
|
|
165
167
|
})]
|
|
166
168
|
}),
|
|
167
|
-
/* @__PURE__ */
|
|
169
|
+
/* @__PURE__ */ b("div", {
|
|
168
170
|
className: "flex-shrink-0 flex items-center gap-1",
|
|
169
171
|
children: [
|
|
170
|
-
|
|
172
|
+
H && j && /* @__PURE__ */ y("button", {
|
|
171
173
|
type: "button",
|
|
172
|
-
onClick: (e) => Q(e, () =>
|
|
174
|
+
onClick: (e) => Q(e, () => j(S)),
|
|
173
175
|
className: "p-1.5 hover:bg-status-success-bg-subtle rounded transition-colors",
|
|
174
|
-
"aria-label": n(
|
|
175
|
-
title: n(
|
|
176
|
-
children: /* @__PURE__ */
|
|
176
|
+
"aria-label": n(V, "files.card.restore", "Restore"),
|
|
177
|
+
title: n(V, "files.card.restore", "Restore"),
|
|
178
|
+
children: /* @__PURE__ */ y(m, { className: "size-4 text-status-success-text" })
|
|
177
179
|
}),
|
|
178
|
-
|
|
179
|
-
onClick: (e) => Q(e, () =>
|
|
180
|
+
H && k && /* @__PURE__ */ y("button", {
|
|
181
|
+
onClick: (e) => Q(e, () => k(S)),
|
|
180
182
|
className: "p-1.5 hover:bg-action-ghost-bg-hover rounded transition-colors",
|
|
181
|
-
"aria-label": n(
|
|
182
|
-
title: n(
|
|
183
|
-
children: /* @__PURE__ */
|
|
183
|
+
"aria-label": n(V, "files.card.share", "Share"),
|
|
184
|
+
title: n(V, "files.card.share", "Share"),
|
|
185
|
+
children: /* @__PURE__ */ y(h, { className: "size-4 text-text-secondary" })
|
|
184
186
|
}),
|
|
185
|
-
|
|
186
|
-
onClick: (e) => Q(e, () =>
|
|
187
|
+
H && M && /* @__PURE__ */ y("button", {
|
|
188
|
+
onClick: (e) => Q(e, () => M(S)),
|
|
187
189
|
className: "p-1.5 hover:bg-action-ghost-bg-hover rounded transition-colors",
|
|
188
|
-
"aria-label": n(
|
|
189
|
-
title: n(
|
|
190
|
-
children: /* @__PURE__ */
|
|
190
|
+
"aria-label": n(V, "files.card.rename", "Rename"),
|
|
191
|
+
title: n(V, "files.card.rename", "Rename"),
|
|
192
|
+
children: /* @__PURE__ */ y(l, { className: "size-4 text-text-secondary" })
|
|
191
193
|
}),
|
|
192
|
-
/* @__PURE__ */
|
|
194
|
+
/* @__PURE__ */ y("div", {
|
|
193
195
|
onClick: (e) => e.stopPropagation(),
|
|
194
|
-
children: /* @__PURE__ */
|
|
195
|
-
items:
|
|
196
|
+
children: /* @__PURE__ */ y(r, {
|
|
197
|
+
items: fe,
|
|
196
198
|
align: "right",
|
|
197
|
-
triggerClassName: !
|
|
198
|
-
isOpen:
|
|
199
|
+
triggerClassName: !H && !W ? "opacity-0 group-hover:opacity-100" : "",
|
|
200
|
+
isOpen: W,
|
|
199
201
|
onToggle: (e) => {
|
|
200
|
-
|
|
202
|
+
G(e), e && U(!0);
|
|
201
203
|
}
|
|
202
204
|
})
|
|
203
205
|
})
|
|
204
206
|
]
|
|
205
207
|
}),
|
|
206
|
-
/* @__PURE__ */
|
|
208
|
+
/* @__PURE__ */ y(c, { className: "flex-shrink-0 size-4 text-text-muted opacity-0 group-hover:opacity-100 transition-opacity" })
|
|
207
209
|
]
|
|
208
210
|
})
|
|
209
211
|
});
|
|
210
212
|
};
|
|
211
213
|
//#endregion
|
|
212
|
-
export {
|
|
214
|
+
export { S as FolderCard };
|
|
213
215
|
|
|
214
216
|
//# sourceMappingURL=FolderCard.js.map
|