Skip to content

Commit

Permalink
Move ContextMenu to the CardBody
Browse files Browse the repository at this point in the history
The ContextMenu has nothing to do with the main app, it appears in the
`CardBody` or better said DirectoryView. This allows us to later to
consolidate the event listeners and if the ContextMenu should be shown
to the NavigatorCardBody instead of the app itself.

Note: later rename CardBody to DirectoryView.
  • Loading branch information
jelly committed Feb 1, 2024
1 parent 0a9049e commit 8167c2d
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 106 deletions.
100 changes: 7 additions & 93 deletions src/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,27 @@

import cockpit from "cockpit";
import { superuser } from "superuser";
import { useDialogs } from "dialogs.jsx";
import React, { useEffect, useState } from "react";
import {
Card,
MenuItem, MenuList,
Page, PageSection,
Sidebar, SidebarPanel, SidebarContent,
Divider, AlertGroup, Alert, AlertActionCloseButton
AlertGroup, Alert, AlertActionCloseButton
} from "@patternfly/react-core";
import { ExclamationCircleIcon } from "@patternfly/react-icons";

import { EmptyStatePanel } from "cockpit-components-empty-state.jsx";
import { ContextMenu } from "./navigatorContextMenu.jsx";
import { NavigatorBreadcrumbs } from "./navigatorBreadcrumbs.jsx";
import { NavigatorCardBody } from "./navigator-card-body.jsx";
import {
copyItem, createDirectory, createLink, deleteItem, editPermissions, pasteItem, renameItem
} from "./fileActions.jsx";
import { SidebarPanelDetails } from "./sidebar.jsx";
import { NavigatorCardHeader } from "./header.jsx";
import { usePageLocation } from "hooks.js";
import { fsinfo } from "./fsinfo";

const _ = cockpit.gettext;

superuser.reload_page_on_change();

export const Application = () => {
const { options } = usePageLocation();
const Dialogs = useDialogs();
const [loading, setLoading] = useState(true);
const [loadingFiles, setLoadingFiles] = useState(true);
const [errorMessage, setErrorMessage] = useState();
Expand Down Expand Up @@ -120,87 +111,9 @@ export const Application = () => {
? files.filter(file => !file.name.startsWith("."))
: files;

const _createDirectory = () => createDirectory(Dialogs, currentDir, selectedContext || selected);
const _createLink = () => createLink(Dialogs, currentDir, files, selectedContext);
const _copyItem = () => {
copyItem(setClipboard, selected.length > 1
? selected.map(s => currentDir + s.name)
: [currentDir + selectedContext.name]);
};
const _pasteItem = (targetPath, asSymlink) => pasteItem(clipboard, targetPath.join("/") + "/", asSymlink, addAlert);
const _renameItem = () => renameItem(Dialogs, { selected: selectedContext, path, setHistory, setHistoryIndex });
const _editPermissions = () => editPermissions(Dialogs, { selected: selectedContext || rootInfo, path });
const _deleteItem = () => {
deleteItem(
Dialogs,
{
selected,
itemPath: currentDir + selectedContext.name,
setHistory,
setHistoryIndex,
path: currentDir,
setSelected
}
);
};

const addAlert = (title, variant, key) => setAlerts(prevAlerts => [...prevAlerts, { title, variant, key }]);
const removeAlert = (key) => setAlerts(prevAlerts => [...prevAlerts.filter(alert => alert.key !== key)]);

const contextMenuItems = (
<MenuList>
{
(!selectedContext
? [
// eslint-disable-next-line max-len
{ title: _("Paste"), onClick: () => _pasteItem(path, false), isDisabled: clipboard === undefined },
{
title: _("Paste as symlink"),
onClick: () => _pasteItem(path, true),
isDisabled: clipboard === undefined
},
{ type: "divider" },
{ title: _("Create directory"), onClick: _createDirectory },
{ title: _("Create link"), onClick: _createLink },
{ type: "divider" },
{ title: _("Edit permissions"), onClick: _editPermissions }
]
: selected.length > 1 && selected.includes(selectedContext)
// eslint-disable-next-line max-len
? [{ title: _("Copy"), onClick: _copyItem }, { title: _("Delete"), onClick: _deleteItem, className: "pf-m-danger" }]
: [
{ title: _("Copy"), onClick: _copyItem },
...(selectedContext.type === "dir")
? [
{
title: _("Paste into directory"),
onClick: () => _pasteItem([...path, selectedContext.name], false),
isDisabled: clipboard === undefined
}
]
: [],
{ type: "divider" },
{ title: _("Edit permissions"), onClick: _editPermissions },
{ title: _("Rename"), onClick: _renameItem },
{ type: "divider" },
{ title: _("Create link"), onClick: _createLink },
{ type: "divider" },
{ title: cockpit.format(_("Delete")), onClick: _deleteItem, className: "pf-m-danger" },
])
.map((item, i) => item.type !== "divider"
? (
<MenuItem
className={"context-menu-option " + item.className} key={item.title}
onClick={item.onClick} isDisabled={item.isDisabled}
>
<div className="context-menu-name">{item.title}</div>
</MenuItem>
)
: <Divider key={i} />)
}
</MenuList>
);

return (
<Page>
<NavigatorBreadcrumbs
Expand Down Expand Up @@ -229,15 +142,16 @@ export const Application = () => {
currentDir={currentDir}
isGrid={isGrid} sortBy={sortBy}
selected={selected} setSelected={setSelected}
selectedContext={selectedContext}
setSelectedContext={setSelectedContext} setHistory={setHistory}
setHistoryIndex={setHistoryIndex} historyIndex={historyIndex}
loadingFiles={loadingFiles}
clipboard={clipboard}
setClipboard={setClipboard}
addAlert={addAlert}
rootInfo={rootInfo}
allFiles={files}
/>
{!loadingFiles &&
<ContextMenu
parentId="folder-view" contextMenuItems={contextMenuItems}
setSelectedContext={setSelectedContext}
/>}
<AlertGroup isToast isLiveRegion>
{alerts.map(alert => (
<Alert
Expand Down
153 changes: 140 additions & 13 deletions src/navigator-card-body.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,20 @@ import {
Gallery,
Icon,
CardTitle, Spinner, CardHeader,
MenuItem, MenuList,
Divider,
} from "@patternfly/react-core";
import { FileIcon, FolderIcon } from "@patternfly/react-icons";

import cockpit from "cockpit";
import { useDialogs } from "dialogs.jsx";
import { ListingTable } from "cockpit-components-table.jsx";

import { ContextMenu } from "./navigatorContextMenu.jsx";
import {
copyItem, createDirectory, createLink, deleteItem, editPermissions, pasteItem, renameItem
} from "./fileActions.jsx";

const _ = cockpit.gettext;

const compare = (sortBy) => {
Expand Down Expand Up @@ -75,7 +82,91 @@ const compare = (sortBy) => {
}
};

// eslint-disable-next-line max-len
const ContextMenuItems = ({ path, currentDir, selected, selectedContext, setSelected, setHistory, setHistoryIndex, addAlert, rootInfo, clipboard, setClipboard, files }) => {
const Dialogs = useDialogs();

const _createDirectory = () => createDirectory(Dialogs, currentDir, selectedContext || selected);
const _createLink = () => createLink(Dialogs, currentDir, files, selectedContext);
const _copyItem = () => {
copyItem(setClipboard, selected.length > 1
? selected.map(s => currentDir + s.name)
: [currentDir + selectedContext.name]);
};
const _pasteItem = (targetPath, asSymlink) => pasteItem(clipboard, targetPath.join("/") + "/", asSymlink, addAlert);
const _renameItem = () => renameItem(Dialogs, { selected: selectedContext, path, setHistory, setHistoryIndex });
const _editPermissions = () => editPermissions(Dialogs, { selected: selectedContext || rootInfo, path });
const _deleteItem = () => {
deleteItem(
Dialogs,
{
selected,
itemPath: currentDir + selectedContext.name,
setHistory,
setHistoryIndex,
path: currentDir,
setSelected
}
);
};

return (
<MenuList>
{
(!selectedContext
? [
// eslint-disable-next-line max-len
{ title: _("Paste"), onClick: () => _pasteItem(path, false), isDisabled: clipboard === undefined },
{
title: _("Paste as symlink"),
onClick: () => _pasteItem(path, true),
isDisabled: clipboard === undefined
},
{ type: "divider" },
{ title: _("Create directory"), onClick: _createDirectory },
{ title: _("Create link"), onClick: _createLink },
{ type: "divider" },
{ title: _("Edit permissions"), onClick: _editPermissions }
]
: selected.length > 1 && selected.includes(selectedContext)
// eslint-disable-next-line max-len
? [{ title: _("Copy"), onClick: _copyItem }, { title: _("Delete"), onClick: _deleteItem, className: "pf-m-danger" }]
: [
{ title: _("Copy"), onClick: _copyItem },
...(selectedContext.type === "dir")
? [
{
title: _("Paste into directory"),
onClick: () => _pasteItem([...path, selectedContext.name], false),
isDisabled: clipboard === undefined
}
]
: [],
{ type: "divider" },
{ title: _("Edit permissions"), onClick: _editPermissions },
{ title: _("Rename"), onClick: _renameItem },
{ type: "divider" },
{ title: _("Create link"), onClick: _createLink },
{ type: "divider" },
{ title: cockpit.format(_("Delete")), onClick: _deleteItem, className: "pf-m-danger" },
])
.map((item, i) => item.type !== "divider"
? (
<MenuItem
className={"context-menu-option " + item.className} key={item.title}
onClick={item.onClick} isDisabled={item.isDisabled}
>
<div className="context-menu-name">{item.title}</div>
</MenuItem>
)
: <Divider key={i} />)
}
</MenuList>
);
};

export const NavigatorCardBody = ({
currentDir,
currentFilter,
files,
historyIndex,
Expand All @@ -87,7 +178,13 @@ export const NavigatorCardBody = ({
setSelected,
setSelectedContext,
sortBy,
loadingFiles
loadingFiles,
clipboard,
setClipboard,
addAlert,
allFiles,
selectedContext,
rootInfo,
}) => {
const [boxPerRow, setBoxPerRow] = useState(0);
const Dialogs = useDialogs();
Expand Down Expand Up @@ -280,23 +377,53 @@ export const NavigatorCardBody = ({
<Spinner />
</Flex>
);

const contextMenu = (
<ContextMenu
parentId="folder-view"
contextMenuItems={
<ContextMenuItems
path={path}
currentDir={currentDir}
selected={selected}
setSelected={setSelected}
selectedContext={selectedContext}
setHistory={setHistory}
setHistoryIndex={setHistoryIndex}
addAlert={addAlert}
clipboard={clipboard}
setClipboard={setClipboard}
files={allFiles}
rootInfo={rootInfo}
/>
}
setSelectedContext={setSelectedContext}
/>
);

if (isGrid) {
return (
<CardBody onClick={resetSelected} id="navigator-card-body">
<Gallery id="folder-view">
{sortedFiles.map(file => <Item file={file} key={file.name} />)}
</Gallery>
</CardBody>
<>
{contextMenu}
<CardBody onClick={resetSelected} id="navigator-card-body">
<Gallery id="folder-view">
{sortedFiles.map(file => <Item file={file} key={file.name} />)}
</Gallery>
</CardBody>
</>
);
} else {
return (
<ListingTable
id="folder-view"
className="pf-m-no-border-rows"
variant="compact"
columns={[_("Name")]}
rows={sortedFiles.map(file => ({ columns: [{ title: <Item file={file} key={file.name} /> }] }))}
/>
<>
{contextMenu}
<ListingTable
id="folder-view"
className="pf-m-no-border-rows"
variant="compact"
columns={[_("Name")]}
rows={sortedFiles.map(file => ({ columns: [{ title: <Item file={file} key={file.name} /> }] }))}
/>
</>
);
}
};

0 comments on commit 8167c2d

Please sign in to comment.