refactor(editor): drop the file library's location row

The row carrying "All files" was the last of the library's own chrome, and with
the controls in the workbench bar it was a line on its own holding a breadcrumb
and a tab name.

The folder tree in the file sidebar covers what it did: it names where you are,
reaches any folder including the root, and accepts a drop the way the breadcrumb
ancestors did. The tab strip already says which tab is open.

Takes the Breadcrumbs component and its styles with it, and the strings only it
read.
This commit is contained in:
Reece
2026-09-01 14:43:38 +01:00
parent 16b297f488
commit 9f462071fe
3 changed files with 1 additions and 196 deletions
@@ -4287,7 +4287,6 @@ allFiles = "All files"
back = "Back"
backToFolder = "Back to {{folder}}"
backToMyFiles = "Back to File library"
breadcrumbs = "Folder path"
bulkActions = "Actions"
cancel = "Cancel"
classification = "Classification"
@@ -4514,9 +4513,6 @@ server = "Server error during folder sync."
[filesPage.tabName]
local = "Local"
recent = "Recent"
shared = "Shared with me"
sharedByMe = "Shared by me"
[filesPage.tabs]
all = "All"
@@ -29,7 +29,6 @@ import DriveFileMoveIcon from "@mui/icons-material/DriveFileMove";
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight";
import { FilesToolbarBulkMenu } from "@app/components/filesPage/FilesToolbarBulkMenu";
import { FilesToolbarCount } from "@app/components/filesPage/FilesToolbarCount";
import { FilesToolbarFilterMenu } from "@app/components/filesPage/FilesToolbarFilterMenu";
@@ -76,10 +75,7 @@ import { duplicateStoredFile } from "@app/utils/duplicateFile";
import { downloadFileFromStorage } from "@app/utils/downloadUtils";
import { fileStorage } from "@app/services/fileStorage";
import { materializeServerStubs } from "@app/services/fileSyncService";
import {
FILES_PAGE_DRAG_TYPE,
parseFilesPageDragPayload,
} from "@app/components/filesPage/dragDrop";
import {} from "@app/components/filesPage/dragDrop";
import { clearFilesPageReturnRoute } from "@app/components/filesPage/filesPageReturnRoute";
import { EDITOR_BASENAME } from "@app/routes/editorBasename";
import "@app/components/filesPage/FilesPage.css";
@@ -986,25 +982,6 @@ export default function FileManagerView() {
return (
<div className="files-page" ref={dropZoneRef}>
{/* Folder context, not a bar: the controls that used to sit beside this are
registered into the workbench bar the other views use. */}
<div className="files-page-location">
{(currentTab === "all" || currentTab === "cloud") && <Breadcrumbs />}
{(currentTab === "local" ||
currentTab === "recent" ||
currentTab === "shared" ||
currentTab === "sharedByMe") && (
<span className="files-page-location-name">
{currentTab === "local"
? t("filesPage.tabName.local", "Local")
: currentTab === "recent"
? t("filesPage.tabName.recent", "Recent")
: currentTab === "shared"
? t("filesPage.tabName.shared", "Shared with me")
: t("filesPage.tabName.sharedByMe", "Shared by me")}
</span>
)}
</div>
<input
ref={fileInputRef}
type="file"
@@ -1799,92 +1776,3 @@ export default function FileManagerView() {
</div>
);
}
function Breadcrumbs() {
const { t } = useTranslation();
const folders = useFolders();
const filesPage = useFilesPage();
const trail = folders.breadcrumbs;
return (
<nav
className="files-page-breadcrumbs"
aria-label={t("filesPage.breadcrumbs", "Folder path")}
>
{trail.map((entry, idx) => {
const isLast = idx === trail.length - 1;
return (
<React.Fragment key={entry.id ?? "root"}>
<Button
variant="tertiary"
className={`files-page-breadcrumb${isLast ? " is-current" : ""}`}
onClick={() => folders.setCurrentFolderId(entry.id)}
onDragOver={(e) => {
if (e.dataTransfer.types.includes(FILES_PAGE_DRAG_TYPE)) {
e.preventDefault();
e.dataTransfer.dropEffect = "move";
}
}}
onDrop={(e) => {
e.preventDefault();
const payload = parseFilesPageDragPayload(e.dataTransfer);
if (!payload) return;
if (payload.kind === "files") {
// Route through moveFilesTo (→ IndexedDBContext.moveFilesToFolder)
// so the revision bumps and the grid refreshes. Surface
// rejection via the banner - console-only was invisible
// to non-dev users.
void filesPage
.moveFilesTo(payload.fileIds, entry.id)
.catch((err) => {
console.error("[breadcrumb] drop failed", err);
folders.setError(
err instanceof Error
? t("filesPage.error.moveFilesFailedDetail", {
message: err.message,
defaultValue: `Could not move files: ${err.message}`,
})
: t(
"filesPage.error.moveFilesFailed",
"Could not move files.",
),
);
});
} else if (payload.kind === "folder") {
// Route through moveFolderTo so the client-side cycle guard fires
// before the server call - otherwise dragging an ancestor onto a
// child crumb shows the generic banner instead of the localized
// "Can't move a folder into one of its own subfolders." message.
void filesPage
.moveFolderTo(payload.folderId, entry.id)
.catch((err) => {
console.error("[breadcrumb] folder drop failed", err);
folders.setError(
err instanceof Error
? t("filesPage.error.moveFolderFailedDetail", {
message: err.message,
defaultValue: `Could not move folder: ${err.message}`,
})
: t(
"filesPage.error.moveFolderFailed",
"Could not move folder.",
),
);
});
}
}}
>
{entry.name}
</Button>
{!isLast && (
<KeyboardArrowRightIcon
className="files-page-breadcrumb-sep"
fontSize="small"
aria-hidden="true"
/>
)}
</React.Fragment>
);
})}
</nav>
);
}
@@ -8,68 +8,6 @@
overflow: hidden;
}
/* Where you are in the library. Not a bar - the controls that used to share this
row are registered into the workbench bar, and the search lives there too. */
.files-page-location {
display: flex;
align-items: center;
min-height: 2rem;
padding: 0.25rem 0.75rem 0;
min-width: 0;
}
.files-page-location-name {
font-size: 0.95rem;
font-weight: 600;
padding: 0.25rem 0.5rem;
color: var(--c-text);
}
.files-page-breadcrumbs {
display: flex;
align-items: center;
gap: 0.25rem;
font-size: 0.95rem;
min-width: 0;
overflow-x: auto;
scrollbar-width: none;
}
.files-page-breadcrumbs::-webkit-scrollbar {
display: none;
}
.files-page-breadcrumb {
background: none;
border: none;
padding: 0.25rem 0.5rem;
border-radius: 0.375rem;
color: var(--c-text-muted);
cursor: pointer;
font-size: inherit;
font-weight: 500;
transition:
background-color 0.12s ease,
color 0.12s ease;
white-space: nowrap;
max-width: 18rem;
overflow: hidden;
text-overflow: ellipsis;
}
.files-page-breadcrumb:hover,
.files-page-breadcrumb.is-current {
background: var(--c-hover);
color: var(--c-text);
}
.files-page-breadcrumb-sep {
color: var(--c-text-subtle);
font-size: 1rem !important;
width: 1rem;
height: 1rem;
opacity: 0.55;
flex-shrink: 0;
}
.files-page-body {
display: flex;
flex: 1 1 auto;
@@ -1344,23 +1282,6 @@
navigation, so the in-header Home/Apps/Close trio is duplicated
and the first to go. Same for "Upload" - the user can use the
centre drop overlay. */
/* ── Mobile + tablet chrome (≤1024px = useIsMobile) ──────────────────
The desktop header is a 3-column grid whose middle track can grow to
40rem. Below ~1024px that track eats the row: the breadcrumb column
collapsed to ~36px (wrapping "All files" to two lines) and the action
column overflowed, pushing Upload off the right edge. One flex row
instead - breadcrumb and actions keep their intrinsic width and the
search takes whatever is left. Ends at the app's mobile breakpoint so
it matches the layout HomePage is already rendering. */
@media (max-width: 1024px) {
.files-page-breadcrumbs {
flex: 0 1 auto;
font-size: 0.85rem;
flex-wrap: nowrap;
overflow-x: auto;
min-width: 0;
}
}
@media (max-width: 640px) {
/* Grid: single column on very narrow phones; two columns from ~440px */