|
@@ -70,7 +70,6 @@ import { FolderReadmePanel } from '../components/FolderReadmePanel';
|
|
|
import { LibraryTagsModal } from '../components/LibraryTagsModal';
|
|
import { LibraryTagsModal } from '../components/LibraryTagsModal';
|
|
|
import { PurgeOldFilesModal } from '../components/PurgeOldFilesModal';
|
|
import { PurgeOldFilesModal } from '../components/PurgeOldFilesModal';
|
|
|
import { useToast } from '../contexts/ToastContext';
|
|
import { useToast } from '../contexts/ToastContext';
|
|
|
-import { useIsMobile } from '../hooks/useIsMobile';
|
|
|
|
|
import { usePageFileDrop } from '../hooks/usePageFileDrop';
|
|
import { usePageFileDrop } from '../hooks/usePageFileDrop';
|
|
|
import { useAuth } from '../contexts/AuthContext';
|
|
import { useAuth } from '../contexts/AuthContext';
|
|
|
import { formatDuration, parseUTCDate, formatDate } from '../utils/date';
|
|
import { formatDuration, parseUTCDate, formatDate } from '../utils/date';
|
|
@@ -662,7 +661,7 @@ function FolderTreeItem({ folder, selectedFolderId, onSelect, onDelete, onLink,
|
|
|
<Link2 className="w-3.5 h-3.5 text-bambu-gray hover:text-bambu-green" />
|
|
<Link2 className="w-3.5 h-3.5 text-bambu-gray hover:text-bambu-green" />
|
|
|
</button>
|
|
</button>
|
|
|
)}
|
|
)}
|
|
|
- <div className={`flex-shrink-0 flex items-center gap-0.5 transition-opacity ${wrapNames ? '' : 'opacity-0 group-hover:opacity-100'}`} onClick={(e) => e.stopPropagation()}>
|
|
|
|
|
|
|
+ <div className={`flex-shrink-0 flex items-center gap-0.5 transition-opacity ${wrapNames ? '' : 'can-hover:opacity-0 group-hover:opacity-100 group-focus-within:opacity-100'}`} onClick={(e) => e.stopPropagation()}>
|
|
|
<div className="relative">
|
|
<div className="relative">
|
|
|
<button
|
|
<button
|
|
|
onClick={() => setShowActions(!showActions)}
|
|
onClick={() => setShowActions(!showActions)}
|
|
@@ -748,7 +747,6 @@ function isSlicedFilename(filename: string): boolean {
|
|
|
interface FileCardProps {
|
|
interface FileCardProps {
|
|
|
file: LibraryFileListItem;
|
|
file: LibraryFileListItem;
|
|
|
isSelected: boolean;
|
|
isSelected: boolean;
|
|
|
- isMobile: boolean;
|
|
|
|
|
onSelect: (id: number) => void;
|
|
onSelect: (id: number) => void;
|
|
|
onDelete: (id: number) => void;
|
|
onDelete: (id: number) => void;
|
|
|
onDownload: (id: number) => void;
|
|
onDownload: (id: number) => void;
|
|
@@ -770,7 +768,7 @@ interface FileCardProps {
|
|
|
t: TFunction;
|
|
t: TFunction;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function FileCard({ file, isSelected, isMobile, onSelect, onDelete, onDownload, onPrint, onSlice, onOpenInSlicer, onRunPipeline, useSlicerApi, canSlice, onPreview3d, onRename, onGenerateThumbnail, onTagClick, thumbnailVersion, hasPermission, canModify, authEnabled, showModified, t }: FileCardProps) {
|
|
|
|
|
|
|
+function FileCard({ file, isSelected, onSelect, onDelete, onDownload, onPrint, onSlice, onOpenInSlicer, onRunPipeline, useSlicerApi, canSlice, onPreview3d, onRename, onGenerateThumbnail, onTagClick, thumbnailVersion, hasPermission, canModify, authEnabled, showModified, t }: FileCardProps) {
|
|
|
// Viewport coordinates rather than a flag, because the menu is rendered by
|
|
// Viewport coordinates rather than a flag, because the menu is rendered by
|
|
|
// `ContextMenu` at `position: fixed` and anchored to the button (#2846). The
|
|
// `ContextMenu` at `position: fixed` and anchored to the button (#2846). The
|
|
|
// card it belongs to is only ~270px tall for a bare STL, which is shorter
|
|
// card it belongs to is only ~270px tall for a bare STL, which is shorter
|
|
@@ -954,8 +952,8 @@ function FileCard({ file, isSelected, isMobile, onSelect, onDelete, onDownload,
|
|
|
)}
|
|
)}
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- {/* Actions - always visible on mobile, hover on desktop */}
|
|
|
|
|
- <div className={`absolute bottom-2 right-2 transition-opacity ${isMobile ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'}`} onClick={(e) => e.stopPropagation()}>
|
|
|
|
|
|
|
+ {/* Actions - hover-revealed with a mouse, always there without one (#2865) */}
|
|
|
|
|
+ <div className="absolute bottom-2 right-2 transition-opacity can-hover:opacity-0 group-hover:opacity-100 group-focus-within:opacity-100" onClick={(e) => e.stopPropagation()}>
|
|
|
<button
|
|
<button
|
|
|
onClick={(e) => {
|
|
onClick={(e) => {
|
|
|
// No open/close toggle: the menu's own outside-mousedown handler
|
|
// No open/close toggle: the menu's own outside-mousedown handler
|
|
@@ -972,11 +970,11 @@ function FileCard({ file, isSelected, isMobile, onSelect, onDelete, onDownload,
|
|
|
)}
|
|
)}
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- {/* Selection checkbox - always visible on mobile, hover on desktop */}
|
|
|
|
|
|
|
+ {/* Selection checkbox - hover-revealed with a mouse, always there without one (#2865) */}
|
|
|
<div className={`absolute top-2 left-2 w-5 h-5 rounded border-2 flex items-center justify-center transition-all ${
|
|
<div className={`absolute top-2 left-2 w-5 h-5 rounded border-2 flex items-center justify-center transition-all ${
|
|
|
isSelected
|
|
isSelected
|
|
|
? 'bg-bambu-green border-bambu-green'
|
|
? 'bg-bambu-green border-bambu-green'
|
|
|
- : `border-white/30 bg-black/30 ${isMobile ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'}`
|
|
|
|
|
|
|
+ : 'border-white/30 bg-black/30 can-hover:opacity-0 group-hover:opacity-100 group-focus-within:opacity-100'
|
|
|
}`}>
|
|
}`}>
|
|
|
{isSelected && <div className="w-2 h-2 bg-white rounded-sm" />}
|
|
{isSelected && <div className="w-2 h-2 bg-white rounded-sm" />}
|
|
|
</div>
|
|
</div>
|
|
@@ -1109,9 +1107,6 @@ export function FileManagerPage() {
|
|
|
() => localStorage.getItem('library-show-modified') === 'true'
|
|
() => localStorage.getItem('library-show-modified') === 'true'
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
- // Mobile detection for touch-friendly UI
|
|
|
|
|
- const isMobile = useIsMobile();
|
|
|
|
|
-
|
|
|
|
|
// Update selectedFolderId when URL parameter changes (e.g., navigating from Project or Archive page)
|
|
// Update selectedFolderId when URL parameter changes (e.g., navigating from Project or Archive page)
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
const folderParam = searchParams.get('folder');
|
|
const folderParam = searchParams.get('folder');
|
|
@@ -2433,7 +2428,6 @@ export function FileManagerPage() {
|
|
|
key={file.id}
|
|
key={file.id}
|
|
|
file={file}
|
|
file={file}
|
|
|
isSelected={selectedFiles.includes(file.id)}
|
|
isSelected={selectedFiles.includes(file.id)}
|
|
|
- isMobile={isMobile}
|
|
|
|
|
t={t}
|
|
t={t}
|
|
|
onSelect={handleFileSelect}
|
|
onSelect={handleFileSelect}
|
|
|
onDelete={(id) => setDeleteConfirm({ type: 'file', id })}
|
|
onDelete={(id) => setDeleteConfirm({ type: 'file', id })}
|